笔记而已,欢迎指正
-
__proto__
是实例所独有的,正常情况下指向其构造函数的 prototype -
prototype
是函数独有的,函数声明后自带 prototype 对象 -
constructor
是 prototype 对象独有的,指向构造函数 -
函数是 Function 的实例也是对象,所以函数同时拥有
__proto__
、constructor -
ES6 中
__proto__
可用 Object.getPrototypeOf 和 Object.getPrototypeOf 来操作
Function 较为特殊
typeof Function.prototype // "function"
Function.prototype instanceof Function // false
Function.prototype instanceof Object // true
-
最简单的说法是 Function.prototype 类型是函数,是为了兼容之前版本的 EcmaScript
-
Function.prototype 本身是一个对象,是 Object 的实例,只是内部实现了
[[Call]]
属性变得可以执行,返回 undefined
- Object 是函数,所以是 Function 的实例
Object.__proto__ === Function.prototype // true
-
Object.prototype 是个对象,但它的
__proto__
为 null(原型链终点) -
普通函数是 Function 的实例,其 prototype 是 Object 的实例
function Foo() {}
Foo.__proto__ === Function.prototype // true
Foo.prototype.__proto__ === Object.prototype // true
拥有两条继承链
- 子类 prototype 原型指向父类 prototype,用于继承原型上的属性和方法
class Parent {}
class Child extends Parent {}
Child.prototype.__proto__ === Parent.prototype // true
- 子类原型指向父类,用于继承静态属性和方法
Child.__proto__ === Parent // true
- 内部实现时,子类构造函数中调用父类,用于继承实例属性和方法
function Child() {
Parent.call(this)
}
可自行在 babel 官网编译
class Parent {
constructor() {
this.setup()
}
static level = 'parent'
setup = () => {
console.log("parent")
}
}
class Child extends Parent {
id = 61
setup = () => {
console.log("child")
}
getId() {
console.log("child getId")
return id
}
}
new Child()
-
函数属性
setup
(或称函数表达式)被当做了普通属性,通过 defineProperty 实现 -
函数声明
getId
则是挂在了 prototype 上 -
另外属性赋值是在构造函数中完成,函数声明则是通过「继承闭包」与构造函数分开,再挂到 prototype
-
上一点对 JS 的继承会有影响,由打印可见,
setup
最终还是执行 parent 上的函数,就是因为 Child 在Parent.call(this)
时,就已经把函数属性挂上去了 -
虽然函数属性书写时绑定 this 方便,但在继承时需要额外注意
- polyfill:通过不断迭代
__proto__
,并与查找对象的 prototype 做对比,直到遇到 null,即原型链终点
function instanceOf(object, target) {
while (object != null) {
if (object == target.prototype) return true
object = object.__proto__
}
return false
}
instanceOf(new Date(), Object) // true
- 查找细节
function A() {}
function B() {}
// 1
A.prototype = B.prototype
// 2
A.prototype = new B()
// 3
A.prototype = Object.create(B.prototype)
const a = new A()
-
2、3 都是继承的“半成品”,只继承了 B 原型上的属性和方法,操作 A.prototype 不会影响 B.prototype
-
1 是赋值,A、B 共用一个原型对象,操作其中一个会影响另一个。但用 instanceof 判断
B.prototype.constructor === B // true
a instanceof A // true
a instanceof B // true
-
可知,instanceof 查找时有且只对比了原型对象,prototype.constructor 不会影响 instanceof 的判断
-
prototype.constructor 原本是 JS 设计的遗留产物,直到 ES6 时才正式定义和使用。为保持良好的习惯,应该尽量让原型的 constructor 指向其构造函数