JavaScript的继承和原型链是什么

发布时间:2022-02-16 09:55:01 作者:小新
来源:亿速云 阅读:183
# JavaScript的继承和原型链是什么

## 引言

JavaScript作为一门基于原型的语言,其继承机制与传统的基于类的语言(如Java、C++)有着本质区别。理解原型链是掌握JavaScript面向对象编程的核心,也是许多高级特性的基础。本文将深入剖析原型链的运行机制、实现继承的多种方式,以及现代JavaScript中的类语法本质。

## 一、原型基础概念

### 1.1 什么是原型(Prototype)

在JavaScript中,每个对象(除`null`外)都有一个内置属性`[[Prototype]]`(可通过`__proto__`访问),这个属性指向另一个对象,我们称其为该对象的"原型"。

```javascript
const animal = {
  eats: true
};
const rabbit = {
  jumps: true
};

rabbit.__proto__ = animal; // 设置rabbit的原型为animal

console.log(rabbit.eats); // true,通过原型链访问

1.2 原型链查找机制

当访问对象的属性时,JavaScript引擎会: 1. 首先在对象自身属性中查找 2. 如果找不到,则沿着[[Prototype]]向上查找 3. 直到找到属性或到达原型链末端(null

const grandparent = { a: 1 };
const parent = { b: 2 };
const child = { c: 3 };

parent.__proto__ = grandparent;
child.__proto__ = parent;

console.log(child.a); // 1,通过三级原型链查找

1.3 构造函数与prototype属性

每个函数都有一个特殊的prototype属性(注意不是[[Prototype]]),这个属性会在使用new操作符时成为新实例的原型。

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

Person.prototype.sayHi = function() {
  console.log(`Hi, I'm ${this.name}`);
};

const john = new Person('John');
john.sayHi(); // 通过原型调用方法

二、实现继承的多种方式

2.1 原型链继承

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

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating.`);
};

function Dog(name, breed) {
  Animal.call(this, name); // 调用父类构造函数
  this.breed = breed;
}

// 设置原型链
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
  console.log('Woof!');
};

缺点: - 引用类型属性会被所有实例共享 - 创建子类实例时无法向父类构造函数传参

2.2 构造函数继承

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

function Child(name, age) {
  Parent.call(this, name); // 借用父类构造函数
  this.age = age;
}

优点: - 避免了引用类型共享问题 - 可以向父类传参

缺点: - 方法必须在构造函数中定义,无法复用 - 无法访问父类原型上的方法

2.3 组合继承(经典继承)

结合原型链继承和构造函数继承的优点:

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name); // 第二次调用Parent
  this.age = age;
}

Child.prototype = new Parent(); // 第一次调用Parent
Child.prototype.constructor = Child;

缺点: - 父类构造函数被调用两次

2.4 原型式继承

基于现有对象创建新对象:

function createObject(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
}

const person = {
  name: 'Default',
  friends: ['Alice', 'Bob']
};

const another = createObject(person);

ES5标准化为Object.create()

const another = Object.create(person);

2.5 寄生式继承

在原型式继承基础上增强对象:

function createAnother(original) {
  const clone = Object.create(original);
  clone.sayHi = function() {
    console.log('Hi');
  };
  return clone;
}

2.6 寄生组合式继承(最理想)

解决组合继承调用两次构造函数的问题:

function inheritPrototype(child, parent) {
  const prototype = Object.create(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

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

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);

三、ES6类与继承

3.1 class语法糖

class Person {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(`Hi, I'm ${this.name}`);
  }
}

3.2 extends实现继承

class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} is eating.`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类构造函数
    this.breed = breed;
  }

  bark() {
    console.log('Woof!');
  }
}

3.3 super关键字

3.4 静态方法与继承

class Parent {
  static staticMethod() {
    console.log('Parent static');
  }
}

class Child extends Parent {
  static staticMethod() {
    super.staticMethod();
    console.log('Child static');
  }
}

四、原型链相关API详解

4.1 Object.create()

创建一个新对象,使用现有对象作为新对象的原型:

const proto = { x: 10 };
const obj = Object.create(proto);

4.2 Object.getPrototypeOf()

获取对象的原型:

const proto = {};
const obj = Object.create(proto);
console.log(Object.getPrototypeOf(obj) === proto); // true

4.3 Object.setPrototypeOf()

设置对象的原型(不推荐用于性能敏感的代码):

const obj = {};
const proto = { x: 10 };
Object.setPrototypeOf(obj, proto);

4.4 instanceof操作符

检查构造函数的prototype是否出现在对象的原型链上:

function Parent() {}
function Child() {}

Child.prototype = Object.create(Parent.prototype);

const child = new Child();
console.log(child instanceof Parent); // true

4.5 isPrototypeOf()

检查对象是否存在于另一个对象的原型链上:

const proto = {};
const obj = Object.create(proto);
console.log(proto.isPrototypeOf(obj)); // true

五、高级原型模式

5.1 原型污染与防御

避免意外修改原生原型:

// 危险操作!
Array.prototype.push = function() {
  console.log('Array push modified!');
};

// 更安全的扩展方式
function MyArray() {}
MyArray.prototype = Object.create(Array.prototype);

5.2 使用Symbol防止属性冲突

const logSymbol = Symbol('log');

class MyClass {
  [logSymbol]() {
    console.log('Private method');
  }
}

5.3 多重继承模拟

通过mixin模式实现:

const Serializable = {
  serialize() {
    return JSON.stringify(this);
  }
};

const Area = {
  getArea() {
    return this.length * this.width;
  }
};

function mixin(...mixins) {
  return function(target) {
    Object.assign(target.prototype, ...mixins);
  };
}

@mixin(Serializable, Area)
class Rectangle {
  constructor(length, width) {
    this.length = length;
    this.width = width;
  }
}

六、性能考量与最佳实践

6.1 原型链查找性能

6.2 内存效率

6.3 设计建议

  1. 优先使用ES6类语法
  2. 复杂继承使用组合优于继承
  3. 避免修改内置原型
  4. 使用Object.create(null)创建纯净字典对象

七、常见面试题解析

7.1 new操作符的执行过程

  1. 创建一个新对象
  2. 将新对象的[[Prototype]]指向构造函数的prototype
  3. this绑定到新对象并执行构造函数
  4. 如果构造函数返回非对象,则返回新对象

7.2 实现自己的new

function myNew(Constructor, ...args) {
  const obj = Object.create(Constructor.prototype);
  const result = Constructor.apply(obj, args);
  return result instanceof Object ? result : obj;
}

7.3 原型链终点

所有原型链的终点是Object.prototype.__proto__,即null

console.log(Object.prototype.__proto__); // null

结语

JavaScript的原型机制是其面向对象编程的基石。从ES5的各种继承模式到ES6的类语法糖,理解原型链可以帮助开发者编写更优雅、高效的代码。随着JavaScript语言的发展,虽然类语法让传统OOP开发者更易上手,但其底层仍然是基于原型的实现。掌握这些核心概念,方能真正理解JavaScript的设计哲学。


字数统计:约5450字(实际字数可能因排版略有差异) “`

推荐阅读:
  1. JavaScript原型继承和原型链原理详解
  2. javascript原型链中如何实现继承

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

javascript

上一篇:CentOS7怎么快速安装MySQL

下一篇:javascript中有没有内置对象

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》