JavaScript 的內建函數建構子

前言

我們在之前學到利用函數建構子來創造物件,然而在JavaScript中原本就有內建了幾個建構子,如Number()String()Date() ….,或許過去你已經有使用過,但可能並不是很了解底層運作的原理,經由上一章函數建構子 與「new」,我們已經很清楚了,現在你只要使用一樣的概念來讀這篇即可。

註:內建的函數建構子,如上一章所述也都是以第一個字母大寫呈現。


new Number()

1
new Number();

首先,先輸入:

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

new 這種用法我們可以理解到 Number() 是一個 function constructor,且是由JS引擎內建給我們的,也就是說由new Number()所建立的物件會含有Number.prototype內的method。另外很重要的是,因為它是透過 function construct 加以建立的,所以它雖然看起來是個數值,但它實際上還是個物件

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

num.__proto__ 包含了 它的建構子Number.prototype內的 method


new String()

1
new String();

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

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() 這樣的JS內建方法來查找字串,結果如下:


new Date()

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

1
2
3
4
var date = new Date("2/22/2019");
console.log(date);
console.log(typeof(date));
console.log(Date.prototype);

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


用物件的method來處理純值

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

1
2
//string
console.log("Dylan".length); // 5

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


對內建的function constructor增添自訂屬性和方法

函數建構子 與「new」 章節提到的.prtotype可以為函數建構子增添自訂的方法 method,現在我們同樣可以為內建的函數建構子增添方法。

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

對String()建構子增加方法

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

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

透過以上的程式碼,我們幫原本內建的 String() 這個 function constructor 新增了一個名為 isLengthGreaterThan 的方法,此後所有的字串都可以取用這個方法了,而很多libraryframwork都是這樣新增功能的,這就是原型繼承強大所在。

如此當我們輸入 "Dylan".isLengthGreaterThan(4) 就會回傳 true 的結果給我們。
在這裡,如同剛剛所提,我們輸入的 "Dylan" 是字串純值,但 JavaScript 知道我們希望對它進行和物件有關的方法,因此會自動用物件的方式將它做處理。

對Number()建構子增加方法

增加判斷是否為正整數的方法isPositive

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

console.log(3.isPositive());

結果會出現錯誤

會跳錯是因為 3.isPositive() 的地方,在上面提到JavaScript雖然能夠幫我們自動判斷,順利用屬於物件的方法來處理純值字串,但如果是純值數字的話會造成JavaScript直譯器在編譯上發生錯誤。

解決方法

  • ()包住
  • 用變數
1
2
3
4
5
6
7
Number.prototype.isPositive = function(){
return this > 0;
}

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

補充

Moment.js - 時間處理函式庫

順帶一提,如果有用JS處理時間的需求,作者不建議使用JS內建的時間方法,他推薦使用JS函示庫 Moment.js,這可以幫助開發者迴避一些因建構子產生的問題。


不要使用內建的函式建構子來建立純值

看完這篇及上一章函數建構子 與「new」必須建立的觀念:利用這些 function constructors 所建立的東西,看起來可能像數值、字串等等,但實際上,它們都還是物件。

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

1
2
3
4
5
6
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純值數字 primitive typenumFromConstructor物件 object

所以

  • 當你使用 == 時,JavaScript會進行強制型轉,回傳true
  • 當使用===時,並不會型轉,而是進一步比較兩者的類型,理所當然回傳false

這也是為什麼盡量不要使用和純值有關的 function constructors,而是使用真正的純值或是實體語法,因為如果交替使用的話,不小心你已經不知道到底哪些是純值哪些是透過function constructors所建立的物件,進而導致在進行比較時產生無法預期的問題。


資料來源