字面量和構造函數


字面量和構造函數

JavaScript中的字面量模式更加簡潔、有表現力,而且在定義對象時不容易出錯。本章將會討論字面量,包括對象、數組和正則表達式字面量,以及為什么字面量要比等價的內置構造函數(如Object()Array()等)要更好。本章還會介紹JSON格式,JSON是使用數組和對象字面量的形式定義的一種數據交換格式。本章還會討論自定義構造函數,包括如何強制使用new以確保構造函數正確執行。

為了方便使用字面量而不是構造函數,本章還會補充一些知識,比如內置包裝構造函數Number()String()Boolean(),以及如何將它們和原始值(數字、字符串和布爾值)比較。最后,快速介紹一下Error()構造函數的用法。

對象字面量

我們可以將JavaScript中的對象簡單地理解為名值對組成的散列表(hash table,也叫哈希表)。在其他編程語言中被稱作“關聯數組”。其中的值可以是原始值也可以是對象。不管是什么類型,它們都是“屬性”(property),屬性值同樣可以是函數,這時屬性就被稱為“方法”(method)。

JavaScript中自定義的對象(用戶定義的本地對象)任何時候都是可變的。內置本地對象的屬性也是可變的。你可以先創建一個空對象,然后在需要時給它添加功能。“對象字面量寫法(object literal notation)”是按需創建對象的一種理想方式。

看一下這個例子:

// 定義空對象
var dog = {};

// 添加一個屬性
dog.name = "Benji";

// 添加一個方法
dog.getName = function () {
    return dog.name;
};

在這個例子中,我們首先定義了一個空對象,然后添加了一個屬性和一個方法,在程序的生命周期內的任何時刻都可以:

  • 更改屬性和方法的值,比如:

    dog.getName = function () { // 重新定義方法,返回一個硬編碼的值 return "Fido"; };

  • 刪除屬性/方法

    delete dog.name;

  • 添加更多的屬性和方法

    dog.say = function () { return "Woof!"; }; dog.fleas = true;

每次都創建空對象並不是必須的,對象字面量模式可以直接在創建對象時添加功能,就像下面這個例子:

var dog = {
    name: "Benji",
    getName: function () {
        return this.name;
    }
};

在本書中多次提到“空對象”(“blank object”和“empty object”),這只是一種簡稱,在JavaScript中根本不存在真正的空對象,理解這一點至關重要。即使最簡單的{}對象也會包含從Object.prototype繼承來的屬性和方法。我們提到的“空(empty)對象”只是說這個對象沒有自有屬性(own properties),不考慮它是否有繼承來的屬性。

對象字面量語法

如果你從來沒有接觸過對象字面量的寫法,可能會感覺怪怪的。但越到后來你就越喜歡它。本質上講,對象字面量語法包括:

  • 將對象主體包含在一對花括號內({ 和 })。
  • 對象內的屬性或方法之間使用逗號分隔。最后一個名值對后也可以有逗號,但在IE下會報錯,所以盡量不要在最后一個屬性或方法后加逗號。
  • 屬性名和值之間使用冒號分隔。
  • 如果將對象賦值給一個變量,不要忘了在右括號}之后補上分號。

通過構造函數創建對象

JavaScript中沒有類的概念,這給JavaScript帶來了極大的靈活性,因為你不必提前知曉關於對象的任何信息,也不需要類的“藍圖”(譯注:指類的結構)。但JavaScript同樣具有構造函數,它的語法和Java或其他語言中基於類的對象創建非常類似。

你可以使用自定義的構造函數來創建對象實例,也可以使用內置構造函數來創建,比如Object()Date()String()等等。

下面這個例子展示了用兩種等價的方法分別創建兩個獨立的實例對象:

// 一種方法,使用字面量
var car = {goes: "far"};

// 另一種方法,使用內置構造函數
// 注意:這是一種反模式
var car = new Object();
car.goes = "far";

從這個例子中可以看到,字面量寫法的一個明顯優勢是,它的代碼更少。“創建對象的最佳模式是使用字面量”還有一個原因,它可以強調對象就是一個簡單的可變的散列表,而不必一定派生自某個類。

另外一個使用字面量而不是Object()構造函數創建實例對象的原因是,對象字面量不需要“作用域解析”(scope resolution)。因為存在你已經創建了一個同名的構造函數Object()的可能,當你調用Object()的時候,解析器需要順着作用域鏈從當前作用域開始查找,直到找到全局Object()構造函數為止。

Object()構造函數的參數

譯注:這小節的標題是Object Constructor Catch,恕譯者水平有限,實在不知如何翻譯,故自作主張修改了本節標題。

創建實例對象時能用對象字面量就不要使用new Object()構造函數,但有時你可能是在別人寫的代碼基礎上工作,這時就需要了解構造函數的一個“特性”(也是不使用它的另一個原因),就是Object()構造函數可以接收參數,通過這個參數可以把對象實例的創建過程委托給另一個內置構造函數,並返回另外一個對象實例,而這往往不是你想要的。

下面的示例代碼中展示了給new Object()傳入不同的參數(數字、字符串和布爾值),最終得到的對象是由不同的構造函數生成的:

// 注意:這是反模式

// 空對象
var o = new Object();
console.log(o.constructor === Object); // true

// 數值對象
var o = new Object(1);
console.log(o.constructor === Number); // true
console.log(o.toFixed(2)); // "1.00"

// 字符串對象
var o = new Object("I am a string");
console.log(o.constructor === String); // true
// 普通對象沒有substring()方法,但字符串對象有
console.log(typeof o.substring); // "function"

// 布爾值對象
var o = new Object(true);
console.log(o.constructor === Boolean); // true

Object()構造函數的這種特性會導致一些意想不到的結果,特別是當參數不確定的時候。最后再次提醒不要使用new Object(),盡可能的使用對象字面量來創建實例對象。

自定義構造函數

除了對象字面量和內置構造函數之外,你也可以通過自定義的構造函數來創建對象實例,正如下面的代碼所示:

var adam = new Person("Adam");
adam.say(); // "I am Adam"

這種寫法非常像Java中用Person類創建了一個實例,兩者的語法非常接近,但實際上JavaScript中沒有類的概念,Person()是一個函數。

Person()構造函數是如何定義的呢?看下面的代碼:

var Person = function (name) {
    this.name = name;
    this.say = function () {
        return "I am " + this.name;
    };
};

當你通過new來調用這個構造函數時,函數體內將發生這些事情:

  • 創建一個空對象,將它的引用賦給this,並繼承函數的原型。
  • 通過this將屬性和方法添加至這個對象。
  • 最后返回this指向的新對象(如果沒有手動返回其他的對象)。

用代碼表示這個過程如下:

var Person = function (name) {
    // 使用對象字面量創建新對象
    // var this = {};

    // 添加屬性和方法
    this.name = name;
    this.say = function () {
        return "I am " + this.name;
    };

    //return this;
};

上例中,為簡便起見,say()方法被添加至this中,結果就是不論何時調用new Person(),在內存中都會創建一個新函數(say()),顯然這是效率很低的,因為所有實例的say()方法是一模一樣的。最好的辦法是將方法添加至Person()的原型中。

Person.prototype.say = function () {
    return "I am " + this.name;
};

我們將會在下一章里詳細討論原型和繼承,現在只要記住將需要重用的成員放在原型里即可。

關於構造函數的內部工作機制也會在后續章節中有更細致的討論。這里我們只做概要的介紹。剛才提到,構造函數執行的時候,首先創建一個新對象,並將它的引用賦給this

// var this = {};

其實事實並不完全是這樣,因為“空”對象並不是真的空,這個對象繼承了Person的原型,看起來更像:

// var this = Object.create(Person.prototype);

在后續章節會進一步討論Object.create()

構造函數的返回值

當使用new調用的時候,構造函數總是會返回一個對象,默認情況下返回this所指向的對象。如果構造函數內沒有給this賦任何屬性,則返回一個“空”對象(除了繼承構造函數的原型之外,沒有自有屬性)。

盡管在構造函數中沒有return語句的情況下,也會隱式返回this。但事實上我們是可以返回任意指定的對象的,在下面的例子中就返回了新創建的that對象。

var Objectmaker = function () {

    // name屬性會被忽略,因為返回的是另一個對象
    this.name = "This is it";

    // 創建並返回一個新對象
    var that = {};
    that.name = "And that's that";
    return that;
};

// 測試
var o = new Objectmaker();
console.log(o.name); // "And that's that"

可以看到,構造函數中其實是可以返回任意對象的,只要你返回的東西是對象即可。如果返回值不是對象(字符串、數字或布爾值),程序不會報錯,但這個返回值被忽略,最終還是返回this所指的對象。

強制使用new的模式

我們知道,構造函數和普通的函數本質一樣,只是通過new調用而已。那么如果調用構造函數時忘記new會發生什么呢?漏掉new不會產生語法錯誤也不會有運行時錯誤,但可能會造成邏輯錯誤,導致執行結果不符合預期。這是因為如果不寫new的話,函數內的this會指向全局對象(在瀏覽器端this指向window)。

當構造函數內包含this.member之類的代碼,並直接調用這個函數(省略new),實際上會創建一個全局對象的屬性member,可以通過window.membermember訪問到。這不是我們想要的結果,因為我們要努力確保全局命名空間干凈。

// 構造函數
function Waffle() {
    this.tastes = "yummy";
}

// 新對象
var good_morning = new Waffle();
console.log(typeof good_morning); // "object"
console.log(good_morning.tastes); // "yummy"

// 反模式,漏掉new
var good_morning = Waffle();
console.log(typeof good_morning); // "undefined"
console.log(window.tastes); // "yummy"

ECMAScript5中修正了這種出乎意料的行為邏輯。在嚴格模式中,this不再指向全局對象。如果在不支持ES5的JavaScript環境中,也有一些方法可以確保有沒有new時構造函數的行為都保持一致。

命名規范

一種簡單解決上述問題的方法就是命名規范,前面的章節已經討論過,構造函數首字母大寫(MyConstructor()),普通函數和方法首字母小寫(myFunction)。

使用that

遵守命名規范有一定的作用,但規范畢竟不是強制,不能完全避免出現錯誤。這里給出了一種模式可以確保構造函數一定會按照構造函數的方式執行,那就是不要將所有成員添加到this上,而是將它們添加到that上,並返回that

function Waffle() {
    var that = {};
    that.tastes = "yummy";
    return that;
}

如果要創建更簡單一點的對象,甚至不需要局部變量that,直接返回一個對象字面量即可,就像這樣:

function Waffle() {
    return {
        tastes: "yummy"
    };
}

不管用什么方式調用它(使用new或直接調用),它都會返回一個實例對象:

var first = new Waffle(),
    second = Waffle();
console.log(first.tastes); // "yummy"
console.log(second.tastes); // "yummy"

這種模式的問題是會丟失原型,因此在Waffle()的原型上的成員不會被繼承到這些對象中。

需要注意的是,這里用的that只是一種命名規范,that並不是語言特性的一部分,它可以被替換為任何你喜歡的名字,比如selfme

調用自身的構造函數

為了解決上述模式的問題,能夠讓對象繼承原型上的屬性,我們使用下面的方法:在構造函數中首先檢查this是否是構造函數的實例,如果不是,則通過new再次調用自己:

function Waffle() {

    if (!(this instanceof Waffle)) {
        return new Waffle();
    }
    this.tastes = "yummy";

}
Waffle.prototype.wantAnother = true;

// 測試
var first = new Waffle(),
    second = Waffle();

console.log(first.tastes); // "yummy"
console.log(second.tastes); // "yummy"

console.log(first.wantAnother); // true
console.log(second.wantAnother); // true

還有一種比較通用的用來檢查實例的方法是使用arguments.callee,而不是直接將構造函數名寫死在代碼中:

if (!(this instanceof arguments.callee)) {
    return new arguments.callee();
}

這種模式利用了一個事實,即在任何函數內部都會創建一個arguments對象,它包含函數調用時傳入的參數。同時arguments包含一個callee屬性,指向正在被調用的函數。需要注意,ES5嚴格模式中已經禁止了arguments.callee的使用,因此最好對它的使用加以限制,並盡可能刪除現有代碼中已經用到的地方。

數組字面量

和JavaScript中大多數“東西”一樣,數組也是對象。可以通過內置構造函數Array()來創建數組,也可以通過字面量形式創建,就像對象字面量那樣。而且更推薦使用字面量創建數組。

這里的示例代碼給出了創建兩個具有相同元素的數組的兩種方法,使用Array()和使用字面量模式:

// 有三個元素的數組
// 注意:這是反模式
var a = new Array("itsy", "bitsy", "spider");

// 完全相同的數組
var a = ["itsy", "bitsy", "spider"];

console.log(typeof a); // "object",因為數組也是對象
console.log(a.constructor === Array); // true

數組字面量語法

數組字面量寫法非常簡單:整個數組使用方括號括起來,數組元素之間使用逗號分隔。數組元素可以是任意類型,包括數組和對象。

數組字面量語法簡單直觀而且優雅,畢竟數組只是從0開始索引的一些值的集合,完全沒必要引入構造器和new運算符(還要寫更多的代碼)。

Array()構造函數的“陷阱”

我們對new Array()敬而遠之還有一個原因,就是為了避免構造函數帶來的陷阱。

如果給Array()構造函數傳入一個數字,這個數字並不會成為數組的第一個元素,而是設置了數組的長度。也就是說,new Array(3)創建了一個長度為3的數組,而不是某個元素是3。如果你訪問數組的任意元素都會得到undefined,因為元素並不存在。下面的示例代碼展示了字面量和構造函數的區別:

// 含有1個元素的數組
var a = [3];
console.log(a.length); // 1
console.log(a[0]); // 3

// 含有3個元素的數組
var a = new Array(3);
console.log(a.length); // 3
console.log(typeof a[0]); // "undefined"

構造函數的行為可能有一點出乎意料,但當給new Array()傳入一個浮點數時情況就更糟糕了,這時會出錯(譯注:給new Array()傳入浮點數會報“范圍錯誤”RangError),因為數組長度不可能是浮點數。

// 使用數組字面量
var a = [3.14];
console.log(a[0]); // 3.14

var a = new Array(3.14); // RangeError: invalid array length
console.log(typeof a); // "undefined"

為了避免在運行時動態創建數組時出現這種錯誤,強烈推薦使用數組字面量來代替new Array()

有些人用Array()構造器來做一些有意思的事情,比如用來生成重復字符串。下面這行代碼返回的字符串包含255個空格(請讀者思考為什么不是256個空格)。var white = new Array(256).join(' ');

檢查是否數組

如果typeof的操作數是數組的話,將返回“object”。

console.log(typeof [1, 2]); // "object"

這個結果勉強說得過去,畢竟數組也是一種對象,但對我們來說這個結果卻沒什么用,實際上你往往是需要知道一個值是不是真正的數組。有時候你會見到一些檢查數組的方法:檢查length屬性、檢查數組方法比如slice()等等,但這些方法非常脆弱,非數組的對象也可以擁有這些同名的屬性。還有些人使用instanceof Array來判斷數組,但這種方法在某些版本的IE里的多個iframe的場景中會出問題(譯注:原因就是在不同iframe中創建的數組不會相互共享其prototype屬性)。

ECMAScript5定義了一個新的方法Array.isArray(),如果參數是數組的話就返回true。比如:

Array.isArray([]); // true

// 嘗試用一個類似數組的對象去測試
Array.isArray({
    length: 1,
    "0": 1,
    slice: function () {}
}); // false

如果你的開發環境不支持ECMAScript5,可以通過Object.prototype.toString()方法來代替。如調用toStringcall()方法並傳入數組上下文,將返回字符串“[object Array]”。如果傳入對象上下文,則返回字符串“[object Object]”。因此可以這樣做:

if (typeof Array.isArray === "undefined") {
    Array.isArray = function (arg) {
        return Object.prototype.toString.call(arg) === "[object Array]";
    };
}

JSON

我們剛剛討論了對象和數組字面量,你應該很熟悉了,現在我們來看一看JSON。JSON(JavaScript Object Notation)是一種輕量級的數據交換格式,可以很容易地用在多種語言中,尤其是在JavaScript中。

JSON格式及其簡單,它只是數組和對象字面量的混合寫法,看一個JSON字符串的例子:

{"name": "value", "some": [1, 2, 3]}

JSON和對象字面量在語法上的唯一區別是,合法的JSON屬性名均需要用引號包含。而在對象字面量中,只有屬性名是非法的標識符時才使用引號包含,比如,屬性名中包含空格{"first name": "Dave"}

在JSON字符串中,不能使用函數和正則表達式字面量。

使用JSON

在前面的章節中講到,出於安全考慮,不推薦使用eval()來粗暴地解析JSON字符串。最好是使用JSON.parse()方法,ES5中已經包含了這個方法,並且現代瀏覽器的JavaScript引擎中也已經內置支持JSON了。對於老舊的JavaScript引擎來說,你可以使用JSON.org所提供的JS文件(http://www.json.org/json2.js)來獲得JSON對象和方法。

// 輸入JSON字符串
var jstr = '{"mykey": "my value"}';

// 反模式
var data = eval('(' + jstr + ')');

// 更好的方式
var data = JSON.parse(jstr);

console.log(data.mykey); // "my value"

如果你已經在使用某個JavaScript庫了,很可能這個庫中已經提供了解析JSON的方法,就不必再額外引入JSON.org的庫了,比如,如果你已經使用了YUI3,你可以這樣:

// 輸入JSON字符串
var jstr = '{"mykey": "my value"}';

// 使用YUI來解析並將結果返回為一個對象
YUI().use('json-parse', function (Y) {
    var data = Y.JSON.parse(jstr);
    console.log(data.mykey); // "my value"
});

如果你使用的是jQuery,可以直接使用它提供的parseJSON()方法:

// 輸入JSON字符串
var jstr = '{"mykey": "my value"}';

var data = jQuery.parseJSON(jstr);
console.log(data.mykey); // "my value"

JSON.parse()方法相對應的是JSON.stringify()。它將對象或數組(或任何原始值)轉換為JSON字符串。

var dog = {
    name: "Fido",
    dob:new Date(),
    legs:[1,2,3,4]
};

var jsonstr = JSON.stringify(dog);

// jsonstr的值為
// {"name":"Fido","dob":"2010-04-11T22:36:22.436Z","legs":[1,2,3,4]}

正則表達式字面量

JavaScript中的正則表達式也是對象,可以通過兩種方式創建它們:

  • 使用new RegExp()構造函數
  • 使用正則表達式字面量

下面的示例代碼展示了創建用來匹配一個反斜杠(\)的正則表達式的兩種方法:

// 正則表達式字面量
var re = /\\/gm;

// 構造函數
var re = new RegExp("\\\\", "gm");

顯然正則表達式字面量寫法的代碼更短,而且不會讓你覺得在用像類一樣的構造函數的思想在寫正則表達式,因此更推薦使用字面量寫法。

另外,如果使用RegExp()構造函數寫法,還需要考慮對引號和反斜杠進行轉義,正如上段代碼所示的那樣,用了四個反斜杠來匹配一個反斜杠。這會增加正則表達式的長度,而且讓它變得難於理解和維護。正則表達式入門不是件容易的事,所以不要放棄任何一個簡化它們的機會,盡量使用字面量而不是通過構造函數來創建正則表達式。

正則表達式字面量語法

正則表達式字面量使用兩個斜杠包裹,主體部分不包括兩端的斜線。在第二個斜線之后可以指定模式匹配的修飾符,修飾符不需要用引號引起來,JavaScript中有三個修飾符:

  • g,全局匹配
  • m,多行匹配
  • i,忽略大小寫的匹配

修飾符可以自由組合,而且與順序無關:

var re = /pattern/gmi;

使用正則表達式字面量可以讓代碼更加簡潔高效,比如當調用String.prototype.replace()方法時,可以傳入正則表達式參數:

var no_letters = "abc123XYZ".replace(/[a-z]/gi, "");
console.log(no_letters); // 123

有一種不得不使用new RegExp()的場景,就是正則表達式是不確定,只有等到運行時才能確定下來的情況。

正則表達式字面量和構造函數還有另一個區別,就是字面量只在解析時創建一次正則表達式對象(譯注:多次解析同一個正則表達式,會產生相同的實例對象)。如果在循環體內反復使用相同的字面量創建對象,則會返回第一次創建的對象以及它的屬性(比如lastIndex)。下面這個例子展示了兩次返回相同的正則表達式的情形。

function getRE() {
    var re = /[a-z]/;
    re.foo = "bar";
    return re;
}

var reg = getRE(),
    re2 = getRE();

console.log(reg === re2); // true
reg.foo = "baz";
console.log(re2.foo); // "baz"

在ECMAScript5中這種情況有所改變,相同正則表達式字面量的每次計算都會創建新的實例對象,目前很多現代瀏覽器也對此做了糾正。

最后需要提一點,不帶new調用RegExp()(作為普通的函數)和帶new調用RegExp()是完全一樣的。

原始值的包裝對象

JavaScript中有五種原始類型:數字、字符串、布爾值、nullundefined。除了nullundefined之外,其他三種都有對應的“包裝對象”(primitive wrapper object)。可以通過內置構造函數Number()String()Boolean()來生成包裝對象。

為了說明數字原始值和數字對象之間的區別,看一下下面這個例子:

// 一個數字原始值
var n = 100;
console.log(typeof n); // "number"

// 一個Number對象
var nobj = new Number(100);
console.log(typeof nobj); // "object"

包裝對象帶有一些有用的屬性和方法。比如,數字對象就帶有toFixed()toExponential()之類的方法,字符串對象帶有substring()chatAt()toLowerCase()等方法以及length屬性。這些方法非常方便,和原始值相比,這是包裝對象的優勢,但其實原始值也可以調用這些方法,因為原始值會首先轉換為一個臨時對象,如果轉換成功,則調用包裝對象的方法。

// 像使用對象一樣使用一個字符串原始值
var s = "hello";
console.log(s.toUpperCase()); // "HELLO"

// 值本身也可以像對象一樣
"monkey".slice(3, 6); // "key"

// 數字也是一樣
(22 / 7).toPrecision(3); // "3.14"

因為原始值可以根據需要轉換成對象,這樣的話,也不必為了用包裝對象的方法而將原始值手動“包裝”成對象。比如,不必使用new String("hi"),直接使用"hi"即可。

// 避免這些:
var s = new String("my string");
var n = new Number(101);
var b = new Boolean(true);

// 更好更簡潔的辦法:
var s = "my string";
var n = 101;
var b = true;

不得不使用包裝對象的一個場景是,有時我們需要對值進行擴充並保持值的狀態。原始值畢竟不是對象,不能直接對其進行擴充。

// 字符串原始值
var greet = "Hello there";

// 為使用split方法,原始值被轉換為對象
greet.split(' ')[0]; // "Hello"

// 給原始值添加屬性並不會報錯
greet.smile = true;

// 但實際上卻沒有作用
typeof greet.smile; // "undefined"

在這段示例代碼中,greet只是臨時被轉換成了對象,以保證訪問其屬性、方法時不會出錯。而如果是另一種情況,greet通過new String()被定義為一個對象,那么擴充smile屬性的過程就會像我們預期的那樣。對字符串、數字或布爾值進行擴充的情況很少見,因此建議只在確實有必要的情況下使用包裝對象。

當省略new時,包裝對象的構造函數將傳給它的參數轉換為原始值:

typeof Number(1); // "number"
typeof Number("1"); // "number"
typeof Number(new Number()); // "number"
typeof String(1); // "string"
typeof Boolean(1); // "boolean"

錯誤處理對象

JavaScript中有很多內置的錯誤處理構造函數,比如Error()SyntaxError()TypeError()等等,它們通常和throw語句一起被使用。這些構造函數創建的錯誤對象包含這些屬性:

  • name

    name屬性是指產生這個對象的構造函數的名字,通常是“Error”,有時會有特定的名字比如“RangeError”

  • message

    創建這個對象時傳入構造函數的字符串

錯誤對象還有一些其他的屬性,比如產生錯誤的行號和文件名,但這些屬性是瀏覽器自行實現的,不同瀏覽器的實現也不一致,因此出於兼容性考慮,並不推薦使用這些屬性。

throw可以拋出任何對象,並不限於“錯誤對象”,因此你可以根據需要拋出自定義的對象。這些對象包含屬性“name”和“message”或其他你希望傳遞給異常處理邏輯的信息,異常處理邏輯由catch語句指定。你可以靈活運用拋出的錯誤對象,將程序從錯誤狀態恢復至正常狀態。

try {
    // 一些不好的事情發生了,拋出錯誤
    throw {
        name: "MyErrorType", // 自定義錯誤類型
        message: "oops",
        extra: "This was rather embarrassing",
        remedy: genericErrorHandler // 應該由誰處理
    };
} catch (e) {
    // 通知用戶
    alert(e.message); // "oops"

    // 優雅地處理錯誤
    e.remedy(); // 調用genericErrorHandler()
}

使用new調用和省略new調用錯誤構造函數是一模一樣的,他們都返回相同的錯誤對象。

小結

在本章里,我們討論了多種字面量模式,它們是使用構造函數寫法的替代方案,本章講述了這些內容:

  • 對象字面量寫法——一種簡潔優雅的定義對象的方法,通過花括號包裹,名值對之間用逗號分隔
  • 構造函數——內置構造函數(內置構造函數通常都有對應的字面量語法)和自定義構造函數
  • 一種強制函數以構造函數的模式運行行(不管用不用new調用構造函數,都始終返回new出來的實例)的技巧
  • 數組字面量寫法——通過方括號包裹,數組元素之間使用逗號分隔
  • JSON——一種輕量級的數據交換格式
  • 正則表達式字面量
  • 避免使用其他的內置構造函數:String()Number()Boolean()以及不同種類的Error()構造函數

通常情況下,除了Date()之外,其他的內置構造函數並不常用,下面的表格對這些構造函數以及它們的字面量語法做了整理。

內置構造函數(不推薦) 字面量語法和原始值(推薦)
var o = new Object(); var o = {};
var a = new Array(); var a = [];
var re = new RegExp("[a-z]","g"); var re = /[a-z]/g;
var s = new String(); var s = "";
var n = new Number(); var n = 0;
var b = new Boolean(); var b = false;
throw new Error("uh-oh"); throw { name: "Error",message: "uh-oh"};或者throw Error("uh-oh");


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM