JavaScript 中的对象有一个特殊的 [[Prototype]] 内置属性,其实就是对于其余对象的引用。几乎全部的对象在建立时 [[Prototype]] 属性都会被赋予一个非空的值。javascript
思考一下以下代码:java
var myObject = { a:2 }; myObject.a; // 2
当你试图引用对象数据访问属性时会触发对象默认的内置[[Get]]操做,按照[[Get]]操做的算法首先从myObject对象内部查找是否有相同名称的属性,若是找到就会放回这个属性的值。算法
若是[[Get]]操做对象内部没法找到须要的属性,那么继续访问对象的[[prototype]]链:函数
let anotherObject = { a:2 }; // 建立一个关联到 anotherObject 的对象 let myObject = Object.create( anotherObject ); myObject.a; // 2
如今 myObject 对象的 [[Prototype]] 关联到了 anotherObject。显然 myObject.a 并不存在,可是尽管如此,属性访问仍然成功地(在 anotherObject 中)找到了值 2。可是若是 anotherObject 中也找不到 a 而且 [[Prototype]] 链不为空的话,就会继续查找下去。这个过程会持续到找到匹配的属性名或者查找完整条 [[Prototype]] 链。若是是后者的话,[[Get]] 操做的返回值是 undefined。
使用 for..in 遍历对象时原理和查找 [[Prototype]] 链相似,任何能够经过原型链访问到的属性都会被枚举。使用 in 操做符来检查属性在对象中是否存在时,一样会查找对象的整条原型链(不管属性是否可枚举):spa
let anotherObject = { a:2 }; // 建立一个关联到 anotherObject 的对象 let myObject = Object.create( anotherObject ); for (let k in myObject) { console.log("found: " + k); } // found: a ("a" in myObject); // true
所以,当你经过各类语法进行属性查找时都会查找 [[Prototype]]链,直到找到属性或者查找完整条原型链。prototype
全部普通对象的[[prototype]]链指向内置的Object.Prototype。因为全部普通对象的[[prototype]]链源于Object.Prototype,因此它含有JavaScript中许多通用的功能。code
给一个对象设置属性并不只仅是添加一个属性或者修改属性值,它包含如下过程:对象
myObject.foo = 'bar';
若是 myObject 对象中包含名为 foo 的普通数据访问属性,这条赋值语句只会修改已有的属性值
若是 foo 不是直接存在于 myObject 中,[[Prototype]] 链就会被遍历,相似 [[Get]] 操做。
若是原型链上找不到 foo,foo 就会被直接添加到 myObject 上。然而,若是 foo 存在于原型链上层,赋值语句 myObject.foo = "bar" 的行为就会有些不一样。
若是属性名 foo 既出如今 myObject 中也出如今 myObject 的 [[Prototype]] 链上层,那么就会发生屏蔽。myObject 中包含的 foo 属性会屏蔽原型链上层的全部 foo 属性,由于myObject.foo 老是会选择原型链中最底层的 foo 属性。blog
屏蔽比咱们想象中更加复杂。下面咱们分析一下若是 foo 不直接存在于 myObject 中而是存在于原型链上层时 myObject.foo = "bar" 会出现的三种状况:继承
function Foo() { // ... } Foo.prototype.constructor === Foo; // true var a = new Foo(); a.constructor === Foo; // true
Foo.prototype 默认(在代码中第一行声明时!)有一个公有而且不可枚举的属性 .constructor,这个属性引用的是对象关联的函数 Foo。此外,咱们能够看到经过“构造函数”调用 new Foo() 建立的对象也有一个 .constructor 属性,指向“建立这个对象的函数”
function NothingSpecial() { console.log( "Don't mind me!" ); } var a = new NothingSpecial();//Don't mind me! let b = NothingSpecial();//Don't mind me! console.log(a)//NothingSpecial{} console.log(b)//undefined
NothingSpecial 实际上是一个普通函数,但经过使用new调用的时候,它就会构造出一个对象而且赋给 a。这里的调用只是构造函数调用,可是NothingSpecial自己并非一个构造函数。
(1)instanceof:
function Foo() { // ... } var a = new Foo(); console.log(a instanceof Foo) //true
instanceof 操做符的左操做数是一个普通的对象,右操做数是一个函数。instanceof 回答的问题是:在 a 的整条 [[Prototype]] 链中是否有指向 Foo.prototype 的对象?惋惜,这个方法只能处理对象(a)和函数(带 .prototype 引用的 Foo)之间的关系。若是你想判断两个对象(好比 a 和 b)之间是否经过 [[Prototype]] 链关联,只用 instanceof没法实现。
(2)isPrototypeOf:
let a = { x:1 } let b = Object.create(a); let c = { y:2 }; console.log(a.isPrototypeOf(b));//true console.log(c.isPrototypeOf(b));//false
isPrototypeOf是用来判断指定对象a是否存在于另外一个对象b的原型链中,是则返回true,不然返回false。
(3)Object.getPrototypeOf(对象):
function Foo(){ //... } let a = new Foo(); let b = { x:1 } console.log(Object.getPrototypeOf(a) == Foo.prototype);//true console.log(Object.getPrototypeOf(b) == Foo.prototype);//false
Object.getPrototypeOf(对象)能够获取对象的[[prototype]]链
(4) proto :
function Foo(){ //... } let a = new Foo(); console.log(a.__proto__); console.log(Foo.prototype); console.log(Foo.prototype.__proto__); console.log(Object.prototype); console.log(Object.prototype.__proto__);
输出结果:
function Foo(){ //... } let a = new Foo(); let c = { x:1 } console.log(a.__proto__ === Object.prototype);//false console.log(a.__proto__ === Foo.prototype);//true console.log(Foo.prototype.__proto__ === Object.prototype);//true console.log(c.__proto__ === Object.prototype);//true
proto 返回对象上一层[[prototype]]原型对象
若是要访问对象中并不存在的一个属性,[[Get]] 操做就会查找对象内部[[Prototype]] 关联的对象。这个关联关系实际上定义了一条“原型链”(有点像嵌套的做用域链),在查找属性时会对它进行遍历。
全部普通对象都有内置的 Object.prototype,指向原型链的顶端(好比说全局做用域),若是在原型链中找不到指定的属性就会中止。toString()、valueOf() 和其余一些通用的功能都存在于 Object.prototype 对象上,所以语言中全部的对象均可以使用它们。
关联两个对象最经常使用的方法是使用 new 关键词进行函数调用中会建立一个关联其余对象的新对象。使用 new 调用函数时会把新对象的 .prototype 属性关联到“其余对象”。带 new 的函数调用一般被称为“构造函数调用”,尽管它们实际上和传统面向类语言中的类构造函数不同。
虽然这些 JavaScript 机制和传统面向类语言中的“类初始化”和“类继承”很类似,可是 JavaScript 中的机制有一个核心区别,那就是不会进行复制,对象之间是经过内部的[[Prototype]] 链关联的。
出于各类缘由,以“继承”结尾的术语(包括“原型继承”)和其余面向对象的术语都没法帮助你理解 JavaScript 的真实机制(不只仅是限制咱们的思惟模式)。相比之下,“委托”是一个更合适的术语,由于对象之间的关系不是复制而是委托。