深入探討 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


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

查看所有标签

猜你喜欢:

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

算法学

算法学

哈雷尔 / 第1版 (2006年2月1日) / 2006年2月1日 / 38.0

本书的意图在于按序学习或研究,而不是作为一个参考。因而按照每章依赖于前面章节的结构组织本书,且流畅易读。第一部分预备知识中的大部分材料对于那些具有程序设计背景的人是熟悉的。无论是否恰当,本书包含了计算机科学家当前感兴趣的研究专题的简明讨论。这本教科书的书后有每章详细参考书目的注记,并通过“后向”指针把教科书中的讨论与相关文献联系起来。目前的版本包含大量习题,以及大约三分之一的题解。可用题解作为教科......一起来看看 《算法学》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

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

RGB HEX 互转工具

html转js在线工具
html转js在线工具

html转js在线工具