javascript 继承和原型链

继承与原型链

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') 创建对象时,会发生以下过程:

  1. 调用构造函数创建新对象,并将 this 指向该对象
  2. 该对象的 __proto__ 自动指向 Person.prototype
  3. 执行构造函数体

实例、原型和构造函数的关系表现为:

  • 实例的 __proto__ 指向构造函数的 prototype
  • 原型对象的 constructor 属性指向构造函数

因此,所有 new Person() 创建的实例都能共享 Person.prototype 上定义的方法。普通对象相当于 new Object(),其 __proto__ 自然指向 Object.prototype

原型链查找机制

访问对象属性 obj.prop 时,查找过程为:

  1. 先在对象自身属性中查找
  2. 若未找到,则沿 obj.__proto__ 向上查找
  3. 直到 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 改变

继承的实现方式:

  1. 传统方式:通过修改原型链

    • 设置子类 prototype.__proto__ 指向父类 prototype
    • 设置子类 __proto__ 指向父类(用于继承静态方法)
  2. 现代方式:使用 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.prototype
  • Dog.prototype.__proto__ === Animal.prototype
  • Dog.__proto__ === Animal
  • Animal.__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.

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值