PJCHENder 未整理筆記

[JS] 談談 JavaScript 中內建的 function constructors 及應注意的地方

2018-05-01

[JS] 談談 JavaScript 中內建的 function constructors 及應注意的地方

@([Udemy] JavaScript: Understanding the Weird Parts)[javaScript, udemy]

img

在這篇筆記中讓我們來看一些JavaScript中內建的 function constructors

new Number()

1
new Number();

首先,讓我們輸入:

1
2
3
var num = new Number(3);
console.log(num);
console.log(Number.prototype);

new 這種用法我們可以理解到 Number 是 JavaScript 中內建的一個 function constructor,也就是說它有內建的 prototype 在內,而且很重要的是,因為它是透過 function construct 加以建立的,所以它雖然看起來是個數值,但它實際上還是個物件

因此,輸出之後我們會看到以下結果:

new String()

1
new String();

類似的方式我們也可以看看用 String() 這個 function construct 所建立的物件:

1
2
3
4
var str = new String('網頁設計前端資源');
console.log(str);
console.log(String.prototype);
console.log(str.indexOf("設計"));

在這裡, str 一樣是透過 function constructor 所建立的物件,而不是真正的字串!同時 str 繼承String.prototype 裡面的內容,所以我們可以直接使用 str.indexOf 這樣的用法來查找字串,結果如下:

new Date( )

即使是 Date(),它輸出的結果雖然看起來很像是一個字串,但它實際上還是物件,我們可以這樣做:

1
2
3
4
var date = new Date("6/28/2016");
console.log(date);
console.log(typeof(date));
console.log(Date.prototype);

如此,我們會發現它依然是個物件,而這個物件繼承了來自 Date.prototype 的屬性和方法:

很重要的一點是:利用這些 function constructors 所建立的東西,看起來可能像數值、字串等等,但實際上,它們都還是物件

有些時候,JavaScript 會知道你想要針對原生值(primitive type)去做一些物件才能處理的事,因此它會幫你將所輸入的原生值進行物件般的處理。

例如,我們輸入:

1
console.log("PJCHENder".length);    //  9

這時候,一般的原生值是沒有屬性(property)和方法(method)的,我們之所以這樣輸入一樣可以得到 9 的結果,是因為 JavaScript 自動的幫我們把這個原生值進行了物件的處理

針對內建的 function constructor 去增添屬性和方法

在清楚瞭解了原型(prototype)、函式建構式(function constructor)和繼承(inheritance)的概念後,現在我們也可以針對內建的 function construct 去增添裡面的屬性和方法,像是這樣:

1
2
3
4
5
String.prototype.isLengthGreaterThan = function(limit){
return this.length > limit;
}

console.log("PJCHENder".isLengthGreaterThan(4)); // True

透過這樣的方法,我們幫原本內建的 String 這個 function constructor 新增了一個名為 isLengthGreaterThan 的方法,如此當我們輸入 "PJCHENder".isLengthGreaterThan(4) 就會回傳 true 的結果給我們。

在這裡,一樣可以注意到,原本我們輸入的 "PJCHENder" 是原生的字串值,但 JavaScript 知道我們希望對它進行和物件有關的方法,因此會自動用物件的方式將它做處理,也因此才會回傳 true 的結果。

:exclamation: 透過這種方式來增添屬性或方法時,要留意的是,不要不小心把原本內建的屬性或方法給無意間覆蓋掉了。

那麼,如果我們想要用同樣的方法,來針對 Number 這個內建函式來操作的話呢?

1
2
3
4
5
Number.prototype.isPositive = function(){
return this > 0;
}

console.log(3.isPositive());

結果會出現錯誤:

之所以會有這個錯誤,主要是因為在上面 console.log(3.isPositive()); 的地方,在先前 JavaScript 雖然能夠幫我們順利將原生值的字串轉成物件來使用內部的方法,但直接使用數值的話會造成 JavaScript 引擎在解析時的錯誤。要解決這個問題只需把 3 先存成一個變數,或在用 () 包住 3 之後再執行就可以了:

1
2
3
4
5
6
7
Number.prototype.isPositive = function(){
return this > 0;
}

var d = 3;
console.log(d.isPositive()); // true
console.log((3).isPositive()); // true

如此就能跑出預期的結果了!!

關於 prototype, function constructorinheritance 如果還有觀念不清楚的地方,建議先閱讀先前的文章,以幫助了解這篇的內容。

相關文章:

但最好不要使用內建的函式建構式來產生原生值

如果可以的話,最好不要使用和原生值有關的內建 function constructors。

在上半篇的筆記當中,我們雖然示範了如果針對和原生值有關的內建函式去增添其 prototype 的方法,但其實應該盡可能不要使用和原生值有關的這些內建函式建構式,為什麼呢?

讓我們看一下這段程式碼:

1
2
3
4
5
var numPrimitive = 5 ;
var numFromConstructor = new Number(5);
console.log(typeof numPrimitive + "; " + typeof numFromConstructor); // number; object
console.log(numPrimitive == numFromConstructor); // true
console.log(numPrimitive === numFromConstructor); // false

你會發現, numPrimitivenumFromConstructor 雖然看起來很像都是一樣的 5,但如同上面所提的,numPrimitive 是原生的數值,而 numFromConstructor 是物件,這也就是為什麼如果使用 numPrimitive == numFromConstructor 時會回傳 true ,但如果是使用 numPrimitive === numFromConstructor 時則會回傳 false

因為使用 == 時,它會把前後兩個東西強制轉換(coerce)成可比較的型式,但如果是 === 則不會強制轉換,而是會進一步比較兩個的類型(type),當兩者類型不同的時候,就會回傳 false

這也就是為什麼如果可以的話,建議不要使用和原生值有關的 function constructors,因為如果交替使用的話,可能會讓自己最後搞不清楚現在這個值到底是原生值,還是透過 function constructor 所建立出來的物件!

可以的話盡量不要使用內建的函式建構式來產生原生值,因為透過函式建構式產生出來的「原生值」其實是「物件」,這樣可能會導致後來型別判斷上的困難。

程式範例

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*--------------------*/

var a = new Number(3);
console.log(a);
console.log(Number.prototype);

/*--------------------*/

var b = new String("網頁設計前端資源");
console.log(b);
console.log(String.prototype);
console.log(b.indexOf("設計"));

/*--------------------*/

console.log("PJCHENder".length); // 9

/*--------------------*/

var c = new Date("6/28/2016");
console.log(c);
console.log(typeof c);
console.log(Date.prototype);

/*--------------------*/

String.prototype.isLengthGreaterThan = function (limit) {
return this.length > limit;
}

console.log("PJCHENder".isLengthGreaterThan(4)); // True

/*--------------------*/

Number.prototype.isPositive = function () {
return this > 0;
}

var d = 3;
console.log(d.isPositive()); // true

/*--------------------*/

var e = 5;
var f = new Number(5);
console.log(typeof e + "; " + typeof f);
console.log(e == f); // true
console.log(e === f); // false

→回到此系列文章目錄

資料來源

掃描二維條碼,分享此文章