继承与原型链
JavaScript 通过原型链机制实现了类似传统面向对象语言的继承特性。几乎所有对象都继承自 Object.prototype(使用 Object.create() 创建的对象除外)。
原型链机制
在定义构造函数时,我们通常在函数的 prototype 属性上添加实例共享的属性和方法:
function Person(name) {
this.name = name
this.sayHello()
}
Person.prototype.sayHello = function() {
console.log('hello', this.name)
}
当执行 new Person('xxx') 创建对象时,会发生以下过程:
- 调用构造函数创建新对象,并将
this指向该对象 - 该对象的
__proto__自动指向Person.prototype - 执行构造函数体
实例、原型和构造函数的关系表现为:
- 实例的
__proto__指向构造函数的prototype - 原型对象的
constructor属性指向构造函数
因此,所有 new Person() 创建的实例都能共享 Person.prototype 上定义的方法。普通对象相当于 new Object(),其 __proto__ 自然指向 Object.prototype。
原型链查找机制
访问对象属性 obj.prop 时,查找过程为:
- 先在对象自身属性中查找
- 若未找到,则沿
obj.__proto__向上查找 - 直到
Object.prototype(原型链终点,其__proto__为null)
obj instanceof Person 的原理就是检查原型链中是否存在 Person.prototype。
判断属性是否直接属于对象时,推荐使用:
Object.hasOwn(o, v)Object.prototype.hasOwnProperty.call(o, v)
避免直接使用 o.hasOwnProperty(v),因为对象可能自定义了该方法,导致无法正确访问原型链上的方法。
继承实现
理解继承前需要明确一些基本关系:
- 所有函数(包括箭头函数)都继承自
Function.prototype - 函数本身也是对象
const fn = () => {}
console.log(fn.__proto__ === Function.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
箭头函数的特殊性:
- 没有
prototype属性,不能用作构造函数 - 继承父级作用域的
this,无法通过call/apply/bind改变
继承的实现方式:
-
传统方式:通过修改原型链
- 设置子类
prototype.__proto__指向父类prototype - 设置子类
__proto__指向父类(用于继承静态方法)
- 设置子类
-
现代方式:使用
class语法
class Animal {
constructor(name) {
this.name = name
}
sayHello() {
console.log('hello, my name is ' + this.name)
}
static log() {
console.log('this is static log')
}
}
class Dog extends Animal {
constructor(name, age) {
super(name)
this.age = age
}
sayHello() {
console.log('hello, my name is ' + this.name + ', I am a dog')
}
run() {
console.log(this.name + ' is running')
}
}
const dog = new Dog('旺财', 1)
使用extends继承关系的原型链同样有以下的相等关系:
dog.__proto__ === Dog.prototypeDog.prototype.__proto__ === Animal.prototypeDog.__proto__ === AnimalAnimal.__proto__ === Function.prototype
可以看出继承主要就是原型继承(Dog.prototype.__proto__ === Animal.prototype)和方法继承(Dog.__proto__ === Animal)
class定义的类有一些和构造函数不同之处:
- 类只能使用
new调用, 不能像普通函数那样直接调用 - 原型或静态属性都是不可枚举的
- 类中的所有静态函数或原型上的函数也不可以使用
new调用 - 函数的
prototype属性不允许重新赋值, 但可以新增、修改或删除
最后说一下Function.prototype. 这个是普通对象, 因此它也继承了Object.prototype, 这就是为什么Object.hasOwnProperty() 可以直接调用, 因为Object的原型是Function.prototype, 原型的原型就是Object.prototype.
1452

被折叠的 条评论
为什么被折叠?



