你不知道的javascript(六)

时间:2022-03-17 14:43:08

原型

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

})


看下这张摘下来的原图:

你不知道的javascript(六)

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的原型链中