溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript的繼承和原型鏈是什么

發布時間:2022-02-16 09:55:01 來源:億速云 閱讀:204 作者:小新 欄目:web開發
# 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關鍵字

  • 作為函數調用時(super()),代表父類構造函數
  • 作為對象調用時(super.method()),指向父類原型

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 原型鏈查找性能

  • 原型鏈過長會影響查找性能
  • 現代JavaScript引擎已優化原型查找

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字(實際字數可能因排版略有差異) “`

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女