理解 JavaScript 原型和继承:从原型链到类的演变

在 JavaScript 中,原型和继承是非常核心的概念,它们帮助我们实现代码复用、构造更灵活的对象模型。理解原型、原型链、new 的实现以及类(class)的使用,能帮助你更好地理解 JavaScript 中的继承机制,并且理解原型与类之间的关系。

1. 显示原型和隐式原型

显示原型:prototype

每个构造函数都会有一个 prototype 属性,这个属性是一个对象,包含了通过构造函数创建的实例共享的属性和方法。比如,假设我们有一个构造函数 Parent,如下所示:

javascript 复制代码
function Parent(age) {
  this.name = 'parent';
  this.age = age;
}

Parent.prototype.say = function() {
  console.log('hello');
};

在上面的例子中,say 方法被定义在 Parent.prototype 上,意味着所有通过 Parent 构造函数创建的实例都可以共享这个 say 方法。

隐式原型:__proto__

每个对象都有一个 __proto__ 属性,它指向该对象的构造函数的 prototype。通过 __proto__,我们可以访问到对象继承自其构造函数的所有属性和方法。

javascript 复制代码
const p = new Parent(30);
console.log(p.__proto__ === Parent.prototype);  // true

可以看到,p.__proto__ 实际上是 Parent.prototype

2. 原型链

原型链是 JavaScript 中的一个非常重要的概念。它描述了对象如何通过 __proto__ 访问继承自父对象的属性和方法。如果在对象本身找不到某个属性或方法,JavaScript 会沿着 __proto__ 向上查找,直到找到为止,最终会查找到 Object.prototype,如果还没有找到,就返回 null

例如,假设我们创建了一个 Child 构造函数,并将其 prototype 设置为一个 Parent 的实例:

javascript 复制代码
Parent.prototype.say = function() {
  console.log('hello');
};

function Parent(age) {
  this.name = 'parent';
  this.age = age;
}

Child.prototype = new Parent();  

function Child(name) {
  this.name = name;
}

const c = new Child('child');
console.log(c.name);  // 'child'
console.log(c.age);   // undefined
c.say();              // 'hello'

在这个例子中,Child 的实例 c 没有直接继承 say 方法,但因为 Child.prototypeParent 的实例,所以 c 可以访问到 Parent.prototype 上的 say 方法。我们看到,c__proto__ 链指向了 Child.prototype,而 Child.prototype__proto__ 又指向了 Parent.prototype,最终找到了 say 方法。

3. new 的实现

理解 new 是如何工作的,可以帮助我们更好地理解 JavaScript 中的构造函数和原型机制。new 操作符背后做了几件事情:

  1. 创建一个新的空对象。
  2. 该对象的 __proto__ 指向构造函数的 prototype
  3. this 被绑定到该新对象上,构造函数内部的代码执行。
  4. 如果构造函数没有显式返回对象,new 返回新创建的对象。

例如,下面是 new 的简化实现:

javascript 复制代码
function myNew(constructor, ...args) {
  const obj = {};  // 1. 创建新对象
  obj.__proto__ = constructor.prototype;  // 2. 指向构造函数的 prototype
  constructor.apply(obj, args);  // 3. 执行构造函数,并将 obj 作为 this
  return obj;  // 4. 返回新对象
}

4. 通过 Object.create 创建原型链

有时,我们希望创建一个对象,并将其原型指向另一个对象。Object.create 是实现这一目标的一个方法。它会创建一个新对象,并将新对象的 __proto__ 指向给定的对象。

javascript 复制代码
const obj = { name: '张三', age: 40 };
const newObj = Object.create(obj);

console.log(newObj.name);  // '张三'

在上面的代码中,newObj__proto__ 指向了 obj,因此它可以访问 obj 上的属性。

5. 构造函数与 class 的继承

JavaScript 的继承模型经历了从原型链到 class 的过渡。在 ES6 中,class 提供了一种更为简洁的方式来实现继承。

javascript 复制代码
class Parent {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log('hello');
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name, age);
    this.sex = 'boy';
  }
}

const c = new Child('张三', 18);
c.say();  // 'hello'

在这个例子中,Child 通过 extends 继承了 Parent,并使用 super() 调用了父类的构造函数。这使得代码更加简洁且易于理解。

6. 总结

JavaScript 的原型和继承机制是构建面向对象程序的基础。通过理解原型、原型链、new 的工作原理以及 class 的使用,我们能够更好地掌握 JavaScript 中的对象继承关系和构造函数的实现。

  • 原型和原型链 是 JavaScript 继承的核心,它让我们能够共享方法和属性。
  • new 操作符 是实例化对象的关键,理解其实现有助于更深入地理解构造函数。
  • Object.create 提供了一种创建对象并指定其原型的简洁方式。
  • ES6 类(class 简化了继承的语法,使得对象的创建和继承更加直观和易用。
相关推荐
uhakadotcom5 分钟前
阿里云Tea OpenAPI:简化Java与阿里云服务交互
后端·面试·github
美食制作家13 分钟前
【无标题】Threejs第一个3D场景
javascript·three
uhakadotcom1 小时前
图像识别中的三大神经网络:Inception、ResNet和VGG
算法·面试·github
uhakadotcom1 小时前
DeepFM算法:提升CTR预估和推荐系统的强大工具
算法·面试·github
HelloRevit1 小时前
React DndKit 实现类似slack 类别、频道拖动调整位置功能
前端·javascript·react.js
ohMyGod_1232 小时前
用React实现一个秒杀倒计时组件
前端·javascript·react.js
uhakadotcom2 小时前
Python 中的 @staticmethod 和 @classmethod 详解
后端·面试·github
艾克马斯奎普特2 小时前
Vue.js 3 渐进式实现之响应式系统——第四节:封装 track 和 trigger 函数
javascript·vue.js
uhakadotcom2 小时前
单点登录的两大核心技术:SAML和OIDC
后端·面试·github
JustHappy3 小时前
「我们一起做组件库🌻」虚拟消息队列?message组件有何不同?(VersakitUI开发实录)
前端·javascript·vue.js