JS-原型细节

67 阅读3分钟

文章参考:你不知道的javaScript

属性屏蔽

如果myObject 对象中包含名为foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。

如果foo不是直接存在于myObject中,[[Prototype]]链就会被遍历,类似[[Get]]操作。

如果原型链上找不到foo,foo就会被直接添加到myObject上。

如果属性名foo既出现在myObject中也出现在myObject的[[Prototype]]链上层,那么就会发生屏蔽。myObject中包含的foo属性会屏蔽原型链上层的所有foo属性,因为 myObject.foo 总是会选择原型链中最底层的foo属性。

function People(){
    this.name = "张三";
}

People.prototype.say = function(){
    console.log("我是张三");
}


var p = new People();

p.say = function(){
    console.log("我是李四");
}
p.say(); //李四

People.prototype.say(); // 张三

当原型链中存在这个属性时情况,考虑到原型对象的完整性,是无法间接修改原型对象的属性的。 1.原型链上存在这个属性且writtable属性值为true的情况:

function People(){
    this.name = "张三";
}

People.prototype.say = function(){
    console.log("我是张三");
}


var p = new People();

p.say = function(){ // 添加属性并未影响原型的值
    console.log("我是李四");
}
p.say(); //李四

People.prototype.say(); // 张三

2.原型链上存在这个属性且writtable属性值为false的情况,可以看成继承了一个无法修改的属性:

function People(){
    this.name = "张三";
}

People.prototype.say = function(){
    console.log("我是张三");
}

Object.defineProperty(People.prototype,"say",{
    writable:false
});

var p = new People();

p.say = function(){ // 静默修改失败,严格报错
    console.log("我是李四");
}
p.say(); // 张三 

People.prototype.say();//张三

3.如果在[[Prototype]] 链上层存在foo并且它是一个setter,那就一定会调用这个setter。foo不会被添加到(或者说屏蔽于)myObject,也不会重新定义foo这个setter。

如果你希望在第二种和第三种情况下也屏蔽foo,那就不能使用=操作符来赋值,而是使 用Object.defineProperty(..)(参见第 3 章)来向myObject 添加foo

继承

js中的继承,实际上是委托更合适。在真正面向对象的语言中,例如java,子类继承父类的属性,子类是复制了父类的一份拷贝,就像真正父子的关系一样,继承了爸爸的大眼睛,且两个是独立的个体。但是js是通过引用来达到委托效果。

function Bar(){}
function Foo(){}
// 和你想要的机制不一样!例如设置Bar.prototype时,就是设置Foo.prototype。 
Bar.prototype = Foo.prototype; 

// 基本上满足你的需求,但是可能会产生一些副作用 :会执行Foo的方法,
Bar.prototype = new Foo()

因此,要创建一个合适的关联对象,我们必须使用Object.create(..)而不是使用具有副作用的Foo(..)。这样做唯一的缺点就是需要创建一个新对象然后把Bar旧原型对象抛弃掉,不能直接修改已有的默认对象。

在ES6之前, 我们只能通过设置.__proto__属性来实现,但是这个方法并不是标准并且无法兼容所有浏 览器。ES6添加了辅助函数Object.setPrototypeOf(..),可以用标准并且可靠的方法来修改关联

// ES6 之前需要抛弃默认的Bar.prototype 
Bar.ptototype = Object.create( Foo.prototype ); 

// ES6 开始可以直接修改现有的Bar.prototype 
Object.setPrototypeOf( Bar.prototype, Foo.prototype )

检查对象和类的关系

a instanceof Foo; // true

instanceof的意思是:在左边的对象的原型链上是否存在Foo.prototype这个对象。

如果使用内置的.bind(..)函数来生成一个硬绑定函数的话,该函数是没有.prototype属性的。在这样的函数上使用instanceof的话, 目标函数的.prototype会代替硬绑定函数的.prototype。

function People(){}
let People1 = People.bind();
console.log('prototype' in People1); //false
console.log('prototype' in People); // true

通常我们不会在“构造函数调用”中使用硬绑定函数,不过如果你这么 做的话,实际上相当于直接调用目标函数。同理,在硬绑定函数上使用 instanceof 也相当于直接在目标函数上使用instanceof。

function People(){}
People.prototype.say = function(){
    console.log("我是张三");
}

let People1 = People.bind();
let p1 = new People1();
let p = new People();
p1.say(); // 我是张三
console.log(p instanceof People1); // true