记《你不知道的JavaScript》原型

原型
  1. JavaScript中的对象有一个特殊的[[Prototype]]内置属性,其实就是对于其他对象的引用。几乎所有对象在创建时[[Prototype]]属性都会被赋予一个非空的值(对象的[[Prototype]]属性可以为空,虽然很少见)
  2. 使用for..in遍历对象时原理和查找[[Prototype]]链类似,任何可通过原型链访问到(并且是enumerable)的属性都会被枚举。
  3. 使用in操作符来检查属性在对象中是否存在时,同样会查找对象的整条原型链(无论属性是否可枚举)。
    Object.prototype
  4. 所有普通的[[Prototype]]链最终会指向内置的Object.prototype。由于所有的“普通”(内置,不是特定主机的拓展)对象都“源于”(或者说[[Prototype]]链的顶端设置为)这个Object.prototype对象,所以他包含JavaScript中许多通用的功能。
  5. 下面分析一下obj.foo=bar的行为(给一个对象设置属性不仅仅是添加一个新属性或者修改已有的属性值):
obj.foo="bar"

如果obj对象中包含foo的普通数据访问属性,这条赋值语句只会修改已有的属性值。
如果foo不是直接存在于obj中,[[Prototype]]链就会被遍历,类似[[Get]]操作。如果原型链上找不到foofoo就会被直接添加到obj上。
如果foo存在于obj中并且存在于[[Prototype]]链上层,就会发生属性屏蔽。
如果foo仅存在于[[Prototype]]链上层时,会出现以下三种情况:
1. 如果在[[Prototype]]链上层存在名为foo的普通数据属性并且没有被标记为只读,那就会直接在obj中添加一个名为foo的新属性,他是屏蔽属性。
2. 如果在[[Prototype]]链上层存在foo,但是它被标记为只读,那么无法修改已有属性或者在obj上创建屏蔽属性。如果在严格模式下,代码会抛出一个错误。否则,这条语句会被忽略。总之,不会发生屏蔽属性。
3. 如果在[[Prototype]]链上层存在foo并且它是一个setter,那就一定会调用这个setterfoo不会被添加到(或者说屏蔽于)obj,也不会重新定义foo这个setter
如果希望在23中也屏蔽foo。那就不要使用=操作符赋值,而是使用Object.defineProperty(...)来向obj中添加foo

let obj={
	a:2
};
let anotherObj=Object.create(obj);

obj.a;//2
anotherObj.a;//2

obj.hasOwnProperty('a');//true
anotherObj.hasOwnProperty('a');//false

anotherObj.a++;

obj.a;//2
anotherObj.a;//3

obj.hasOwnProperty('a');//true
anotherObj.hasOwnProperty('a');//true
(原型)继承
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=Object.create(Foo.prototype)

Bar.prototype.mylabel=function(){
	return this.label
}

const a=new Bar("a",'obja')

a.myName();
a.mylabel();

ES6之后新增辅助函数来修改关联,对比两种方法将Bar.prototypeFoo.prototype关联

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

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

如果忽略Object.create(...)方法带来的轻微性能损失(抛弃的对象需要进行垃圾回收),它实际上比ES6及其之后的方法更短而且可读性更高。

检查“类”关系

假设有对象a,如何寻找a委托的对象?
检查一个实例(JavaScript中的对象)的继承祖先(JavaScript中的委托关联)通常被称为内省(或者反射)

	function Foo(){
		//...
	}
	Foo.prototype.blah=...;
	const a=new Foo();
  1. 站在类的角度来判断
	a instanceof Foo//true

instanceof操作符的左操作数是一个普通对象,右操作数是一个函数。
instanceof回答的问题是:在a[[prototype]]链中是否有Foo.prototype指向的对象。
这个方法只能处理对象(a)和函数(带.prototype引用的Foo)之间的关系。如果想判断两个对象(比如a和b)之间是否通过[[prototype]]链关联,只用instanceof无法实现。

注意:如果使用内置的.bind(...)函数来生成一个硬绑定函数的话,该函数是没有.prototype属性的。在这样的函数上使用instanceof的话,目标函数的.prototype会代替应绑定函数的.prototype;
通常不会“构造函数调用”中使用硬绑定函数,不过如果这么做的话,实际上相当于直接调用目标函数。同理,在硬绑定函数上使用instanceof也相当于直接在目标函数上使用instanceof
2. 下面第二种判断[[prototype]]反射的方法,它更加简洁

	Foo.prototype.isPrototypeOf(a); //true

注意,在本例中,我们实际上并不关心(甚至不需要Foo),我们只需要一个可以用来判断的对象(本例中是Foo.prototype)就行。isPrototype(...)回答的问题是:在a的整条[[prototype]]链中是否出现过Foo.prototype
同样的问题,同样的答案。但是在第二种方法中并不需要间接引用函数(Foo),它的.prototype属性会被自动访问。
我们只需要两个对象就可以判断他们之间的关系。举例来说:

	b.isPrototypeOf(c)

注意,这个方法并不需要使用函数(‘类’),他直接使用bc之间的对象引用来判断他们的关系。
我们也可以获取一个对象的[[prototype]]链,在ES5中,标准的方法是:

	Object.getPrototypeOf(a);

可以验证一下,这个对象引用是否和我们想的一样:

	Object.getPrototypeOf(a) === Foo.prototype; //true

绝大多数(不是所有)浏览器也支持一种非标准的方法来访问内部属性[[prototype]]属性:

	a.__proto__ === Foo.prototype; //true
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值