原型
JavaScript中的对象有个特殊的内置属性叫:Prototype,其实就是对于其他对象的引用,几乎所有对象都会被赋予一个非空的prototype的属性值,当你试图引用对象的属性时会触发get操作,这个操作第一步就是检查对象自身是否有这个属性,有的话就使用它,没有的话,就会继续访问对象的原型链,直到找到,或者没找到返回undefined。最顶端的原型是Object.prototype。使用for..in来遍历对象的属性时,原理就和查找原型链是类似的
1.属性设置和屏蔽
myObject.foo = 'bar';
如果myObject对象中包含名为foo的普通数据访问属性,那么该语句只会修改已有的属性值
如果myObject对象中和原型链中都包含foo属性,则原型链上的属性会被屏蔽。
如果myObject对象不包含,则它的原型链就会被遍历,如果找不到,则foo就会被直接添加到myObject上,但是如果已经存在了foo属性,则会发生如下三种情况:
1)当原型链上存在名为foo的普通数据访问属性,没有被标记为只读,则直接会在myObject对象上添加一个屏蔽属性foo
2)当原型链上存在名为foo的普通数据访问属性,被标记为只读,则无法创建屏蔽属性,严格模式下报错。
3)当原型链上存在foo并且它是一个setter,那就会调用这个setter,foo不会被添加到myObject。
但是有一种更为保险一点的做法就是使用Object.defineProperty()来向对象添加foo。
2.原型继承
在JavaScript中,我们并不会将一个对象(类)复制到另一个对象(实例),只是将它们关联起来而已,摘用原图:
看下下面的代码:
function Foo(){
}
Foo.prototype.constructor ===Foo;//true
var a = new Foo();
a.constructor === Foo;//true
本身a是没有constructor属性的,是查找原型链而被委托的。
但是
Foo.prototype = {......};
var a1 = new Foo();
a1.constructor ===Foo;//false;
a1.constructor ===Object;//true;
这再次说明,Foo并不是一个构造函数,它也只是一个普通的函数而已。JavaScript中没有类的概念。
a1并没有constructor属性,它委托原型链上的Foo.prototype,但是它被重新赋值后,便没有个这个属性(默认的对象是有这个属性的),所以它会继续委托至顶端的原型链Object.prototype,这个对象有.constructor属性,指向内置的Object()函数。
我们已经把在Foo.prototype上把constructor弄丢了,得补回来:
Object.defineProperty(Foo.prototype,'constructor',{
enumerable:false,
writable:true,
configurable:true,
value:Foo
})
看下这张摘下来的原图:
function Foo(name){
this.name = name;
}
Foo.prototype.you = function(){
console.log(this.name);
}
function Bar(name,label){
Foo.call(this,name);
this.label = label;
}
Bar.prototype = Object.create(Foo.prototype);
//此时没有Bar.prototype.constructor了,需要的话补回来
//Bar.prototype.constructor = Bar;
Bar.prototype.my = function(){
console.log(this.label);
}
var a = new Bar('a','b');
a.you();//a
a.my();//b
Bar.prototype = Object.create(Foo.prototype);的意思是:
创建一个新的Bar.prototype对象并把它关联到Foo.prototype,还有两种这样的方法,但是是不可取的:
1)Bar.prototype = Foo.prototype;
只是直接引用Foo.prototype对象,但你做一些修改的时候,会影响到原来的。
2)Bar.prototype = new Foo();
这样确实创建了一个关联到Foo.prototype的新对象,但是它使用了Foo()这样的构造形式,这样当Foo()有一些副作用的时候,会影响到Bar()的下一代。
//ES6之前我们可以这样
Bar.prototype = Object.create(Foo.prototype)
(通过设置._proto_属性也可以,只是有兼容性问题)
//ES6开始
Object.setPrototypeOf(Bar.prototype,Foo.prototype);
3.如何检查是来自于哪个委托
假设有一个对象a,如何寻找对象a委托的对象呢
1)function Foo(){}
var a =new Foo();
a instanceof Foo;//true
它的意思是:在a的整条原型链中是否有指向Foo.prototype的对象,但是不能判断两个对象之间的关系
2)b.isPrototypeOf(c);
b是否出现在c的原型链中