prototype是函数特有的属性,一个对象的构造函数也是函数,js中没有类,只有构造函数。关于原型链的继承在这里不展开。

__proto__属性已经被标记为deprecated,可以用Object.getPrototypeOf(obj)的方式代替访问该属性,应该避免修改该属性,因为它会影响到所有跟这个属性有关的对象,性能非常低下,可以用Object.create(targetproto)的方式直接用你希望的原型创建一个新的对象。

本文以下内容仍以__proto__的方式表示该属性,主要为解释原理,在生产中应该避免使用

__proto__是对象上的属性,而js中一切皆对象,访问__proto__属性等同于访问该对象的构造函数的prototype属性,对于一个普通的构造函数及通过其构造的对象,这一点不难理解,在console中测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
//Person构造函数
function Person(name){
    this.name=name;
}

var person1=new Person();   //构造一个person1对象
person1.__proto__===Person.prototype    //true,访问__proto__属性等同于访问该对象的构造函数的prototype属性
person1.__proto__===person1.constructor.prototype   //true,同上

Person.prototype.age=18 //给Person.prototype添加一个age属性
person1.age //18

person1.__proto__.country="china"   //添加一个country属性,两种方式相同,操作的是同一个原型

var person2=new Person;
person2.country //china
person2.age //18

Object和Function

相比于上面的普通对象和构造函数,ObjectFunction比较难理解,在浏览器点开console,输入Object.prototype,在输出中随便找点点击一个函数,如toString(),里面有个__proto__,再点进去,里面还有个__proto__,再点进去,里面的内容跟刚才Object.prototype的输入里一样的,如此循环下去。。。

想搞清楚这些,先明确几个概念:

  • js中一切皆对象,person1是对象,其构造函数Person也是对象,Object,Function都是对象。所有对象的原型链尽头都是Object.prototype。对象的原型链:person1 -> Person.prototype -> Object.protoytpe -> null,这其中的每一环,如Person.prototype也都是对象,所有对象不断的访问__proto__属性,都会返回自己的原型链上游,只有一个例外,Object.prototype.__proto__===null
  • 所有函数对象的构造函数都是Function,所有函数对象的原型都是Function.prototype
  • 一切构造函数是对象的同时,也是函数。ObjectFunction,既是对象又是构造函数。
  • 原型Function.prototype也是对象,其原型链上游为Object.prototype

如构造函数PersonPerson.__proto__===Function.prototype返回true,这里的Person可以理解为一个对象:一个从Function.prototype这个原型继承而来的对象。

在console中运行下面的代码来验证上述的原型之间的关系:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//构造Dog
function Dog(){}
var dog1=new Dog(){}

//dog1是普通的对象,它的__proto__指向它的构造函数的原型,
dog1.__proto__===Dog.prototype //true
Dog.__proto__===Function.prototype  //true 所有的函数对象的原型都是Function.prototype。
Dog.prototype.__proto===Object.prototype //true Dog.prototype这个对象的原型就是Object.prototype
dog1.__proto__.__proto__===Object.prototype //true 跟上面的表示等价
dog1.__proto__.__proto__.__proto__  //null Object.prototype的原型是null

//下面的Object、Number、Dog、Function都是构造函数,所有的函数对象的原型都是Function.prototype。
Object.__proto__===Function.prototype   //true
Number.__proto__===Function.prototype   //true
Dog.__proto__===Function.prototype  //true
Function.__proto__===Function.prototype //true

//原型`Function.prototype`也是对象,其原型链上游为Object.prototype。
Function.prototype.__proto__===Object.prototype //true
Object.prototype.__proto__  //null

回到上面无限循环的例子,对应每一步解释:

  1. 在console输入Object.prototype时,返回的是所有对象的原型链顶端的Object.prototype`对象,里面有一些方法
  2. 点击toString()方法,该方法本质是函数,所有的函数对象的原型都是Function.prototype,即toString().__proto__===Function.prototype,所以点开__proto__就是Function.prototype对象
  3. Function.prototype对象也是对象,它的原型就是Object.prototype,即Function.prototype.__proto__===Object.prototype,所以点开__proto__又回到了1,无限循环。