深入探討 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
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解 Java 函数式编程,第 5 部分: 深入解析 Monad
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入浅出Disruptor
- 深入了解 JSONP
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Markdown 在线编辑器
Markdown 在线编辑器
RGB HSV 转换
RGB HSV 互转工具