内容简介:儘管沒用到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 已經成為標準
- 觀察
person.__proto__
-
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 傳入 firstName
與 lastName
,只負責建立 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
- 分別使用
person.__proto__
與Person.prototype
觀察 -
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 的要求。
- 分別使用
person.__proto__
與Person.prototype
觀察 -
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
字眼都完全沒看見。
- 分別使用
person.__proto__
與Person.prototype
觀察 -
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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解 Java 函数式编程,第 5 部分: 深入解析 Monad
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入浅出Disruptor
- 深入了解 JSONP
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。