[JavaScriptWeird]No.43 Reflection 與 Extend

前言

這門課程推出的時候 ES6 還未問世,現在可以看到許多的框架都有 Extend 的概念,甚至 ES6 也有 Extends ,而本小節課程作者要用 underscore.js ,利用一個叫做 Reflection 的概念達成 Extend。

Reflection 與 Extend

Reflection 意思就是一個物件可以列出自身的屬性,並且改變當中的屬性、方法。而 JavaScript 物件有能力可以看自己的屬性和方法,而我們可以透過這樣達成一個很有用的模式,稱為 Extend (擴展、繼承) 。

讓我們先準備好一些程式碼,並引入 underscore.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var person = {  
firstName: 'Default',
lastName: 'Default',
getFullName: function(){
return this.firstName + ' ' + this.lastName;
}
}

var john = {
firstName: 'John',
lastName: 'Doe'
}

// 僅方便理解原型使用,需要使用原型時不可以使用這種方式!!
john.__proto__ = person;

我們建立了一些物件,並且把 john 的原型設為 person ,這是先前文章內的一個範例,我們將從這個例子做延伸。

for(var prop in john){
console.log(prop + ': ' + john[prop]);
}

首先使用 for in ( MDN 介紹) 迴圈,遍歷 john 物件內所有成員並且印出,讓我們看看現在 john 物件內有什麼東西。

為什麼 john 物件內會有在原型上的 getFullName 方法 ?

因為 for in 迴圈也會到原型上取用屬性和方法,因此不侷限於物件本身。

也可以透過 if 結合 hasOwnProperty 進一步篩選結果

1
2
3
4
5
for(var prop in john){  
if (john.hasOwnProperty(prop)){
console.log(prop + ': ' + john[prop]);
}
}

這樣就可以單純取出在 john 物件上的屬性或是方法,如果不在自身物件上,就是在原型上、或者根本不在原型鏈上的任何地方。

這樣的概念可以讓我們做更多的事 - 補足原型繼承,接下來課程作者就使用 underscore 內的 extend 函式作為範例介紹, extend 可以將其他物件的屬性、方法新增給目標物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var person = {  
firstName: 'Default',
lastName: 'Default',
getFullName: function(){
return this.firstName + ' ' + this.lastName;
}
}

var john = {
firstName: 'John',
lastName: 'Doe'
}

// 僅方便理解原型使用,需要使用原型時不可以使用這種方式!!
john.__proto__ = person;

for(var prop in john){
if (john.hasOwnProperty(prop)){
console.log(prop + ': ' + john[prop]);
}
}
// 新增以下物件
var jane = {
address: '111 Main St.',
getFormalFullName: function() {
return this.firstName + ' ' + this.lastName;
}
}

var jim = {
getFirstName: function() {
return this.firstName;
}
}

這邊新增的 janejim 物件,並沒有將它們放入原型鏈,但 john 又想要能有它們的屬性或方法,於是可以這麼做:

1
2
_.extend(john, jane, jim);  
console.log(john);

如此一來 john 就有了 janejim 內的方法,而且原型 person 也還在。打開 underscore.js 觀察 extend 函式是如何處理這一塊的。


可以看到 createAssigner 是利用閉包的特性撰寫而成,而使用 extend 後,物件 john 被新增了另外兩個物件 janejim 的屬性與方法。

透過 extend 函式現在 john 可以使用原本不屬於自身的屬性以及方法了。

1
console.log(john.getFirstName()); // John

0%