四个规则
- 引用类型,都具有对象特征,即(可自由扩展属性);
- 引用类型,都有一个隐式原型__proto__属性,属性值是一个普通的对象;
- 引用类型,隐式原型__proto__的属性值指向它的构造函数的显式原型prototype属性值;
- 当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型__proto__(也就是它的构造用函数的显示原型prototype)中寻找;
引用类型:Object,Array,Function,Date,RegExp,这里的__proto__我们称为隐式原型,没有官方的中文统一叫法;
规则一:引用类型,都具有对象特性,即(可自由扩展属性):
const obj = {}
const arr = []
const fn = function() {}
obj.a = 1
arr.a = 1
fn.a = 1
console.log(obj.a) // 1
console.log(arr.a) // 1
console.log(fn.a) // 1
这个规则很好理解,Date和RegExp也一样的,就不再说明了;
规则二:引用类型,都有一个隐式原型__proto__属性,属性值时一个普通的对象;
const obj = {}
const arr = []
const fn = function() {}
console.log('obj.__proto__', obj.__proto__) //
console.log('arr.__proto__', arr.__proto__)
console.log('fn.__proto__', fn.__proto__)
规则三:引用类型,隐式原型__proto__的属性值指向它的构造函数的显示原型prototype属性值;
const obj = {}
const arr = []
const fn = function() {}
console.log('obj.__proto__ === Object.prototype', obj.__proto__ === Object.prototype) // true
console.log('arr.__proto__ === Array.prototype', arr.__proto__ === Array.prototype) // true
console.log('fn.__proto__ === Function.prototype', fn.__proto__ === Function.prototype) // true
规则四:当你试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型__proto__(也就是它的构造函数的显式原型prototype)中寻找;
const obj = { a: 1 }
console.log(obj.toString) // ƒ toString() { [native code] }
首先,obj对象上并没有 toString属性,之所以能获取到toString属性,是遵循了第四条规则,从它的构造函数Object的prototype中获取;
一个特例
- 我试图想推翻上面的规则,看下面这段代码:
function Person(name) {
this.name = name
return this // 其实这里可以不写的,默认返回this对象
}
var nick = new Person('nick')
console.log(nick.toString) // ƒ toString() { [native code] }
按理说,nick是Person构造函数生成的实例,而Person的prototype并没有toString方法,那么为什么,nick能获取到toString方法呢?
这里就引出原型链的概念了,nick实例先从自身出发寻找,发现并没有toString方法。找不到,就往上找,找到它的构造函数的prototype中,还是没找到,构造函数的prototype也是一个对象,所以就找到它的__proto__,也就是对象的构造函Object的原型,所以就找到了Object.prototype下的toString方法;
一张图片
- 用图片描述原型链:
最后一个是 null , 设计上为了避免死循环而设置的,Object.prototype的隐式原型指向的是 null
一个方法
- instanceof运算符:用于测试构造函数的Prototype属性是否出现在对象原型链中的任何位置。
instanceof的简易手写板,如下所示:
function instance_of(L, R) {
// 验证如果为几百数据类型,就直接返回false
const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol']
if (baseType.includes(typeof(L))) { return false }
let RP = R.prototype // 取R的显示原型
L = L.__proto__ // 取L的隐式原型
while(true) {
if (L === null) { // 找到最顶层
return false
}
if(L === RP) { // 严格相等
return true
}
L = L.__proto__ // 没找到继续向上一层原型链查找
}
}
我们再来看下面这段代码
function Foo(name) {
this.name = name
}
var f = new Foo('nick')
console.log(f instanceof Foo) // true
console.log(f instanceof Object) // true
上面代码判断流程大致如下:
- f instanceof Foo: f的隐式原型__proto__和Foo.prototype,是相等的,所以返回 true
- f instanceof Object: f的隐式原型__proto__,和Object.prototype不等,所以继续往上走。 f的隐式原型__proto__指向 Foo.prototype, 所以继续用Foo.prototype.__proto__去对比 Object.prototype, 这会相等了。因为Foo.prototype就是一个普通对象;