<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 第三章:原生類型 在第一和第二章中,我們幾次提到了各種內建類型,通常稱為“原生類型”,比如?`String`?和?`Number`。現在讓我們來仔細檢視它們。 這是最常用的原生類型的一覽: * `String()` * `Number()` * `Boolean()` * `Array()` * `Object()` * `Function()` * `RegExp()` * `Date()` * `Error()` * `Symbol()`?—— 在 ES6 中被加入的! 如你所見,這些原生類型實際上是內建函數。 如果你擁有像 Java 語言那樣的背景,JavaScript 的?`String()`?看起來像是你曾經用來創建字符串值的?`String(..)`?構造器。所以,你很快就會觀察到你可以做這樣的事情: ```source-js var s = new String( "Hello World!" ); console.log( s.toString() ); // "Hello World!" ``` 這些原生類型的每一種確實可以被用作一個原生類型的構造器。但是被構建的東西可能與你想象的不同: ```source-js var a = new String( "abc" ); typeof a; // "object" ... 不是 "String" a instanceof String; // true Object.prototype.toString.call( a ); // "[object String]" ``` 創建值的構造器形式(`new String("abc")`)的結果是一個基本類型值(`"abc"`)的包裝器對象。 重要的是,`typeof`?顯示這些對象不是它們自己的特殊?*類型*,而是?`object`?類型的子類型。 這個包裝器對象可以被進一步觀察,像這樣: ```source-js console.log( a ); ``` 這個語句的輸出會根據你使用的瀏覽器變化,因為對于開發者的查看,開發者控制臺可以自由選擇它認為合適的方式來序列化對象。 注意:?在寫作本書時,最新版的 Chrome 打印出這樣的東西:`String {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}`。但是老版本的 Chrome 曾經只打印出這些:`String {0: "a", 1: "b", 2: "c"}`。當前最新版的 Firefox 打印?`String ["a","b","c"]`,但它曾經以斜體字打印?`"abc"`,點擊它可以打開對象查看器。當然,這些結果是總頻繁變更的,而且你的體驗也許不同。 重點是,`new String("abc")`?為?`"abc"`?創建了一個字符串包裝器對象,而不僅是基本類型值?`"abc"`?本身。 ## 內部?`[[Class]]` `typeof`?的結果為?`"object"`?的值(比如數組)被額外地打上了一個內部的標簽屬性?`[[Class]]`(請把它考慮為一個內部的分類方法,而非與傳統的面向對象編碼的類有關)。這個屬性不能直接地被訪問,但通常可以間接地通過在這個值上借用默認的?`Object.prototype.toString(..)`?方法調用來展示。舉例來說: ```source-js Object.prototype.toString.call( [1,2,3] ); // "[object Array]" Object.prototype.toString.call( /regex-literal/i ); // "[object RegExp]" ``` 所以,對于這個例子中的數組來說,內部的?`[[Class]]`?值是?`"Array"`,而對于正則表達式,它是?`"RegExp"`。在大多數情況下,這個內部的?`[[Class]]`?值對應于關聯這個值的內建的原生類型構造器(見下面的討論),但事實卻不總是這樣。 基本類型呢?首先,`null`?和?`undefined`: ```source-js Object.prototype.toString.call( null ); // "[object Null]" Object.prototype.toString.call( undefined ); // "[object Undefined]" ``` 你會注意到,不存在?`Null()`?和?`Undefined()`?原生類型構造器,但不管怎樣?`"Null"`?和?`"Undefined"`?是被暴露出來的內部?`[[Class]]`?值。 但是對于像?`string`、`number`、和?`boolean`?這樣的簡單基本類型,實際上會啟動另一種行為,通常稱為“封箱(boxing)”(見下一節“封箱包裝器”): ```source-js Object.prototype.toString.call( "abc" ); // "[object String]" Object.prototype.toString.call( 42 ); // "[object Number]" Object.prototype.toString.call( true ); // "[object Boolean]" ``` 在這個代碼段中,每一個簡單基本類型都自動地被它們分別對應的對象包裝器封箱,這就是為什么?`"String"`、`"Number"`、和?`"Boolean"`?分別被顯示為內部?`[[Class]]`?值。 注意:?從 ES5 發展到 ES6 的過程中,這里展示的?`toString()`?和?`[[Class]]`?的行為發生了一點兒改變,但我們會在本系列的?*ES6 與未來*?一書中講解它們的細節。 ## 封箱包裝器 這些對象包裝器服務于一個非常重要的目的。基本類型值沒有屬性或方法,所以為了訪問?`.length`?或?`.toString()`?你需要這個值的對象包裝器。值得慶幸的是,JS 將會自動地?*封箱*(也就是包裝)基本類型值來滿足這樣的訪問。 ```source-js var a = "abc"; a.length; // 3 a.toUpperCase(); // "ABC" ``` 那么,如果你想以通常的方式訪問這些字符串值上的屬性/方法,比如一個?`for`?循環的?`i < a.length`?條件,這么做看起來很有道理:一開始就得到一個這個值的對象形式,于是 JS 引擎就不需要隱含地為你創建一個。 但事實證明這是一個壞主意。瀏覽器們長久以來就對?`.length`?這樣的常見情況進行性能優化,這意味著如果你試著直接使用對象形式(它們沒有被優化過)進行“提前優化”,那么實際上你的程序將會?*變慢*。 一般來說,基本上沒有理由直接使用對象形式。讓封箱在需要的地方隱含地發生會更好。換句話說,永遠也不要做?`new String("abc")`、`new Number(42)`?這樣的事情 —— 應當總是偏向于使用基本類型字面量?`"abc"`?和?`42`。 ### 對象包裝器的坑 如果你?*確實*?選擇要直接使用對象包裝器,那么有幾個坑你應該注意。 舉個例子,考慮?`Boolean`?包裝的值: ```source-js var a = new Boolean( false ); if (!a) { console.log( "Oops" ); // 永遠不會運行 } ``` 這里的問題是,雖然你為值?`false`?創建了一個對象包裝器,但是對象本身是“truthy”(見第四章),所以使用對象的效果是與使用底層的值?`false`?本身相反的,這與通常的期望十分不同。 如果你想手動封箱一個基本類型值,你可以使用?`Object(..)`?函數(沒有?`new`?關鍵字): ```source-js var a = "abc"; var b = new String( a ); var c = Object( a ); typeof a; // "string" typeof b; // "object" typeof c; // "object" b instanceof String; // true c instanceof String; // true Object.prototype.toString.call( b ); // "[object String]" Object.prototype.toString.call( c ); // "[object String]" ``` 再說一遍,通常不鼓勵直接使用封箱的包裝器對象(比如上面的?`b`?和?`c`),但你可能會遇到一些它們有用的罕見情況。 ## 開箱 如果你有一個包裝器對象,而你想要取出底層的基本類型值,你可以使用?`valueOf()`?方法: ```source-js var a = new String( "abc" ); var b = new Number( 42 ); var c = new Boolean( true ); a.valueOf(); // "abc" b.valueOf(); // 42 c.valueOf(); // true ``` 當以一種查詢基本類型值的方式使用對象包裝器時,開箱也會隱含地發生。這個處理的過程(強制轉換)將會在第四章中更詳細地講解,但簡單地說: ```source-js var a = new String( "abc" ); var b = a + ""; // `b` 擁有開箱后的基本類型值"abc" typeof a; // "object" typeof b; // "string" ``` ## 原生類型作為構造器 對于?`array`、`object`、`function`?和正則表達式值來說,使用字面形式來創建它們的值幾乎總是更好的選擇,而且字面形式與構造器形式所創建的值是同一種對象(也就是,沒有非包裝的值)。 正如我們剛剛在上面看到的其他原生類型,除非你真的知道你需要這些構造器形式,一般來說應當避免使用它們,這主要是因為它們會帶來一些你可能不會想要對付的異常和陷阱。 ### [](https://github.com/getify/You-Dont-Know-JS/blob/1ed-zh-CN/types%20%26%20grammar/ch3.md#array)`Array(..)` ```source-js var a = new Array( 1, 2, 3 ); a; // [1, 2, 3] var b = [1, 2, 3]; b; // [1, 2, 3] ``` 注意:?`Array(..)`?構造器不要求在它前面使用?`new`?關鍵字。如果你省略它,它也會像你已經使用了一樣動作。所以?`Array(1,2,3)`?和?`new Array(1,2,3)`?的結果是一樣的。 `Array`?構造器有一種特殊形式,如果它僅僅被傳入一個?`number`?參數,與將這個值作為數組的?*內容*?不同,它會被認為是用來“預定數組大小”(嗯,某種意義上)用的長度。 這是個可怕的主意。首先,你會意外地用錯這種形式,因為它很容易忘記。 但更重要的是,其實沒有預定數組大小這樣的東西。你所創建的是一個空數組,并將這個數組的?`length`?屬性設置為那個指定的數字值。 一個數組在它的值槽上沒有明確的值,但是有一個?`length`?屬性意味著這些值槽是存在的,在 JS 中這是一個詭異的數據結構,它帶有一些非常奇怪且令人困惑的行為。可以創建這樣的值的能力,完全源自于老舊的、已經廢棄的、僅具有歷史意義的功能(比如`arguments`?這樣的“類數組對象”)。 注意:?帶有至少一個“空值槽”的數組經常被稱為“稀散數組”。 這是另外一個例子,展示瀏覽器的開發者控制臺在如何表示這樣的對象上有所不同,它產生了更多的困惑。 舉例來說: ```source-js var a = new Array( 3 ); a.length; // 3 a; ``` 在 Chrome 中?`a`?的序列化表達是(在本書寫作時):`[ undefined x 3 ]`。這真的很不幸。?它暗示著在這個數組的值槽中有三個?`undefined`?值,而事實上這樣的值槽是不存在的(所謂的“空值槽(empty slots)” —— 也是一個爛名字!)。 要觀察這種不同,試試這段代碼: ```source-js var a = new Array( 3 ); var b = [ undefined, undefined, undefined ]; var c = []; c.length = 3; a; b; c; ``` 注意:?正如你在這個例子中看到的?`c`,數組中的空值槽可以在數組的創建之后發生。將數組的?`length`?改變為超過它實際定義的槽值的數目,你就隱含地引入了空值槽。事實上,你甚至可以在上面的代碼段中調用?`delete b[1]`,而這么做將會在?`b`的中間引入一個空值槽。 對于?`b`(在當前的 Chrome 中),你會發現它的序列化表現為?`[ undefined, undefined, undefined ]`,與之相對的是?`a`?和?`c`?的?`[ undefined x 3 ]`。糊涂了吧?是的,大家都糊涂了。 更糟糕的是,在寫作本書時,Firefox 對?`a`?和?`c`?報告?`[ , , , ]`。你發現為什么這使人犯糊涂了嗎?仔細看。三個逗號表示有四個值槽,不是我們期望的三個值槽。 什么!??Firefox 在它們的序列化表達的末尾放了一個額外的?`,`,因為在 ES5 中,列表(數組值,屬性列表等等)末尾的逗號是允許的(被砍掉并忽略)。所以如果你在你的程序或控制臺中敲入?`[ , , , ]`?值,你實際上得到的是一個底層為?`[ , , ]`的值(也就是,一個帶有三個空值槽的數組)。這種選擇,雖然在閱讀開發者控制臺時使人困惑,但是因為它使拷貝粘貼的時候準確,所以被留了下來。 如果你現在在搖頭或翻白眼兒,你并不孤單!(聳肩) 不幸的是,事情越來越糟。比在控制臺的輸出產生的困惑更糟的是,上面代碼段中的?`a`?和?`b`?實際上在有些情況下相同,但在另一些情況下不同: ```source-js a.join( "-" ); // "--" b.join( "-" ); // "--" a.map(function(v,i){ return i; }); // [ undefined x 3 ] b.map(function(v,i){ return i; }); // [ 0, 1, 2 ] ``` 呃。 `a.map(..)`?調用會?*失敗*?是因為值槽根本就不實際存在,所以?`map(..)`?沒有東西可以迭代。`join(..)`?的工作方式不同,基本上我們可以認為它是像這樣被實現的: ```source-js function fakeJoin(arr,connector) { var str = ""; for (var i = 0; i < arr.length; i++) { if (i > 0) { str += connector; } if (arr[i] !== undefined) { str += arr[i]; } } return str; } var a = new Array( 3 ); fakeJoin( a, "-" ); // "--" ``` 如你所見,`join(..)`?好用僅僅是因為它?*認為*?值槽存在,并循環至?`length`?值。不管?`map(..)`?內部是在做什么,它(顯然)沒有做出這樣的假設,所以源自于奇怪的“空值槽”數組的結果出人意料,而且好像是失敗了。 那么,如果你想要?*確實*?創建一個實際的?`undefined`?值的數組(不只是“空值槽”),你如何才能做到呢(除了手動以外)? ```source-js var a = Array.apply( null, { length: 3 } ); a; // [ undefined, undefined, undefined ] ``` 糊涂了吧?是的。這里是它大概的工作方式。 `apply(..)`?是一個對所有函數可用的工具方法,它以一種特殊方式調用這個使用它的函數。 第一個參數是一個?`this`?對象綁定(在本系列的?*this 與對象原型*?中有詳細講解),在這里我們不關心它,所以我們將它設置為?`null`。第二個參數應該是一個數組(或?*像*?數組的東西 —— 也就是“類數組對象”)。這個“數組”的內容作為這個函數的參數“擴散”開來。 所以,`Array.apply(..)`?在調用?`Array(..)`?函數,并將一個值(`{ length: 3 }`?對象值)作為它的參數值擴散開。 在?`apply(..)`?內部,我們可以預見這里有另一個?`for`?循環(有些像上面的?`join(..)`),它從?`0`?開始上升但不包含至?`length`(這個例子中是?`3`)。 對于每一個索引,它從對象中取得相應的鍵。所以如果這個數組對象參數在?`apply(..)`?內部被命名為?`arr`,那么這種屬性訪問實質上是`arr[0]`、`arr[1]`?和?`arr[2]`。當然,沒有一個屬性是在?`{ length: 3 }`?對象值上存在的,所以這三個屬性訪問都將返回值?`undefined`。 換句話說,調用?`Array(..)`?的結局基本上是這樣:`Array(undefined,undefined,undefined)`,這就是我們如何得到一個填滿?`undefined`?值的數組的,而非僅僅是一些(瘋狂的)空值槽。 雖然對于創建一個填滿?`undefined`?值的數組來說,`Array.apply( null, { length: 3 } )`?是一個奇怪而且繁冗的方法,但是它要比使用砸自己的腳似的?`Array(3)`?空值槽要可靠和好得?太多了。 底線:你?在任何情況下,永遠不,也不應該有意地創建并使用詭異的空值槽數組。就別這么干。它們是怪胎。 ### `Object(..)`、`Function(..)`?和?`RegExp(..)` `Object(..)`/`Function(..)`/`RegExp(..)`?構造器一般來說也是可選的(因此除非是特別的目的,應當避免使用): ```source-js var c = new Object(); c.foo = "bar"; c; // { foo: "bar" } var d = { foo: "bar" }; d; // { foo: "bar" } var e = new Function( "a", "return a * 2;" ); var f = function(a) { return a * 2; }; function g(a) { return a * 2; } var h = new RegExp( "^a*b+", "g" ); var i = /^a*b+/g; ``` 幾乎沒有理由使用?`new Object()`?構造器形式,尤其因為它強迫你一個一個地添加屬性,而不是像對象的字面形式那樣一次添加許多。 `Function`?構造器僅在最最罕見的情況下有用,也就是你需要動態地定義一個函數的參數和/或它的函數體。不要將?`Function(..)`?僅僅作為另一種形式的?`eval(..)`。你幾乎永遠不會需要用這種方式動態定義一個函數。 用字面量形式(`/^a*b+/g`)定義正則表達式是被大力采用的,不僅因為語法簡單,而且還有性能的原因 —— JS 引擎會在代碼執行前預編譯并緩存它們。和我們迄今看到的其他構造器形式不同,`RegExp(..)`?有一些合理的用途:用來動態定義一個正則表達式的范例。 ```source-js var name = "Kyle"; var namePattern = new RegExp( "\\b(?:" + name + ")+\\b", "ig" ); var matches = someText.match( namePattern ); ``` 這樣的場景在 JS 程序中一次又一次地合法出現,所以你有需要使用?`new RegExp("pattern","flags")`?形式。 ### `Date(..)`?和?`Error(..)` `Date(..)`?和?`Error(..)`?原生類型構造器要比其他種類的原生類型有用得多,因為它們沒有字面量形式。 要創建一個日期對象值,你必須使用?`new Date()`。`Date(..)`?構造器接收可選參數值來指定要使用的日期/時間,但是如果省略的話,就會使用當前的日期/時間。 目前你構建一個日期對象的最常見的理由是要得到當前的時間戳(一個有符號整數,從1970年1月1日開始算起的毫秒數)。你可以在一個日期對象實例上調用?`getTime()`?得到它。 但是在 ES5 中,一個更簡單的方法是調用定義為?`Date.now()`?的靜態幫助函數。而且在前 ES5 中填補它很容易: ```source-js if (!Date.now) { Date.now = function(){ return (new Date()).getTime(); }; } ``` 注意:?如果你不帶?`new`?調用?`Date()`,你將會得到一個那個時刻的日期/時間的字符串表達。在語言規范中沒有規定這個表達的確切形式,雖然各個瀏覽器趨向于贊同使用這樣的東西:`"Fri Jul 18 2014 00:31:02 GMT-0500 (CDT)"`。 `Error(..)`?構造器(很像上面的?`Array()`)在有?`new`?與沒有?`new`?時的行為是相同的。 你想要創建 error 對象的主要原因是,它會將當前的執行棧上下文捕捉進對象中(在大多數 JS 引擎中,在創建后使用只讀的?`.stack`?屬性表示)。這個棧上下文包含函數調用棧和 error 對象被創建時的行號,這使調試這個錯誤更簡單。 典型地,你將與?`throw`?操作符一起使用這樣的 error 對象: ```source-js function foo(x) { if (!x) { throw new Error( "x wasn't provided" ); } // .. } ``` Error 對象實例一般擁有至少一個?`message`?屬性,有時還有其他屬性(你應當將它們作為只讀的),比如?`type`。然而,與其檢視上面提到的?`stack`?屬性,最好是在 error 對象上調用?`toString()`(明確地調用,或者是通過強制轉換隱含地調用 —— 見第四章)來得到一個格式友好的錯誤消息。 提示:?技術上講,除了一般的?`Error(..)`?原生類型以外,還有幾種特定錯誤的原生類型:`EvalError(..)`、`RangeError(..)`、`ReferenceError(..)`、`SyntaxError(..)`、`TypeError(..)`?和?`URIError(..)`。但是手動使用這些特定錯誤原生類型十分少見。如果你的程序確實遭受了一個真實的異常,它們是會自動地被使用的(比如引用一個未聲明的變量而得到一個?`ReferenceError`?錯誤)。 ### `Symbol(..)` 在 ES6 中,新增了一個基本值類型,稱為“Symbol”。Symbol 是一種特殊的“獨一無二”(不是嚴格保證的!)的值,可以作為對象上的屬性使用而幾乎不必擔心任何沖突。它們主要是為特殊的 ES6 結構的內建行為設計的,但你也可以定義你自己的 symbol。 Symbol 可以用做屬性名,但是你不能從你的程序中看到或訪問一個 symbol 的實際值,從開發者控制臺也不行。例如,如果你在開發者控制臺中對一個 Symbol 求值,將會顯示?`Symbol(Symbol.create)`?之類的東西。 在 ES6 中有幾種預定義的 Symbol,做為?`Symbol`?函數對象的靜態屬性訪問,比如?`Symbol.create`,`Symbol.iterator`?等等。要使用它們,可以這樣做: ```source-js obj[Symbol.iterator] = function(){ /*..*/ }; ``` 要定義你自己的 Symbol,使用?`Symbol(..)`?原生類型。`Symbol(..)`?原生類型“構造器”很獨特,因為它不允許你將?`new`?與它一起使用,這么做會拋出一個錯誤。 ```source-js var mysym = Symbol( "my own symbol" ); mysym; // Symbol(my own symbol) mysym.toString(); // "Symbol(my own symbol)" typeof mysym; // "symbol" var a = { }; a[mysym] = "foobar"; Object.getOwnPropertySymbols( a ); // [ Symbol(my own symbol) ] ``` 雖然 Symbol 實際上不是私有的(在對象上使用?`Object.getOwnPropertySymbols(..)`?反射,揭示了 Symbol 其實是相當公開的),但是它們的主要用途可能是私有屬性,或者類似的特殊屬性。對于大多數開發者,他們也許會在屬性名上加入?`_`?下劃線前綴,這在經常在慣例上表示:“這是一個私有的/特殊的/內部的屬性,別碰!” 注意:?`Symbol`?*不是*?`object`,它們是簡單的基本標量。 ### 原生類型原型 每一個內建的原生構造器都擁有它自己的?`.prototype`?對象 ——?`Array.prototype`,`String.prototype`?等等。 對于它們特定的對象子類型,這些對象含有獨特的行為。 例如,所有的字符串對象,和?`string`?基本值的擴展(通過封箱),都可以訪問在?`String.prototype`?對象上做為方法定義的默認行為。 注意:?做為文檔慣例,`String.prototype.XYZ`?會被縮寫為?`String#XYZ`,對于其它所有?`.prototype`?的屬性都是如此。 * `String#indexOf(..)`:在一個字符串中找出一個子串的位置 * `String#charAt(..)`:訪問一個字符串中某個位置的字符 * `String#substr(..)`、`String#substring(..)`?和?`String#slice(..)`:將字符串的一部分抽取為一個新字符串 * `String#toUpperCase()`?和?`String#toLowerCase()`:創建一個轉換為大寫或小寫的新字符串 * `String#trim()`:創建一個截去開頭或結尾空格的新字符串。 這些方法中沒有一個是在?*原地*?修改字符串的。修改(比如大小寫變換或去空格)會根據當前的值來創建一個新的值。 有賴于原型委托(見本系列的?*this 與對象原型*),任何字符串值都可以訪問這些方法: ```source-js var a = " abc "; a.indexOf( "c" ); // 3 a.toUpperCase(); // " ABC " a.trim(); // "abc" ``` 其他構造器的原型包含適用于它們類型的行為,比如?`Number#toFixed(..)`(將一個數字轉換為一個固定小數位的字符串)和?`Array#concat(..)`(混合數組)。所有這些函數都可以訪問?`apply(..)`、`call(..)`?和?`bind(..)`,因為?`Function.prototype`定義了它們。 但是,一些原生類型的原型不?*僅僅*?是單純的對象: ```source-js typeof Function.prototype; // "function" Function.prototype(); // 它是一個空函數! RegExp.prototype.toString(); // "/(?:)/" —— 空的正則表達式 "abc".match( RegExp.prototype ); // [""] ``` 一個特別差勁兒的主意是,你甚至可以修改這些原生類型的原型(不僅僅是你可能熟悉的添加屬性): ```source-js Array.isArray( Array.prototype ); // true Array.prototype.push( 1, 2, 3 ); // 3 Array.prototype; // [1,2,3] // 別這么留著它,要不就等著怪事發生吧! // 將`Array.prototype`重置為空 Array.prototype.length = 0; ``` 如你所見,`Function.prototype`?是一個函數,`RegExp.prototype`?是一個正則表達式,而?`Array.prototype`?是一個數組。有趣吧?酷吧? #### 原型作為默認值 `Function.prototype`?是一個空函數,`RegExp.prototype`?是一個“空”正則表達式(也就是不匹配任何東西),而?`Array.prototype`?是一個空數組,這使它們成了可以賦值給變量的,很好的“默認”值 —— 如果這些類型的變量還沒有值。 例如: ```source-js function isThisCool(vals,fn,rx) { vals = vals || Array.prototype; fn = fn || Function.prototype; rx = rx || RegExp.prototype; return rx.test( vals.map( fn ).join( "" ) ); } isThisCool(); // true isThisCool( ["a","b","c"], function(v){ return v.toUpperCase(); }, /D/ ); // false ``` 注意:?在 ES6 中,我們不再需要使用?`vals = vals || ..`?這樣的默認值語法技巧了(見第四章),因為在函數聲明中可以通過原生語法為參數設定默認值(見第五章)。 這個方式的一個微小的副作用是,`.prototype`?已經被創建了,而且是內建的,因此它僅被創建?*一次*。相比之下,使用?`[]`、`function(){}`?和?`/(?:)/`?這些值本身作為默認值,將會(很可能,要看引擎如何實現)在每次調用?`isThisCool(..)`?時重新創建這些值(而且稍可能要回收它們)。這可能會消耗內存/CPU。 另外,要非常小心不要對?后續要被修改的值?使用?`Array.prototype`?做為默認值。在這個例子中,`vals`?是只讀的,但如果你要在原地對?`vals`?進行修改,那你實際上修改的是?`Array.prototype`?本身,這將把你引到剛才提到的坑里! 注意:?雖然我們指出了這些原生類型的原型和一些用處,但是依賴它們的時候要小心,更要小心以任何形式修改它們。更多的討論見附錄A“原生原型”。 ## 復習 JavaScript 為基本類型提供了對象包裝器,被稱為原生類型(`String`、`Number`、`Boolean`?等等)。這些對象包裝器使這些值可以訪問每種對象子類型的恰當行為(`String#trim()`?和?`Array#concat(..)`)。 如果你有一個像?`"abc"`?這樣的簡單基本類型標量,而且你想要訪問它的?`length`?屬性或某些?`String.prototype`?方法,JS 會自動地“封箱”這個值(用它所對應種類的對象包裝器把它包起來),以滿足這樣的屬性/方法訪問。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看