两条规则:
实例.__proto__ === 构造函数.prototype
实例.属性 === 如果自身有该属性 ? 实例.属性 : 实例.[__proto__]n次.属性
关于规则2的解释:
查找实例的属性时,先判断自身有没有这个属性,如果有,那么直接获取。
否则,查找它的__proto__
有没有这个属性,有的话,就是它,
否则,查找它的__proto__.__proto__
有没有这个属性……
如果一直找不到,就是undefined
。(此处必有蹊跷……略
测试用例:(测试环境,Chrome 44,Win7)
1. 考察构造函数和实例
var F=function(){}; //构造函数F
F.prototype={}; //先设置构造函数的prototype
var f=new F; //造一个实例
console.assert(f.__proto__===F.prototype); //根据规则1
console.assert(f.a===undefined); //实例还没有这个属性
F.prototype.a=1; //给prototype增加一个属性
console.assert(f.a===f.__proto__.a); //根据规则2
console.assert(f.a===F.prototype.a); //根据规则1
console.assert(f.a===1); //找到了属性值
总结:
f.a===f.__proto__.a
===F.prototype.a===1
2. 考察继承
var G=function(){}; //再来一个构造函数
G.prototype=f; //让这个构造函数的prototype是刚才那个实例
var g=new G; //再造一个实例
console.assert(g.__proto__===G.prototype); //根据规则1
console.assert(g.__proto__===f); //根据刚才的赋值
console.assert(g.a===g.__proto__.a); //根据规则2
console.assert(g.a===f.a); //根据上面的推导
console.assert(g.a===1); //找到了属性值
总结:
g.a===g.__proto__.a===g.__proto__.__proto__.a
===G.prototype.__proto__.a===f.__proto__.a===F.prototype.a===1
3. 考察只读性
g.a=2; //为实例增加属于自己的属性
console.assert(g.a===2); //根据规则2
console.assert(f.a===1); //不影响其他实例的查找规则
结论:
原型继承,本质上利用了实例属性的查找规则。
参考文献:
《ECMAScript® 2015 Language Specification》——第3页 4.2.1