先看看最简单的栗子:
//构造函数 function People(name, age){ this.name = name; this.age = age; } //原型对象(所有由构造函数实例而来的对象都继承自这个对象) People.prototype = { constructor:People, //显式设置构造函数的反向引用,避免实例的constructor属性指向Object函数 introduce:function(){ console.log(this.name + '今年' + this.age + '岁'); }, speak:function(x){ console.log(this.name + "会讲" + x + "语言"); } } let p1= new Person('leaf',12); let p2 = new Person('kafu',13); console.log(p1.__proto__===People.prototype); //true console.log(People.prototype.constructor===People); //true console.log(Object.getPrototypeOf(p1));
先说明几个概念再上图
1、prototype
每个函数都有一个prototype属性,这个属性值是一个对象。而构造函数的
prototype属性被用作新对象的原型, 也就是实例的原型。
在ECMAScript5中,该属性是不能被直接读取或修改的,但是可以通过Object.getPrototypeOf()间接的读取到它.
作用:实现类的扩充、类之间的继承
2、__proto__
js对象(除了null)都"具有"的这个属性,这个属性会指向该对象的原型。
”具有”,它并不存在与Person.prototype中,实际上,它是来自于Object.prototype。
即:p1.__proto__ === People.prototype
作用:创建一个以指定对象为原型的对象,以及,为内置类型添加子类型。
3、constructor
前面讲到,每个函数都有一个prototype属性,这个属性值是一个对象,这个对象包含唯一一个不可枚举的
属性constructor,constructor属性的值是一个函数对象, 它始终指向创建当前对象的构造函数。即:People.prototype.constructor===People,其实实例.constructor也指向了构造函数;这里有一个坑!看下面
4、prototype和constructor之坑
当我们给原型对象添加新方法时候,如果我们是改写People.prototype,比如
People.prototype={ getName:function(){ return this name; } }
此时我们再去打印console.log(People.prototype.constructor===People) //false
why?
因为当重写People.prototype对象的时候,相当于
Person.prototype = new Object({ getName: function() { return this.name; } });
那此时People.prototype对象的构造函数是不是就是 Object了?而constructor始终指向创建当前对象的构造函数,所以此时:People.prototype.constructor===Object了。
修正方法:
可以重新覆盖People.prototype.constructor,即People.prototype.constructor===People;
或者不该重写People.prototype,添加属性,直接在后面加就可以了。
People.prototype.method1=...
People.prototype.method2=...
5、实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
6、原型链
如图中红色线形成的链状结构,一直指到null。
7、图解