__proto__、Prototype、constructor
prototype
函数的对象的原型,给其它对象提供共享属性,函数所独有的。
它的作用是:new对象的时候,告诉新创建的实例他的构造函数是谁,有哪些共享属性和方法
如下所示,t 中并没有 M,但是有 x 和 y,说明 M 没有共享,但是 x 和 y共享了
__proto__
__proto__,隐式原型对象,它是所有对象(包括函数)都有的。
任何一个对象都有 __proto__,它作为一个对象的隐式原型(__proto__)指向构造该对象的构造函数的原型(prototype),这也保证了实例能够访问在构造函数原型中定义的属性和方法。
其实就是 A函数的实例的__proto__ => A函数的prototype,此时就可以访问到 prototype 上存储的共享属性了
这时候有同学就要问了,那我直接通过字面量创建的对象 { } 呢?
其实就是相当于 new Object(),不信你试试 {}.__proto__ === Object.prototype
原型链
当你在访问一个对象属性的时候,如果该对象内部不存在这个属性,那么就回去它的 __proto__ 上查找,如果依旧不存在这个属性,那么就继续沿着 __proto__ 的 __proto__ 查找。以此类推,直到找到 null。而这个查找的过程,也就构成了我们常说的原型链。
这里引出一个点,既然刚才说到,A函数的实例的__proto__ => A函数的prototype,这里又说到 __proto__ 可以一直往上找,直到null为止
那么替换一下, A函数的prototype的__proto__是什么呢?(有点绕,其实就是一个替换)
没错,就是其父类的函数(这里我觉的直接说父类好理解)的 prototype
仔细捋顺一下其实结构就清晰了
constructor
constructor属性是一个对象指向该对象的构造函数。对象所独有属性。每一个对象都有其对应的构造函数,本身或者继承而来。
通常obj.constructor === obj.__proto__.constructor
对于构造函数,可以理解为通过 function X() { } 实例化后得到的 x 对象,那么 x 的构造函数就是 X
原型继承
__proto__ 与 prototype 与 constructor
function Father(){ this.a =1; } // Father是函数 let child = new Father() // child是实例对象
// child 的 隐式原型对象(__proto__) 指向 构造函数(constructor) Father 的原型(prototype)
- child._ proto_ === Father.prototype
// child 的 隐式原型对象 的 隐式原型对象 指向 Object 的原型
- Father.prototype._ proto_ === Object.prototype
// Object 再往上就指向 null 了【原型链顶层】
- Object.prototype._ proto_ === null
如上所示,和前面分析的一样
其实 child.constructor 是在 __proto__ 原型中找到的 constructor
child.constructor === Father // true
child.__proto__.constructor === child.constructor // true
所以其实是这么一个结构:
子类实例对象:(
属性方法
__proto__ ==> 父类(
父类属性方法
constructor: function 子类(){}
__proto__ ==> 父类的父类(
父类的父类属性方法
constructor: function 父类(){}
__proto__。。。一直套娃,最后套到Object,然后是null
)
)
)
下面看一段原型代码
function Foo(name) { this.name = name; } Foo.prototype.myName = function(){ return this.name; } function Bar(name, label){ Foo.call(this,name); this.label = label; } // 创建新的Bar.prototype对象并关联到Foo.prototype Bar.prototype = Object.create(Foo.prototype); // Notice!现在没有Foo.prototype.constructor了,因为prototype被整个替换成了{}
Bar.prototype.myLabel = function(){ return this.label; } var a = new Bar('a', 'obj a'); a.myName(); //'a' a.myLabel(); //'obj a'
解决办法
可以使用ES6提供的新方法,这样就不会破坏结构了
// obj:要设置其原型的对象。 // prototype:该对象的新原型(一个对象或 null)。 // return:指定的对象。 Object.setPrototypeOf(obj, prototype)
上述方法中,两个参数均需要传入对象
通常使用如下:
当 Bar 和 Foo 都是函数时,因为我们实际需要共享的是 原型prototype 中的属性和方法,所以通常要传入其 原型 prototype 实现 Bar 对 Foo的继承
Object.setPrototypeOf(Bar.prototype, Foo.prototype)