<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 第一章:類型 大多數開發者會說,動態語言(就像 JS)沒有?*類型*。讓我們看看 ES5.1 語言規范([http://www.ecma-international.org/ecma-262/5.1/)在這個問題上是怎么說的:](http://www.ecma-international.org/ecma-262/5.1/%EF%BC%89%E5%9C%A8%E8%BF%99%E4%B8%AA%E9%97%AE%E9%A2%98%E4%B8%8A%E6%98%AF%E6%80%8E%E4%B9%88%E8%AF%B4%E7%9A%84%EF%BC%9A) > 在本語言規范中的算法所操作的每一個值都有一種關聯的類型。可能的值的類型就是那些在本條款中定義的類型。類型還進一步被分為 ECMAScript 語言類型和語言規范類型 > > 一個 ECMAScript 語言類型對應于 ECMAScript 程序員使用 ECMAScript 語言直接操作的值。ECMAScript 語言類型有 Undefined,Null,Boolean,String,Number,和 Object。 現在,如果你是一個強類型(靜態類型的)語言的愛好者,你可能會反對“類型”一詞的用法。在那些語言中,“類型”的含義要比它在 JS 這里的含義豐富得?*多*。 有些人說 JS 不應該聲稱擁有“類型”,它們應被稱為“標簽”或者“子類型”。 去他的!我們將使用這個粗糙的定義(看起來和語言規范的定義相同,只是改變了措辭):一個?*類型*?是一組固有的,內建的性質,對于引擎?和開發者?來說,它獨一無二地標識了一個特定的值的行為,并將它與其他值區分開。 換句話說,如果引擎和開發者看待值?`42`(數字)與看待值?`"42"`(字符串)的方式不同,那么這兩個值就擁有不同的?*類型*?-- 分別是?`number`?和?`string`。當你使用?`42`?時,你就在?*試圖*?做一些數字的事情,比如計算。但當你使用?`"42"`?時,你就在?*試圖*?做一些字符串的事情,比如輸出到頁面上,等等。這兩個值有著不同的類型。 這絕不是一個完美的定義。但是對于這里的討論足夠好了。而且它與 JS 描述它的方式并不矛盾。 # 類型的重要意義 拋開學術上關于定義的分歧,為什么 JavaScript 有或者沒有?*類型*?那么重要? 對每一種?*類型*?和它的固有行為有一個正確的理解,對于理解如何正確和準確地轉換兩個不同類型的值來說是絕對必要的(參見第四章,強制轉換)。幾乎每一個被編寫過的 JS 程序都需要以某種形式處理類型的強制轉換,所以,你能負責任、有信心地這么做是很重要的。 如果你有一個?`number`?值?`42`,但你想像一個?`string`?那樣對待它,比如從位置?`1`?中將?`"2"`?作為一個字符抽取出來,那么顯然你需要首先將值從?`number`(強制)轉換成一個?`string`。 這看起來十分簡單。 但是這樣的強制轉換可能以許多不同的方式發生。其中有些方式是明確的,很容易推理的,和可靠的。但是如果你不小心,強制轉換就可能以非常奇怪的,令人吃驚的方式發生。 對強制轉換的困惑可能是 JavaScript 開發者所經歷的最深刻的挫敗感之一。它曾經總是因為如此?*危險*?而為人所詬病,被認為是一個語言設計上的缺陷而應當被回避。 帶著對 JavaScript 類型的全面理解,我們將要闡明為什么強制轉換的?*壞名聲*?是言過其實的,而且是有些冤枉的 -- 以此來反轉你的視角,來看清強制轉換的力量和用處。但首先,我們必須更好地把握值與類型。 ## 內建類型 JavaScript 定義了七種內建類型: * `null` * `undefined` * `boolean` * `number` * `string` * `object` * `symbol`?-- 在 ES6 中被加入的! 注意:?除了?`object`?所有這些類型都被稱為“基本類型(primitives)”。 `typeof`?操作符可以檢測給定值的類型,而且總是返回七種字符串值中的一種 -- 令人吃驚的是,對于我們剛剛列出的七中內建類型,它沒有一個恰好的一對一匹配。 ```source-js typeof undefined === "undefined"; // true typeof true === "boolean"; // true typeof 42 === "number"; // true typeof "42" === "string"; // true typeof { life: 42 } === "object"; // true // 在 ES6 中被加入的! typeof Symbol() === "symbol"; // true ``` 如上所示,這六種列出來的類型擁有相應類型的值,并返回一個與類型名稱相同的字符串值。`Symbol`?是 ES6 的新數據類型,我們將在第三章中討論它。 正如你可能已經注意到的,我在上面的列表中剔除了?`null`。它是?*特殊的*?-- 特殊在它與?`typeof`?操作符組合時是有 bug 的。 ```source-js typeof null === "object"; // true ``` 要是它返回?`"null"`?就好了(而且是正確的!),但是這個原有的 bug 已經存在了近二十年,而且好像永遠也不會被修復了,因為有太多已經存在的 web 的內容依存著這個 bug 的行為,“修復”這個 bug 將會?*制造*?更多的“bug”并毀掉許多 web 軟件。 如果你想要使用?`null`?類型來測試?`null`?值,你需要一個復合條件: ```source-js var a = null; (!a && typeof a === "object"); // true ``` `null`?是唯一一個“falsy”(也叫類 false;見第四章),但是在?`typeof`?檢查中返回?`"object"`?的基本類型。 那么?`typeof`?可以返回的第七種字符串值是什么? ```source-js typeof function a(){ /* .. */ } === "function"; // true ``` 很容易認為在 JS 中?`function`?是一種頂層的內建類型,特別是看到?`typeof`?操作符的這種行為時。然而,如果你閱讀語言規范,你會看到它實際上是對象(object)的“子類型”。特別地,一個函數(function)被稱為“可調用對象” —— 一個擁有?`[[Call]]`?內部屬性、允許被調用的對象。 函數實際上是對象這一事實十分有用。最重要的是,它們可以擁有屬性。例如: ```source-js function a(b,c) { /* .. */ } ``` 這個函數對象擁有一個?`length`?屬性,它被設置為函數被聲明時的形式參數的數量。 ```source-js a.length; // 2 ``` 因為你使用了兩個正式命名的參數(`b`?和?`c`)聲明了函數,所以“函數的長度”是?`2`。 那么數組呢?它們是 JS 原生的,所以它們是一個特殊的類型咯? ```source-js typeof [1,2,3] === "object"; // true ``` 不,它們僅僅是對象。考慮它們的最恰當的方法是,也將它們認為是對象的“子類型”(見第三章),帶有被數字索引的附加性質(與僅僅使用字符串鍵的普通對象相反),并維護著一個自動更新的?`.length`?屬性。 ## 值作為類型 在 JavaScript 中,變量沒有類型 --?值才有類型。變量可以在任何時候,持有任何值。 另一種考慮 JS 類型的方式是,JS 沒有“類型強制”,也就是引擎不堅持認為一個?*變量*?總是持有與它開始存在時相同的?*初始類型*的值。在一個賦值語句中,一個變量可以持有一個?`string`,而在下一個賦值語句中持有一個?`nubmer`,如此類推。 *值*?`42`?有固有的類型?`number`,而且它的?*類型*?是不能被改變的。另一個值,比如?`string`?類型的?`"42"`,可以通過一個稱為?強制轉換?的處理從?`number`?類型的值?`42`?中創建出來(見第四章)。 如果你對一個變量使用?`typeof`,它不會像表面上看起來那樣詢問“這個變量的類型是什么?”,因為 JS 變量是沒有類型的。取而代之的是,它會詢問“在這個變量里的值的類型是什么?” ```source-js var a = 42; typeof a; // "number" a = true; typeof a; // "boolean" ``` `typeof`?操作符總是返回字符串。所以: ```source-js typeof typeof 42; // "string" ``` 第一個?`typeof 42`?返回?`"number"`,而?`typeof "number"`?是?`"string"`。 ### `undefined`?vs "undeclared" *當前*?還不擁有值的變量,實際上擁有?`undefined`?值。對這樣的變量調用?`typeof`?將會返回?`"undefined"`: ```source-js var a; typeof a; // "undefined" var b = 42; var c; // 稍后 b = c; typeof b; // "undefined" typeof c; // "undefined" ``` 大多數開發者考慮“undefined”這個詞的方式會誘使他們認為它是“undeclared(未聲明)”的同義詞。然而在 JS 中,這兩個概念十分不同。 一個“undefined”變量是在可訪問的作用域中已經被聲明過的,但是在?*這個時刻*?它里面沒有任何值。相比之下,一個“undeclared”變量是在可訪問的作用域中還沒有被正式聲明的。 考慮這段代碼: ```source-js var a; a; // undefined b; // ReferenceError: b is not defined ``` 一個惱人的困惑是瀏覽器給這種情形分配的錯誤消息。正如你所看到的,這個消息是“b is not defined”,這當然很容易而且很合理地使人將它與“b is undefined.”搞混。需要重申的是,“undefined”和“is not defined”是非常不同的東西。要是瀏覽器能告訴我們類似于“b is not found”或者“b is not declared”之類的東西就好了,那會減少這種困惑! 還有一種?`typeof`?與未聲明變量關聯的特殊行為,進一步增強了這種困惑。考慮這段代碼: ```source-js var a; typeof a; // "undefined" typeof b; // "undefined" ``` `typeof`?操作符甚至為“undeclared”(或“not defined”)變量返回?`"undefined"`。要注意的是,當我們執行?`typeof b`?時,即使?`b`?是一個未聲明變量,也不會有錯誤被拋出。這是?`typeof`?的一種特殊的安全防衛行為。 和上面類似地,要是?`typeof`?與未聲明變量一起使用時返回“undeclared”就好了,而不是將其結果值與不同的“undefined”情況混為一談。 ### `typeof`?Undeclared 不管怎樣,當在瀏覽器中處理 JavaScript 時這種安全防衛是一種有用的特性,因為瀏覽器中多個腳本文件會將變量加載到共享的全局名稱空間。 注意:?許多開發者相信,在全局名稱空間中絕不應該有任何變量,而且所有東西應當被包含在模塊和私有/隔離的名稱空間中。這在理論上很偉大但在實踐中幾乎是不可能的;但它仍然是一個值得的努力方向!幸運的是,ES6 為模塊加入了頭等支持,這終于使這一理論變得可行的多了。 作為一個簡單的例子,想象在你的程序中有一個“調試模式”,它是通過一個稱為?`DEBUG`?的全局變量(標志)來控制的。在實施類似于在控制臺上輸出一條日志消息這樣的調試任務之前,你想要檢查這個變量是否被聲明了。一個頂層的全局?`var DEBUG = true`?聲明只包含在一個“debug.js”文件中,這個文件僅在你開發/測試時才被加載到瀏覽器中,而在生產環境中則不會。 然而,在你其他的程序代碼中,你不得不小心你是如何檢查這個全局的?`DEBUG`?變量的,這樣你才不會拋出一個?`ReferenceError`。這種情況下?`typeof`?上的安全防衛就是我們的朋友。 ```source-js // 噢,這將拋出一個錯誤! if (DEBUG) { console.log( "Debugging is starting" ); } // 這是一個安全的存在性檢查 if (typeof DEBUG !== "undefined") { console.log( "Debugging is starting" ); } ``` 即便你不是在對付用戶定義的變量(比如?`DEBUG`),這種檢查也是很有用的。如果你為一個內建的 API 做特性檢查,你也會發現不拋出錯誤的檢查很有幫助: ```source-js if (typeof atob === "undefined") { atob = function() { /*..*/ }; } ``` 注意:?如果你在為一個還不存在的特性定義一個“填補”,你可能想要避免使用?`var`?來聲明?`atob`。如果你在?`if`?語句內部聲明?`var atob`,即使這個?`if`?條件沒有通過(因為全局的?`atob`?已經存在),這個聲明也會被提升(參見本系列的?*作用域與閉包*)到作用域的頂端。在某些瀏覽器中,對一些特殊類型的內建全局變量(常被稱為“宿主對象”),這種重復聲明也許會拋出錯誤。忽略?`var`?可以防止這種提升聲明。 另一種不帶有?`typeof`?的安全防衛特性,而對全局變量進行這些檢查的方法是,將所有的全局變量作為全局對象的屬性來觀察,在瀏覽器中這個全局對象基本上是?`window`?對象。所以,上面的檢查可以(十分安全地)這樣做: ```source-js if (window.DEBUG) { // .. } if (!window.atob) { // .. } ``` 和引用未聲明變量不同的是,在你試著訪問一個不存在的對象屬性時(即便是在全局的?`window`?對象上),不會有?`ReferenceError`?被拋出。 另一方面,一些開發者偏好避免手動使用?`window`?引用全局變量,特別是當你的代碼需要運行在多種 JS 環境中時(例如不僅是在瀏覽器中,還在服務器端的 node.js 中),全局變量可能不總是稱為?`window`。 技術上講,這種?`typeof`?上的安全防衛即使在你不使用全局變量時也很有用,雖然這些情況不那么常見,而且一些開發者也許發現這種設計方式不那么理想。想象一個你想要其他人復制-粘貼到他們程序中或模塊中的工具函數,在它里面你想要檢查包含它的程序是否已經定義了一個特定的變量(以便于你可以使用它): ```source-js function doSomethingCool() { var helper = (typeof FeatureXYZ !== "undefined") ? FeatureXYZ : function() { /*.. 默認的特性 ..*/ }; var val = helper(); // .. } ``` `doSomethingCool()`?對稱為?`FeatureXYZ`?變量進行檢查,如果找到,就使用它,如果沒找到,使用它自己的。現在,如果某個人在他的模塊/程序中引入了這個工具,它會安全地檢查我們是否已經定義了?`FeatureXYZ`: ```source-js // 一個 IIFE(參見本系列的 *作用域與閉包* 中的“立即被調用的函數表達式”) (function(){ function FeatureXYZ() { /*.. my XYZ feature ..*/ } // 引入 `doSomethingCool(..)` function doSomethingCool() { var helper = (typeof FeatureXYZ !== "undefined") ? FeatureXYZ : function() { /*.. 默認的特性 ..*/ }; var val = helper(); // .. } doSomethingCool(); })(); ``` 這里,`FeatureXYZ`?根本不是一個全局變量,但我們仍然使用?`typeof`?的安全防衛來使檢查變得安全。而且重要的是,我們在這里?*沒有*?可以用于檢查的對象(就像我們使用?`window.___`?對全局變量做的那樣),所以?`typeof`?十分有幫助。 另一些開發者偏好一種稱為“依賴注入”的設計模式,與?`doSomethingCool()`?隱含地檢查?`FeatureXYZ`?是否在它外部/周圍被定義過不同的是,它需要依賴明確地傳遞進來,就像這樣: ```source-js function doSomethingCool(FeatureXYZ) { var helper = FeatureXYZ || function() { /*.. 默認的特性 ..*/ }; var val = helper(); // .. } ``` 在設計這樣的功能時有許多選擇。這些模式里沒有“正確”或“錯誤” -- 每種方式都有各種權衡。但總的來說,`typeof`?的未聲明安全防衛給了我們更多選項,這還是很不錯的。 ## 復習 JavaScript 有七種內建?*類型*:`null`、`undefined`、`boolean`、`number`、`string`、`object`、`symbol`。它們可以被?`typeof`?操作符識別。 變量沒有類型,但是值有類型。這些類型定義了值的固有行為。 許多開發者會認為“undefined”和“undeclared”大體上是同一個東西,但是在 JavaScript 中,它們是十分不同的。`undefined`?是一個可以由被聲明的變量持有的值。“未聲明”意味著一個變量從來沒有被聲明過。 JavaScript 很不幸地將這兩個詞在某種程度上混為了一談,不僅體現在它的錯誤消息上(“ReferenceError: a is not defined”),也體現在?`typeof`?的返回值上:對于兩者它都返回?`"undefined"`。 然而,當對一個未聲明的變量使用?`typeof`?時,`typeof`?上的安全防衛機制(防止一個錯誤)可以在特定的情況下非常有用。
                  <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>

                              哎呀哎呀视频在线观看