深入探討 Class 與 Prototype

栏目: 编程语言 · 发布时间: 6年前

内容简介:儘管沒用到ECMAScript 5ECMAScript 2015

儘管沒用到 繼承 ,ECMAScript 有一個很特別的 Prototype 概念,負責存放 object 的 method 與共用 property,雖然 ECMAScript 2015 支援了 class 語法,但事實上底層仍然使用 Prototype 實作,了解 Prototype 能讓我們更進一步掌握 ECMAScript。

Version

ECMAScript 5

ECMAScript 2015

Factory Function

function createPerson(firstName, lastName) {
  return {
    firstName,
    lastName,    
  };
}

const person = createPerson('Sam', 'Xiao');

const prototype = {
  fullName: function() {
    return this.firstName + ' ' + this.lastName;
  },
};
Object.setPrototypeOf(person, prototype);

console.log(person.__proto__);
console.log(person.fullName());

我們知道若將 method 放在 object,當建立多個 object 時,會不斷建立新的 method,較浪費記憶體,比較好的方式是將 property 建立在 object 內,而將 method 建立在 prototype。

第 1 行

function createPerson(firstName, lastName) {
  return {
    firstName,
    lastName,    
  };
}

const person = createPerson('Sam', 'Xiao');

建立 createPerson() Factory Function,只負責建立 object 的 property 即可。

第 10 行

const prototype = {
  fullName: function() {
    return this.firstName + ' ' + this.lastName;
  },
};
Object.setPrototypeOf(person, prototype);

使用 Object.setPrototypeOf() 動態將 prototype 成為 person 的 prototype。

Object.setPropertyOf() 為 ECMAScript 2015 所新增

17 行

console.log(person.__proto__);

使用 person.__proto__ 觀察其 prototype。

__proto__ 在 ECMAScript 2015 已經成為標準

深入探討 Class 與 Prototype

  1. 觀察 person.__proto__
  2. person 的 prototype 為含有 fullName() 的 object。

Constructor Function

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype.fullName = function() {
  return this.firstName + ' ' + this.lastName;
}

const person = new Person('Sam', 'Xiao');

console.log(person.__proto__);
console.log(Person.prototype);
console.log(person.fullName());

自己使用 Factory Function 實踐 prototype 當然可行,但程式碼稍嫌冗長,ECMAScript 另外提供了 Constructor Function,讓我們以更精簡的方式實現 prototype。

第 1 行

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

類似 Factory Function 傳入 firstNamelastName ,只負責建立 object 的 property 即可。

其中 this 為稍後 new 所建立的 object,因此可以直接使用 this.firstName 新增 property。

第 6 行

Person.prototype.fullName = function() {
  return this.firstName + ' ' + this.lastName;
}

Person.prototype 預設為 {} ,直接對 {} 動態加上 fullName() method。

prototype 為 function 專屬的 property,在 object 沒有 prototype ,只有 __proto__

第 10 行

const person = new Person('Sam', 'Xiao');

當 function 使用 new 時,就搖身一變成為 Constructor Function, this 為所建立的 object。

12 行

console.log(person.__proto__);
console.log(Person.prototype);

無論使用 person.__proto__Person.prototype ,都得到相同結果,prototype 都是指向相同的 object。

我們可以發現 Constructor Function 寫法,遠比土法煉鋼的 Factory Function 精簡,這也是為什麼在 ES5 時代,都是使用 Constructor Function,而很少使用 Factory Function

深入探討 Class 與 Prototype

  1. 分別使用 person.__proto__Person.prototype 觀察
  2. person.__proto__Person.prototype 的結果都一樣
function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype = {
  constructor: Person,
  fullName: function() {
    return this.firstName + ' ' + this.lastName;
  },  
};

const person = new Person('Sam', 'Xiao');

console.log(person.__proto__);
console.log(Person.prototype);
console.log(person.fullName());

第 6 行

Person.prototype = {
  constructor: Person,
  fullName: function() {
    return this.firstName + ' ' + this.lastName;
  },  
};

若 method 多時,其實有另外一種寫法,就是直接使用 Object Literal 方式定義 object,然後指定給 prototype

不過這種寫法有個地方要小心,之前 Person.prototype.fullName 是對 {} 動態新增 method,原本 {} 的 property 都還留著,其中一個最重要的 property 就是 constructor 指向 Person Constructor Function。

但若將整個 Object Literal 指定給 Person.prototype 時,別忘了要自行補上 constructor: Person ,這才符合原本 Person.prototype.constructor 要指向 Constructor Function 的要求。

深入探討 Class 與 Prototype

  1. 分別使用 person.__proto__Person.prototype 觀察
  2. person.__proto__Person.prototype 的結果都一樣

我在學習 Prototype 時,最大的關卡是觀念上明明是要對 object 指定 prototype,為什麼到最後是對 Constructor Function 指定 prototype 呢 ?

其實剛剛有發現一個有趣的現象:

person.__proto__Person.prototype 指向同一個 object 。

Factory Function 是直接建立 prototype object,再透過 Object.setPropertyOf()__proto__ 指向 prototype object。

Constructor Function 也是直接建立 prototype object,讓 __proto__ 指向 Constructor Function 的 prototype property。

也就是說,指定給 Constructor Function 的 prototype 的 object,最後相當於指定給 object 的 __proto__ ,這也是為什麼我們明明希望將 prototype object 指定給 __proto__ ,卻最後卻指定給 Constructor Function 的 prototype ,因為意義一樣,這就是 Constructor Function 的黑魔法。

Class

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  
  fullName() {
    return this.firstName + ' ' + this.lastName;
  }
}

const person = new Person('Sam', 'Xiao');

console.log(person.__proto__);
console.log(Person.prototype);
console.log(person.fullName());

ECMAScript 2015 支援 class 後,語法又更簡單了,連 prototype 字眼都完全沒看見。

深入探討 Class 與 Prototype

  1. 分別使用 person.__proto__Person.prototype 觀察
  2. person.__proto__Person.prototype 的結果都一樣

這再次證明了 ECMAScript 2015 的 class ,其本質仍然是 prototype-based,而非 class-based,method 該放在 prototype 的觀念也完全一樣,只是 prototype 這些瑣事,底層都幫你做掉了

Conclusion

  • 隨著時代的進步,寫法不斷的精簡,但 ECMAScript 的 prototype-based 本質是不變的
  • 雖然名義上是針對 Constructor Function 的 prototype 指定 prototype object,但也相當於是對 __proto__ 指定 prototype object
  • ECMAScript 2015 的 class 寫法雖然完全看不到 prototype ,但本質仍是將 method 放在 prototype,是道地的 Syntatic Sugar

Reference

MDN , Object.prototype.constructor

MDN , Object prototypes

John Resig , Secret of the JavaScript Ninja, First Edition


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First 设计模式(中文版)

Head First 设计模式(中文版)

弗里曼 / O'Reilly Taiwan公司 / 中国电力出版社 / 2007-9 / 98.00元

《Head First设计模式》(中文版)共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式。前言先介绍这本书的用法;第1章到第11章陆续介绍的设计模式为Strategy、Observer、Decorator、Abstract Factory、Factory Method、Singleton,Command、Adapter、Facade、TemplateMethod、I......一起来看看 《Head First 设计模式(中文版)》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具