[[Prototype]]JavaScript中的对象(函数也是对象)有一个非凡的[[Prototype]]内置属性,所谓的原型链就是由它“链”起来的。
属性查找当援用对象的属性时会触发[[Get]]操作,能够了解为会执行[[Get]](),其逻辑是先查找以后对象是否存在该属性,如果存在就应用它。否则就去递归遍历,查找[[Prototype]]属性所援用的对象中是否存在要查找的属性,如果找到则返回,否则直到[[Prototype]]=null时查找完结,此时返回undefined。
在应用for in遍历对象时原理和查找[[Prototype]]链相似,任何能够通过原型链拜访到并且是enumerable的属性都会被枚举。应用in操作符来查看属性在对象中是否存在时,会查找对象的整条原型链。
属性设置和屏蔽上面以myObject.foo = 'bar'为例来阐明所有可能呈现的状况:
如果myObject本身存在foo属性,不论其[[Prototype]]链下层是否存在,都会产生屏蔽景象,会从新赋值。如果myObject本身不存在,[[Prototype]]链上也不存在,则foo属性会被增加到myObject上。如果myObject本身不存在,[[Prototype]]链上存在,会呈现如下三种状况:
如果[[Prototype]]链上的foo为一般数据拜访属性,并且没有被标记为只读,那就会在myObject上增加foo属性,它是屏蔽属性。如果[[Prototype]]链上的foo被标记为只读,如果运行在严格模式下,会抛出一个谬误。否则,这条赋值语句被疏忽,并不会产生属性屏蔽。如果[[Prototype]]链上的foo是一个setter,那么肯定会调用这个setter。foo不会增加到myObject,也不会从新定义foo这个setter。如果心愿在第二种和第三种下也屏蔽foo,那就不能应用=操作符来赋值,而是应用Object.defineProperty()来向myObject增加foo。
上面来个例子领会一下,同时为原型继承做一个铺垫
var person = {name: 'a'};var fn = { getName: function() { return this.name; }};先想一下person为什么能够调用到Object.prototype中定义的办法?Object被定义为函数,上面会提到,只有是函数都会存在prototype属性,它指向一个对象,被称为原型对象,toString、hasOwnProperty等办法就定义在该原型对象上。var person = {name: 'a'};执行时会创立一个新的对象,并且底层会先将person的[[Prototype]]属性值设置为Object.prototype,相当于执行Object.setPrototypeOf({name: a}, Object.prototype)。这样在整个[[Prototype]]链上就能够找到这些办法了。
再想一下咱们怎么能使person对象能够调用fn中的getName办法呢?应用Object.assign(person, fn)将fn的getName间接增加到person对象中。应用Object.setPrototypeOf(person, fn),会将person中的[[Prototype]]属性由默认的Object改为指向fn对象,而fn中的[[Prototype]]会指向Object.prototype。在执行person.getName()时会进行属性查找,依据下面提到的规定,在其[[Prototype]]链上能够找到getName办法,其中的this利用的隐式绑定。除此之外,还有另外一种实现形式,其原理和第二条一样:
var fn = { getName: function() { return this.name; }};// 创立一个新对象person,使其[[Prototype]]指向fnvar person = Object.create(fn);person.name = 'a';person.getName(); // 'a'函数中的prototypeJavaScript中的函数有一种非凡个性:所有函数默认都会领有一个名为prototype的私有并且不可枚举的属性,他会指向一个对象:这个对象通常被称为该函数的原型。该函数同时存在内置属性[[Prototype]],留神这两者的区别!
同时该prototype对象存在一个叫constructor的属性,会持有该函数的援用。
这些个性与上面要说的“构造函数”没有任何关系,也就是说只有是函数就有这些个性。
构造函数JavaScript中把首字母大写的办法称为构造函数,这只是一种约定,同时这也意味着要应用new关键字来调用。
应用new调用函数会执行上面的步骤:
创立一个全新的对象。这个新对象会被执行[[Prototype]]连贯。这个新对象会被绑定到函数调用的this。如果函数没有返回其它对象。那么new表达式中的函数调用会主动返回这个对象。上面是伪代码
function customNew(fn) { var o = {}; var rs = fn.apply(o, [].slice.call(arguments, 1)); Object.setPrototypeOf(o, fn.prototype); return typeof rs === 'undefined' ? o : rs;}function Person(name) { this.name = name;}Person.prototype.getName = function() { return this.name;};var p = customNew(Person, 'JS');p.getName(); // 'JS'console.log(p.constructor === Person); // truePerson的实例能够拜访到getName和constructor都是基于“属性拜访”的原理。
...