過去到現在寫了很多 this
相關的文章,如果沒有實際運用過 this
的開發者可能會有疑惑「this 是不是只有在面試考題用到?」、「this 在實戰中會用到嗎?」、「為什麼要學 this?」。
其實 this
在實戰中的應用非常廣,已經有接觸框架的開發者對此較不陌生,基本上所有的檔案都會存在 this
,而本篇也就針對 this 的應用情境進行介紹。
過去框架尚未盛行以前,開發上都是平鋪直述的概念在進行,當有一個需求如下時:
function displayData(data, target) {
console.log(data.string);
console.log(`將 ${ data.string } 繪製於 ${ target } 上`)
}
var someData = {
string: '用在卡片上的資料',
target: '卡片'
}
displayData(someData, '卡片');
透過這樣的方式,我們定義了 資料、目標位置、渲染用的函式,此階段中沒有元件的概念,彼此之間再結構上沒有任何關聯性,當程式碼較少時相當好閱讀,但在日趨複雜的情況就可能會有以下的狀況:
接下來導入物件的概念以後,可以將方法與資料整合在一起,這樣就可以解決上述所列出的問題,不僅可以避免資料與方法分離的狀況,命名上也可以更為簡易。
var card = {
data: '卡片上的資料',
target: '卡片',
render: function() {
console.log(`這個是 ${this.data},可以繪製於 ${this.target}`);
// 這個元件是 卡片上的資料,可以繪製於 卡片
},
trigger: function() {
this.render();
}
}
card.trigger();
當透過物件的形式進行開發時,就需要使用 this
來取得或呼叫物件中彼此之間的方法與資料。如果對於 this
觀念想補強可參考此文章。
this
,改用 card
不行嗎?這也是同學們常見的問題,在此範例中物件內的 this
等同於 card
物件,所以將上述程式碼中的 this
均替換為 card
也是可以運作的。
var card = {
data: '卡片上的資料',
target: '卡片',
render: function () {
console.log(`這個是 ${card.data},可以繪製於 ${card.target}`);
// 這個元件是 卡片上的資料,可以繪製於 卡片
},
trigger: function () {
card.render();
},
};
card.trigger();
不過實戰中,以物件的方式建構元件時,會盡可能地增加其使用性,所以 card
在原始碼中並不會是唯一的存在,可能會有更多的相同或類似的結構來作為運用。
如以下的物件可以延伸自 card
元件,如果使用同樣的名稱就會導致運作上的錯誤,維持 this
將是最好的做法。
var navbar = {
data: '導覽列的資料',
target: '導覽列',
render: function() {
console.log(`這個是 ${this.data},可以繪製於 ${this.target}`)
},
trigger: function() {
this.render();
}
}
navbar.trigger();
而實際上是如何建立兩個接近結構的元件呢?
以目前來說兩者的結構是接近的,只有 data 的內容是不同的,這段在實戰中大多都是藉由 “框架” 的特性來完成,以下會繼續介紹框架是如何做到這樣的結構,並且如何管理物件中相同的方法。
為了讓各個物件中會重複使用的方法統一管理,並且讓其又能定義屬於各自的方法,框架中大多會使用 JavaScript 中的一特性 “Prototype” 來進行管理,關於 Prototype 的觀念本篇不會多加敘述,如需了解可參考:
透過原型可以將物件中的方法集中定義,並且使兩者均可透過 this.render()
的方式呼叫此方法。
需要定義原型,則必須運用到 “建構函式”,以下提供的建構函式範例可用來建立如上結構的物件,並且將 render
的方法預先定義為原型,而建構函式在此的目的為:
methods
內,此結構與 Vue 是接近的)function Component(obj) {
var vm = this;
// 取出 methods 屬性內的物件,定義為各自的方法
var methods = Object.keys(obj.methods);
if (methods.length) {
methods.forEach(function(key) {
vm[key] = obj.methods[key];
});
}
vm.data = obj.data;
vm.target = obj.target;
}
Component.prototype.render = function() {
console.log(`這個是 ${this.data},可以繪製於 ${this.target}`);
}
接下來,就可以透過上方定義的建構函式來建立相同的物件。
var navbar = new Component({
data: '導覽列的資料',
target: '導覽列',
methods: {
trigger: function() {
this.render();
}
}
});
navbar.trigger(); // 這個是 導覽列的資料,可以繪製於 導覽列
此結構透過 Chrome Console 可看到類似於先前所列的結構,僅不過將 render 改為在原型內,呼叫方式也一樣是使用 this
。
var card = new Component({
data: '卡片的資料',
target: '卡片',
methods: {
trigger: function() {
console.log(`${this.data} 在這`); // 卡片的資料 在這
}
}
});
card.trigger();
透過建構函式就可不斷建立相同結構的物件,也可定義屬於各自的資料、方法,而物件之中都依然是使用 this
來進行該物件內的所有資料、方法的調用。也因此 this
是一個取得本地元件屬性的方法,如果搞清楚為何使用,也能夠發現其實它並沒有那麼複雜(觀念非常多,但實戰中只會用到一招而已)。