JS 原型和原型链
JavaScript 基于原型继承,class 实际上是函数,是一种语法糖;
隐式原型和显式原型
.__proto__
是隐式原型;.prototype
是显式原型;
每个实例都有隐式原型;
每个 class 都有显式原型;
每个实例的.__proto__
都指向 class 的.prototype
;
实例会先在自身的属性和方法上寻找,如果找不到则自动顺着.__proto__
链查找;
instanceof 判断数据类型
instanceof
只能用于判断引用类型;
instanceof
通过左操作数的隐式原型链
(__proto__)向上查找,如果在某一刻与右操作数的显式原型
(prototype)重合,则返回 true,否则继续向上查找,直到 null,返回 false;(注意 ⚠️:左边是原型链,右边是原型);
也就是说,instanceof
根据对象(左操作数)是否是某个构造函数(右操作数)的实例来返回布尔值(判断对象的类型);
引用《你不知道的 JavaScript 上卷》第二部分第五章的一句话,可以很形象的描述instanceof
工作的原理:a instanceof Foo
instanceof 回答的问题是:在 a 的整条
[[Prototype]]
链中是否有 Foo.prototype 指向的对象?
[[Prototype]]
属性是对象的一个特殊的内置属性,其实就是对于其他对象的引用,几乎所有对象在创建时[[Prototype]]
属性都会被赋予一个非空的值;
当试图引用对象的属性时,会触发[[Get]]
操作,如果对象自身有这个属性,则使用它,否则沿着[[Prototype]]
链向上查找,这个过程中如果找到匹配项则返回值,如果找完整条[[Prototype]]
链都没有匹配项,则返回 undefined;
规则很眼熟吧,没错,[[Prototype]]
链就是原型链;
简单实现 instanceof
1 | /** |
Object.prototype.toString.call()
Object.prototype.toString()
是 Object 原型对象上的方法,返回表示该对象类型的字符串;
为什么不直接调用对象自身的 toString() 方法
不直接调用是因为大部分类型都重写了 toString() 方法,代表各自不同的逻辑,所以需要调用 Object 原型对象上的 toString() 方法,才能返回表示对象类型的字符串;
例如:
- Array 的 toString() 方法返回数组元素的字符串表示;
- String 的 toString() 方法返回字符串的字面量表示;
- Number 的 toString() 方法返回数值对应的字符串表示;
- Boolean 的 toString() 方法返回布尔值对应的字符串表示
Object.getPrototypeOf()
Object.getPrototypeOf()
返回指定对象的原型(内部[[Prototype]]
属性的值);
在 ES5 中,如果传递给方法的参数不是对象,则会抛出 TypeError 异常;
在 ES6 中,如果传递给方法的参数不是对象,则会强制类型转换为对象;
isPrototypeOf()
还是引用《你不知道的 JavaScript 上卷》第二部分第五章的一句话,可以很形象的描述isPrototypeOf()
工作的原理:Foo.prototype.isPrototypeOf(a)
isPrototypeOf() 回答的问题是:在 a 的整条
[[Prototype]]
链中是否出现过 Foo.prototype?
isPrototypeOf()
并不一定需要间接引用构造函数,只需要两个对象也可以判断它们之间的关系,它们的.prototype
或__proto__
属性会被自动访问;
1 | //问:b 是否出现在 c 的原型链中? |
特殊情况:
Object.getPrototypeOf(包装器(构造函数))
会把创建对象的包装器(构造函数)当作对象,返回的是函数对象的原型Function.prototype
;
class 的原型本质
class 的原型本质是构造函数 + 原型链
,继承通过原型链实现,类中定义的方法放在构造函数的原型上;
画图理解原型链
下面代码的原型链是怎样的?
1 | function Foo() {} |
fn.__proto__ === Foo.prototype
,实例的隐式原型指向(构造)函数的显式原型,这是毋庸置疑的;Foo.prototype.constructor === Foo
,函数原型的构造函数指向函数自身(废话);Foo.__proto__ === Function.prototype
,Foo 是函数,所以 Foo 的隐式原型指向 Function 的显式原型;Foo.prototype.__proto__ === Object.prototype
,一切皆对象,所以函数原型的隐式原型最终指向 Object.prototype;Object.prototype.__proto__ === null
,原型链的终点,1、4、5 组成了一条原型链;Function.__proto__ === Function.prototype
,后面解释;Function.prototype.constructor === Function
,同 2;Function.prototype.__proto__ === Object.prototype
,同 4;Object.__proto__ === Function.prototype
,同 6;Object.prototype.constructor === Object
,同 2;
解释一下 6 和 9,Function 和 Object 都是(构造)函数,因为可以使用 new 操作符,既然是函数,所以原型是 Function.prototype 就合情合理了;
如图: