[筆記][JS]為何使用原型

Ian Chen
5 min readFeb 10, 2020

前幾天向朋友請教了一些有關JS原型的問題,趁自己還沒忘記趕快把它做成筆記。

為何使用prototype而不使用function?

在JS的世界中,並沒有真實的Class類別,JS所提供的類別不過是使用原型所實作出的語法糖。

在JS ES5中:

function Point(x, y) {
this.x = x;
this.y = y;
}

Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

使用JS ES6提供的Class語法糖:

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}

toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}

因此,在使用Class之前,我們都應該先暸解Prototype的基本用法。

這邊用一個更簡單的例子比較為什麼要使用原型而不是function。

假設我們今天要用JS去實作一個程式,它有著“誰說了什麼”的簡單功能,當我們使用function時會長成這樣:

const say = (name, msg) => {
console.log(`${name}:${msg}`)
}
say('ian','hi!') // ian:hi!
say('coco','hello!') // coco:hello!

使用function看似精簡,但當今天有很多話要說時就會變成:

say('ian','hi!') // ian:hi!
say('coco','hello!') // coco:hello!
say('ian','hi!') // ian:hi!
say('coco','hello!') // coco:hello!
say('ian','hi!') // ian:hi!
say('coco','hello!') // coco:hello!
say('ian','hi!') // ian:hi!
say('coco','hello!') // coco:hello!

在這邊不難發現,這段對談其實只有ian和coco在對話,但我們卻在執行函式時都多用了一個變數去存放人名。

在這邊即使我們沒有原型的概念,也同樣可以利用物件解決問題!

const ian = {
name:'ian',
say(text){
console.log(`${this.name}:${text}`)
}
}
const coco = {
name:'coco',
say(text){
console.log(`${this.name}:${text}`)
}
}
ian.say('hi!') // ian:hi!
coco.say('hello!') // coco:hello!

這樣一來,我們就能夠在每次呼叫函式時少傳一個變數。

但我們再仔細比較一下這兩個物件就會發現:這兩個物件的變數以及方法根本一模模一樣樣啊!!!

這時候如果我們知道怎麼使用new建構物件,就能輕鬆解決這個問題:

function People(name){
this.name = name
this.say = function(text){
console.log(`${this.name}:${text}`)
}
}
//使用new建構物件實體const ian = new People('ian');
const coco= new People('coco');
ian.say('hi!') // ian:hi!
coco.say('hello!') // coco:hello!

做到這邊,大家可能會想說:不對啊,主題不是原型嗎?說好的原型勒???

別急,上面的範例雖然有效解決了前面的所有問題,但是當我們這樣做時:

ian.say = null;
coco.say('hello!') // coco:hello!

我們會發現如果把ian物件的say()給清除時,coco物件的say()卻還是能夠執行,這是因為當我們使用new去創造實體物件時,JS會分別做出一個say()放到各自的物件當中,換言之,這兩個say()並不是存放在同一個記憶體位置。

那如果我希望兩個物件的say()是指向同樣的記憶體位置呢???

別急別急,Prototype不就來了嗎?

function People(name){
this.name = name
}
People.__proto__.say = function(text){
console.log(`${this.name}:${text}`)
}
const ian = new People('ian');
const coco= new People('coco');
ian.__proto__.say = null;
coco.say() // 不能用了!

這邊補充一下,範例中的People.__proto__.say便是指定原型的方式。

也因為say()是使用原型指定的,所以當ian物件中的say函式被清掉,coco物件的say()就也不能使用了!

今天的筆記就到這邊結束,希望各位讀者看到這篇文章有解決跟我一樣的問題!

最後的最後,特別感謝神Q超人的耐心講解,雖然他好像看不到就是了XD

--

--