原型链
原型对象
任何函数都可以成为构造函数,但并不能将任何函数都叫做构造函数,函数只有通过new关键字被实例化时才能成为构造函数。如下:
var Test = function(){}
Test.prototype.aa = '11'; // 为原型对象新的属性
var test1 = new Test();
console.log(test1.aa); // "11"
var test2 = new Test();
test2.name = '22' // 原型对象上的属性只能访问,不能修改;因此此处是为p1创建新的属性name,并赋值;
console.log(test1.aa); // "22"
js中每个函数都有一个prototype属性(原型对象),该属性是函数独有的;在prototype对象中的属性或方法,会被该函数实例化的所有对象共享。
js中每个对象都有两个属性constructor和__proto__,constructor指向实例化该对象的构造方法,__proto__指向实例化该对象的构造方法中的prototype属性(原型对象);
此例中,test1和test2这两个对象都是通过构造方法Test实例化的,因此它们的构造方法都是Test,它们的__proto__属性也都指向Test.prototype原型对象;
根据js原型链继承规则,当test1或test2在访问属性或方法时,会先从自身查找是否对应的属性或方法;若没有,就会向上查找__proto__属性所指向的原型对象中是否有属性或方法;若还是没有,则会继续向上查找原型对象的__proto__属性所指向的原型对象是否有属性或方法,以此类推,直到最顶层的Object对象;Object的__proto__为null,即Object没有原型对象。
注:实例对象可以共享访问原型对象的属性,但不能修改;修改时会为实例对象创建一个新的属性。
Function对象
在js中万物皆对象,包括js中的函数实际也都是一个Function对象,所有函数(包括构造函数)都是由Function对象的构造函数Function()实例化的;运行如下代码便可验证这个结论。
(function(){}).constructor === Function // true
// 或
Test instanceof Function // true
即,函数对象Function与Array、Map一样,都是一种对象类型;Function的原型对象最终也是会追溯到Object。
详见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function
Object对象
在JavaScript中,几乎所有对象都是Object
类型的实例,所有对象都从Object.prototype
继承方法和属性;
test instanceof Object // true
Function、Array、Map等这些对象不仅可以使用自身的属性、方法,还都能使用Object原型对象中的属性和方法。
详见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
注意事项
除非有特殊需求,否则不建议去扩展原生对象和原型;原因有如下几点:
1>. 原型链属性污染:原型属性是所有对象共享的,所以一处修改会影响其它所有对象,例如:obj.__proto__['sex'] = '1'
;
2>. 使用混乱:原型方法很容易与原生方法混在一起无法区分;在实际开发应尽量减少全局函数或变量,以避免冲突;
3>. 如果自定义的原型函数错误的覆盖了原生函数,将对所有使用到的地方造成致命伤害;尤其是我们并不知道JS未来会扩展哪些原生函数,因此也不清楚未来会不会与自定义的函数重复;
原型对象MDN文档:https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes