prototype与__proto__

2017-06-11  本文已影响0人  为所欲为233


之前对原型链的理解处于表面上的:
一个实例化的对象,当访问它的某个属性时,如果它本生没有该属性,则会去原型找该属性,再没有再到原型的原型上找,直到最后一个原型。
表面上并没有问题,但是当我以这个理论写了Person.getName(),却被告诉Person.getName不是一个函数,也就是找不到getName方法;

代码:
function Person() {
    this.name = 'woff';
}
Person.prototype.getName = function() {
    return this.name;
}
Person.getName();

当Person.getName时,Person本身没有,它在到它的原型上找到getName,但事实不是如此。

但是如果new一个对象就没问题

let p1 = new Person();
p1.getName();    //'woff'

联想到之前看到的__proto__,以及相关资料,得出结论

原型链的继承是依赖对象的__proto__属性完成的,prototype属性只是为__proto__提供指向而已。

首先说说对象的__proto__属性,当创建一个对象时,该对象会有一个__proto__属性,指向Object对象,当该访问对象的某个属性时,如果它自己没有就会到__proto__所指向的对象上面找。

那p1是如何找到getName属性的呢,要看new操作发生了什么

当let p1 = new Person(),实际是创建了一个{},再调用Person.apply({}, arguments)。即对象冒充,在{}上创建了一个name属性。

此时,相当与执行了

p1 = Object.create(Person.prototype);
Person.call(p1);

最终将赋值给p1,但是该对象并没有getName属性,所以它会到__proto__属性上去找,之前说了当创建一个{}时,它的__proto__是指向Object的,但是getName并不在Object上,所以,如果让__proto__指向Person的原型对象,那不是就能找到getName了。

Person上有个prototype属性,每个被创建的函数都会有该属性,指向这个函数的原型对象,所以prototype的作用就是保存该原型对象的引用,在new一个对象时,将该引用传给{}的__proto__属性,prototype仅此而已,不做其他的事情了。

{}.__proto__ = Person.prototype, 正是这条语句让原型链串起来了。


所以一开始的Person.getName是不存在的,因为Person要找getName,是靠__proto__实现的,而Person是个函数,不存在该属性。而p1可以,因为p1是对象,并且__proto__指向Person.prototype。值得注意的是prototype仅仅只是保存原型的引用而已。


现在用一幅图来形容一条原型链的本质

原型链继承机制

这样的逻辑就很清晰了,还有要注意的是emily是没有prototype属性的,只有构造函数才有, 但是emily有__proto__属性,构造函数是不可能存在的。

避免操作对象的__proto__属性,因为这将改变原型链,即使是新增或删除属性,因为prototype是被所有的实例化对象所共有的,修改将会对所有的对象产生影响。如果要操作prototype,应该用构造函数,如Person.prototyoe.xxx = function() { xxx }。

上一篇 下一篇

猜你喜欢

热点阅读