人人都爱原型链
# 人人都爱原型链
# 四个规则
我们先来了解下⾯引⽤类型的四个规则:
1、引⽤类型,都具有对象特性,即可⾃由扩展属性。
2、引⽤类型,都有⼀个隐式原型
__proto__
属性,属性值是⼀个普通的对象。3、引⽤类型,隐式原型
__proto__
的属性值指向它的构造函数的显式原型__proto__
属性值。4、当你试图得到⼀个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型
__proto__
(也就是它的构造函数的显式原型__proto__
)中寻找。
"引⽤类型: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
2
3
4
5
6
7
8
9
"这个规则应该⽐较好理解,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__);
2
3
4
5
6
# 规则三
引⽤类型,隐式原型
__proto__
的属性值指向它的构造函数的显式原型__proto__
属性值:
onst obj = {};
const arr = [];
const fn = function() {}
obj.__proto__ == Object.prototype // true
arr.__proto__ === Array.prototype // true
fn.__proto__ == Function.prototype // true
2
3
4
5
6
# 规则四
当你试图得到⼀个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型
__proto__
(也就是它的构造函数的显式原型 )中寻找:
const obj = { a:1 }
obj.toString
// ƒ toString() { [native code] }
2
3
⾸先,
obj
对象并没有toString
属性,之所以能获取到toString
属性,是遵循了第四条规则,从它的构造函数Object
的prototype
⾥去获取。
# ⼀个特例
我试图想推翻上⾯的规则,看下⾯这段代码:
function Person(name) {
this.name = name
return this // 其实这⾏可以不写,默认返回 this 对象
}
var nick = new Person("nick")
nick.toString
// ƒ toString() { [native code]
2
3
4
5
6
7
按理说,
nick
是Person
构造函数⽣成的实例,⽽Person
的prototype
并没有toString
⽅法,那么为什么,nick
能获取到toString
⽅法?这⾥就引出
原型链
的概念了,nick
实例先从⾃身出发检讨⾃⼰,发现并没有toString
⽅法。找不到,就往上⾛,找
Person
构造函数的prototype
属性,还是没找到。构造函数的prototype
也是⼀个对象嘛,那对象的构造函数是Object
,所以就找到了Object.prototype
下的toString
⽅法。
"上述寻找的过程就形成了原型链的概念,我理解的原型链就是这样⼀个过程。也不知道哪个⼈说过⼀句,JavaScript ⾥万物皆对象。从上述情况看来,好像是这么个理。🤔🤔
# 一张图片
⽤图⽚描述原型链:
最后⼀个
null
,设计上是为了避免死循环⽽设置的,Object.prototype
的隐式原型指向null
。
# 一个方法
instanceof
运算符⽤于测试构造函数的prototype
属性是否出现在对象原型链中的任何位置。 instanceof 的简易⼿写版,如下所示:
// 变量R的原型 存在于 变量L的原型链上
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__; // 没找到继续向上⼀层原型链查找
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
我们再来看下⾯这段代码:
function Foo(name) {
this.name = name;
}
var f = new Foo('nick')
f instanceof Foo // true
f instanceof Object // true
2
3
4
5
6
上述代码判断流程⼤致如下:
1、
f instanceof Foo
:f
的隐式原型__proto__
和Foo.prototype
,是相等的,所以返回true
2、
f instanceof Object
:f
的隐式原型__proto__
,和Object.prototype
不等,所以继续往上⾛。f
的隐式原型__proto__
指向Foo.prototype
,所以继续⽤Foo.prototype. __proto__
去对⽐Object.prototype
,这会⼉就相等了,因为Foo.prototype
就是⼀个普通的对象。
"再⼀次验证万物皆对象....
原型链就是⼀个过程,原型是原型链这个过程中的⼀个单位,贯穿整个原型链。