深入探討 ECMAScript 之 Class 與 Prototype
栏目: JavaScript · 发布时间: 7年前
内容简介:儘管沒用到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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Google's PageRank and Beyond
Amy N. Langville、Carl D. Meyer / Princeton University Press / 2006-7-23 / USD 57.50
Why doesn't your home page appear on the first page of search results, even when you query your own name? How do other web pages always appear at the top? What creates these powerful rankings? And how......一起来看看 《Google's PageRank and Beyond》 这本书的介绍吧!