[JS奇怪的世界]No.49 Reflection與Extend

前言

接下來將要講解另一個建立物件的函數很有趣很有效的東西,許多資料庫都會使用到,而這稱為 extend。

而通常我們都是用一個叫 Reflection 的東西來做到 extend。

Reflection

Reflection: 一個物件可以看到自己的東西,然後改變自己的屬性和方法。

讓我們試著從範例來學習。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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) {
console.log(prop + ':' + john[prop]);
}

這時候會看到一個很神奇的東西,getFullName(),這是因為 for in 會到外面取用屬性和方法的 不只在物件本身,還會到原型上找

那如果今天我們要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)) { // hasOwnProperty 是原型物件的東西,我們可以透過這種方式來確定是否物件有這個屬性。
console.log(prop + ':' + john[prop]);
}
}

這樣 getFullName() 就沒有出現了,因為 john 的物件屬性並沒有 getFullName()

而這個概念可以用來幫助我們做一些事情【補足原型繼承】,所以這邊試試看引入 underscore.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var newscript = document.createElement('script');
newscript.setAttribute('type','text/javascript');
newscript.setAttribute('src','https://underscorejs.org/underscore-min.js');
var john = {
firstname: 'John',
lastname: 'Doe',
}

var jane = {
address: '111 Main St.',
getFormFullName: function() {
return this.lastname + ', ' + this.firstname;
}
}

var jim = {
getFirstName: function() {
return firstname;
}
}

_.extend(john, jane, jim); // 這樣做將會把物件結合起來
console.log(john);

這時候我們可以看到 johnjane 的物件地址、jim 物件的 getFirstName 函數,還有 jane 物件的 getFormalFullName()

那這是怎麼做到的?首先先開啟 underscore.js,開啟之前記得千萬不要害怕開源的專案,這是一個非常好學習教材,我們試著找 extend 看看。

首先我們可以看到他建立了屬性或方法,而這個叫 createAssigner 的函數我們試著找看看。

我們可以發現 createAssigner 是一個函數,其中他接受了 keys 還有 defaults,然後回傳函數本身,我們可以看到這是一個閉包。

首先它會先取得傳入參數的長度

再來是包裝物件,他將物件包裝起來(避免參考問題) (((我猜)))

接下來就是當物件長度若小於兩筆,或是物件為空,就返回物件

接下來他會用 for 將所有物件跑一遍,因為陣列第一筆是 john ,所以起始會直接跳過 01 開始

所以因為經過 underscore.js 的處理, jim 就可以使用 getFormFullName()了。

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
var newscript = document.createElement('script');
newscript.setAttribute('type','text/javascript');
newscript.setAttribute('src','https://underscorejs.org/underscore-min.js');
var john = {
firstname: 'John',
lastname: 'Doe',
}

var jane = {
address: '111 Main St.',
getFormFullName: function() {
return this.lastname + ', ' + this.firstname;
}
}

var jim = {
getFirstName: function() {
return firstname;
}
}

_.extend(john, jane, jim); // 這樣做將會把物件結合起來
console.log(john);

john.getFormFullName();

圖源

JavaScript 全攻略:克服 JS 奇怪的部分