本文为了解决以下问题:
__proto__
(实际原型)和prototype(原型属性)不一样!!!
constructor
属性(原型对象中包含这个属性,实例当中也同样会继承这个属性)
prototype
属性(constructor.prototype
原型对象)
__proto__
属性(实例指向原型对象的指针)
首先弄清楚几个概念:
- 什么是对象
若干属性的集合
- 什么是原型?
原型是一个对象,其他对象可以通过它实现继承。
- 哪些对象有原型?
所有的对象在默认情况下都有一个原型,因为原型本身也是对象,所以每个原型自身又有一个原型(只有一种例外,默认的对象原型在原型链的顶端)
任何一个对象都可以成为原型
接下来就是最核心的内容:
constructor 属性
constructor属性始终指向创建当前对象的构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var arr=[1,2,3]; console.log(arr.constructor); var a={}; console.log(arr.constructor); var bool=false; console.log(bool.constructor); var name="hello"; console.log(name.constructor); var sayName=function(){} console.log(sayName.constrctor) function A(){} var a=new A(); console.log(a.constructor);
|
以上部分即解释了任何一个对象都有constructor
属性,指向创建这个对象的构造函数
prototype属性
注意:prototype
是每个函数对象都具有的属性,被称为原型对象,而__proto__
属性才是每个对象才有的属性。一旦原型对象被赋予属性和方法,那么由相应的构造函数创建的实例会继承prototype
上的属性和方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function A(){} var a=new A(); A.prototype.name="xl"; A.prototype.sayName=function(){ console.log(this.name); } console.log(a.name); a.sayName();
|
constructor属性和prototype属性
每个函数都有prototype
属性,而这个prototype
的constructor
属性会指向这个函数。
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name){ this.name=name; } Person.prototype.sayName=function(){ console.log(this.name); } var person=new Person("xl"); console.log(person.constructor); console.log(Person.prototype.constructor); console.log(Person.constructor);
|
如果我们重写(重新定义)这个Person.prototype
属性,那么constructor
属性的指向就会发生改变了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| Person.prototype={ sayName:function(){ console.log(this.name); } } console.log(person.constructor==Person); console.log(Person.constructor==Person); console.log(Person.prototype.constructor); Person.prototype={ sayName:function(){ console.log(this.name); } } Person.prototype=new Object(){ sayName:function(){ console.log(this.name); } } Person.prototype.constructor=Person;
|
接下来解释为什么,看下面的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function Person(name){ this.name = name; } var personOne=new Person("xl"); Person.prototype = { sayName: function(){ console.log(this.name); } }; var personTwo = new Person('XL'); console.log(personOne.constructor == Person); console.log(personTwo.constructor == Person);
|
接下解释__proto__
和prototype
属性
同样拿上面的代码来解释:
1 2 3 4 5 6 7 8 9 10 11 12
| function Person(name){ this.name=name; } Person.prototype.sayName=function(){ console.log(this.name); } var person=new Person("xl"); person.sayName();
|
首先给构造函数的原型对象Person.prototype
赋给sayName
方法,由构造函数Person
创建的实例person
会继承原型对象上的sayName
方法。
为什么会继承原型对象的方法?
因为ECMAscript的发明者为了简化这门语言,同时又保持继承性,采用了链式继承的方法。
由constructor
创建的每个instance
都有个__proto__
属性,它指向constructor.prototype
。那么constrcutor.prototype
上定义的属性和方法都会被instance
所继承.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function Person(name){ this.name=name; } Person.prototype.sayName=function(){ console.log(this.name); } var personOne=new Person("a"); var personTwo=new Person("b"); personOne.sayName(); personTwo.sayName(); console.log(personOne.__proto__==Person.prototype); console.log(personTwo.__proto__==Person.prototype); console.log(personOne.constructor==Person); console.log(personTwo.constructor==Person); console.log(Person.prototype.constructor==Person); console.log(Person.constructor); console.log(Person.__proto__.__proto__);
|
参考文章:
从proto和prototype来深入理解JS对象和原型链
JavaScript原型继承工作原理
javascript秘密花园
深入理解JavaScript系列(5):强大的原型和原型链
constructor, prototype, __proto__
详解