深入理解JS原型(你不知道的js)
[[Prototype]]
在js中,几乎所有对象在创建时,其[[Prototype]]属性都会被赋予一个非空的值,其实就是对其他对象的引用,可以用Object.getPrototypeOf(obj)或者obj.__proto__访问。(也有空值的情况),当试图引用对象的属性时,就会触发[[Get]]操作,[[Get]]操作默认第一步是检测对象本身是否有这个属性,如果不在就进行第二步,访问对象的[[Prototype]]链,🌰
原型关联上图可以看出对象myObject是没有a属性的,但是由于myObject的[[Prototype]]关联到了对象anotherObject,所以属性访问成功的找到了值1,即myObject.__proto__ == anotherObject,如果原型链中也找不到这个属性,则返回undefined
使用for..in遍历对象时原理和查找[[Prototype]]链类似,任何可以通过原型链访问到,并且是可枚举的属性都会被枚举。使用in操作符检查属性是否存在于对象中时,都会查找整条原型链,无论属性是否可以枚举。
小结:通过各种语法进行属性查找时都会查找[[Prototype]]链,直到找到属性或者查找完整条原型链,原型链的终点,都会指向内置的Object.prototype,所有内置对象都源于这个Object.prototype对象
属性设置和屏蔽
给对象设置一个属性,并不仅仅是添加一个新属性和修改已有的属性,分4种情况,🌰
修改对象属性以上面的🌰来说
1.若myObject对象中包含要修改的foo普通数据访问的属性,则这条赋值语句只会修改已有的属性值
2.若foo不直接存在于myObject对象中,[[Prototyoe]]链就会遍历,若原型链上找不到,foo属性就会被直接添加到myObject上
3.如果对象本身和原型链上都存在此属性,则myObject中包含的属性会屏蔽原型链上层所有的foo属性,原型链查找规则,只选择原型链最底层的属性
4.若此属性存在于原型链上层时,又分为3中情况
1.若原型链上层的此属性是普通数据访问属性,并且此属性的writeable: true,那就会直接在myObject上添加一个名为foo的属性,它是屏蔽属性
2.若原型链上层的此属性的writeable: false,那则无法修改已有属性或创建屏蔽属性,严格模式下会抛错
3.若原型链上层的此属性是一个setter,那就调用这个setter,不会创建屏蔽属性,也不会重新定义foo这个setter
注意,一些操作会隐式的产生屏蔽,例如 ++
“类”
在js中根本不存在类,一切皆对象。
所有的函数默认有一个名为prototype的公有的且不可枚举的属性,会指向一个对象,我们习惯叫做该函数的原型(注意是函数),通过函数名.prototype属性引用来访问,所有应该更准确的说所谓的原型是一个被贴上函数名.prototype标签的对象。🌰
prototype属性是引用的一个对象这个对象是什么?可以这么理解,通过调用 new Foo()创建的每个对象,最终被[[Prototype]]链接到“Foo.prototype“引用的对象
“构造函数”
上面例子的Foo.prototype还有一个constructor属性,翻译过来是构造的意思,但这是一个误解,实际上是这个属性引用的是对象的关联函数,这里是Foo。
比如说,var a = new Foo(),a.constructor == Foo,看上去a好像是a有一个constructor属性指向Foo,实际上只是.constructor属性也是被委托给Foo.prototype对象,而Foo.prototype.constructor默认是指向Foo的,但这个属性不是不可修改的,我们可以手动修改,
Foo.prototype对象的修改实际上函数本身只是普通的函数,只是加了new关键字,new会劫持所有普通函数并用构造对象的形式来调用它。直白一点就是,仅当使用new时,函数会变成“构造函数调用”。
现在我们知道[[prototype]]机制就是存在于对象中的一个内部链接,它会引用其他对象,如果在对象上没有找到想要的属性或方法,引擎就会继续在[[prototype]]关联的对象上查找,以此类推,这一系列对象的链接被称为“原型链”
关于原型就介绍这么多,以后有新的感悟还会继续更新,希望大家给与指正和交流...