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

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                JavaScript 是 ECMAScript 規范的一種實現,本小節重點梳理下 ECMAScript 中的常考知識點,然后就一些容易出現的題目進行解析。 </br> ## 知識點梳理 * 變量類型 * JS 的數據類型分類和判斷 * 值類型和引用類型 * 原型與原型鏈(繼承) * 原型和原型鏈定義 * 繼承寫法 * 作用域和閉包 * 執行上下文 * this * 閉包是什么 * 異步 * 同步 vs 異步 * 異步和單線程 * 前端異步的場景 * ES6/7 新標準的考查 * 箭頭函數 * Module * Class * Set 和 Map * Promise </br> ## 變量類型 JavaScript 是一種弱類型腳本語言,所謂弱類型指的是定義變量時,不需要什么類型,在程序運行過程中會自動判斷類型。 ECMAScript 中定義了 6 種原始類型: * Boolean * String * Number * Null * Undefined * Symbol(ES6 新定義) **注意**:原始類型不包含 Object。 > 題目:類型判斷用到哪些方法? ### `typeof` `typeof xxx`得到的值有以下幾種類型:`undefined` `boolean` `number` `string` `object` `function`、`symbol` ,比較簡單,不再一一演示了。這里需要注意的有三點: * `typeof null`結果是`object` ,實際這是`typeof`的一個bug,null是原始值,非引用類型 * `typeof [1, 2]`結果是`object`,結果中沒有`array`這一項,引用類型除了`function`其他的全部都是`object` * `typeof Symbol()` 用`typeof`獲取`symbol`類型的值得到的是`symbol`,這是 ES6 新增的知識點 ### `instanceof` 用于實例和構造函數的對應。例如判斷一個變量是否是數組,使用`typeof`無法判斷,但可以使用`[1, 2] instanceof Array`來判斷。因為,`[1, 2]`是數組,它的構造函數就是`Array`。同理: ``` function Foo(name) { this.name = name } var foo = new Foo('bar') console.log(foo instanceof Foo) // true ``` > 題目:值類型和引用類型的區別 ### 值類型 vs 引用類型 除了原始類型,ES 還有引用類型,上文提到的`typeof`識別出來的類型中,只有`object`和`function`是引用類型,其他都是值類型。 根據 JavaScript 中的變量類型傳遞方式,又分為**值類型**和**引用類型**,值類型變量包括 Boolean、String、Number、Undefined、Null,引用類型包括了 Object 類的所有,如 Date、Array、Function 等。在參數傳遞方式上,值類型是按值傳遞,引用類型是按共享傳遞。 下面通過一個小題目,來看下兩者的主要區別,以及實際開發中需要注意的地方。 ``` // 值類型 var a = 10 var b = a b = 20 console.log(a) // 10 console.log(b) // 20 ``` 上述代碼中,`a` `b`都是值類型,兩者分別修改賦值,相互之間沒有任何影響。再看引用類型的例子: ``` // 引用類型 var a = {x: 10, y: 20} var b = a b.x = 100 b.y = 200 console.log(a) // {x: 100, y: 200} console.log(b) // {x: 100, y: 200} ``` 上述代碼中,`a` `b`都是引用類型。在執行了`b = a`之后,修改`b`的屬性值,`a`的也跟著變化。因為`a`和`b`都是引用類型,指向了同一個內存地址,即兩者引用的是同一個值,因此`b`修改屬性時,`a`的值隨之改動。 再借助題目進一步講解一下。 > 說出下面代碼的執行結果,并分析其原因。 ``` function foo(a){ a = a * 10; } function bar(b){ b.value = 'new'; } var a = 1; var b = {value: 'old'}; foo(a); bar(b); console.log(a); // 1 console.log(b); // value: new ``` 通過代碼執行,會發現: * `a`的值沒有發生改變 * 而`b`的值發生了改變 這就是因為`Number`類型的`a`是按值傳遞的,而`Object`類型的`b`是按共享傳遞的。 JS 中這種設計的原因是:按值傳遞的類型,復制一份存入棧內存,這類類型一般不占用太多內存,而且按值傳遞保證了其訪問速度。按共享傳遞的類型,是復制其引用,而不是整個復制其值(C 語言中的指針),保證過大的對象等不會因為不停復制內容而造成內存的浪費。 引用類型經常會在代碼中按照下面的寫法使用,或者說**容易不知不覺中造成錯誤**! ``` var obj = { a: 1, b: [1,2,3] } var a = obj.a var b = obj.b a = 2 b.push(4) console.log(obj, a, b) ``` 雖然`obj`本身是個引用類型的變量(對象),但是內部的`a`和`b`一個是值類型一個是引用類型,`a`的賦值不會改變`obj.a`,但是`b`的操作卻會反映到`obj`對象上。 </br> ## 原型和原型鏈 JavaScript 是基于原型的語言,原型理解起來非常簡單,但卻特別重要,下面還是通過題目來理解下JavaScript 的原型概念。 > 題目:如何理解 JavaScript 的原型 對于這個問題,可以從下面這幾個要點來理解和回答,**下面幾條必須記住并且理解** * **所有的引用類型(數組、對象、函數),都具有對象特性,即可自由擴展屬性(`null`除外)** * **所有的引用類型(數組、對象、函數),都有一個`__proto__`屬性,屬性值是一個普通的對象** * **所有的函數,都有一個`prototype`屬性,屬性值也是一個普通的對象** * **所有的引用類型(數組、對象、函數),`__proto__`屬性值指向它的構造函數的`prototype`屬性值** 通過代碼解釋一下,大家可自行運行以下代碼,看結果。 ``` // 要點一:自由擴展屬性 var obj = {}; obj.a = 100; var arr = []; arr.a = 100; function fn () {} fn.a = 100; // 要點二:__proto__ console.log(obj.__proto__); console.log(arr.__proto__); console.log(fn.__proto__); // 要點三:函數有 prototype console.log(fn.prototype) // 要點四:引用類型的 __proto__ 屬性值指向它的構造函數的 prototype 屬性值 console.log(obj.__proto__ === Object.prototype) ``` ### 原型 先寫一個簡單的代碼示例。 ``` // 構造函數 function Foo(name, age) { this.name = name } Foo.prototype.alertName = function () { alert(this.name) } // 創建示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name) } // 測試 f.printName() f.alertName() ``` 執行`printName`時很好理解,但是執行`alertName`時發生了什么?這里再記住一個重點 **當試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那么會去它的`__proto__`(即它的構造函數的`prototype`)中尋找**,因此`f.alertName`就會找到`Foo.prototype.alertName`。 那么如何判斷這個屬性是不是對象本身的屬性呢?使用`hasOwnProperty`,常用的地方是遍歷一個對象的時候。 ``` var item for (item in f) { // 高級瀏覽器已經在 for in 中屏蔽了來自原型的屬性,但是這里建議大家還是加上這個判斷,保證程序的健壯性 if (f.hasOwnProperty(item)) { console.log(item) } } ``` > 題目:如何理解 JS 的原型鏈 ### 原型鏈 還是接著上面的示例,如果執行`f.toString()`時,又發生了什么? ``` // 省略 N 行 // 測試 f.printName() f.alertName() f.toString() ``` 因為`f`本身沒有`toString()`,并且`f.__proto__`(即`Foo.prototype`)中也沒有`toString`。這個問題還是得拿出剛才那句話——**當試圖得到一個對象的某個屬性時,如果這個對象本身沒有這個屬性,那么會去它的`__proto__`(即它的構造函數的`prototype`)中尋找**。 如果在`f.__proto__`中沒有找到`toString`,那么就繼續去`f.__proto__.__proto__`中尋找,因為`f.__proto__`就是一個普通的對象而已嘛! * `f.__proto__`即`Foo.prototype`,沒有找到`toString`,繼續往上找 * `f.__proto__.__proto__`即`Foo.prototype.__proto__`。`Foo.prototype`就是一個普通的對象,因此`Foo.prototype.__proto__`就是`Object.prototype`,在這里可以找到`toString` * 因此`f.toString`最終對應到了`Object.prototype.toString` 這樣一直往上找,你會發現是一個鏈式的結構,所以叫做“原型鏈”。如果一直找到最上層都沒有找到,那么就宣告失敗,返回`undefined`。最上層是什么 —— `Object.prototype.__proto__ === null` ### 原型鏈中的`this` 所有從原型或更高級原型中得到、執行的方法,其中的`this`在執行時,就指向了當前這個觸發事件執行的對象。因此`printName`和`alertName`中的`this`都是`f`。 </br> ## 作用域和閉包 作用域和閉包是前端面試中,最可能考查的知識點。例如下面的題目: > 題目:現在有個 HTML 片段,要求編寫代碼,點擊編號為幾的鏈接就`alert`彈出其編號 ``` <ul> <li>編號1,點擊我請彈出1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> ``` 一般不知道這個題目用閉包的話,會寫出下面的代碼: ``` var list = document.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { list[i].addEventListener('click', function(){ alert(i + 1) }, true) } ``` 實際上執行才會發現始終彈出的是`6`,這時候就應該通過閉包來解決: ``` var list = document.getElementsByTagName('li'); for (var i = 0; i < list.length; i++) { list[i].addEventListener('click', function(i){ return function(){ alert(i + 1) } }(i), true) } ``` 要理解閉包,就需要我們從「執行上下文」開始講起。 ### 執行上下文 先講一個關于 **變量提升** 的知識點,面試中可能會遇見下面的問題,很多候選人都回答錯誤: > 題目:說出下面執行的結果(這里筆者直接注釋輸出了) ``` console.log(a) // undefined var a = 100 fn('zhangsan') // 'zhangsan' 20 function fn(name) { age = 20 console.log(name, age) var age } console.log(b); // 這里報錯 // Uncaught ReferenceError: b is not defined b = 100; ``` 在一段 JS 腳本(即一個`<script>`標簽中)執行之前,要先解析代碼(所以說 JS 是解釋執行的腳本語言),解析的時候會先創建一個 **全局執行上下文** 環境,先把代碼中即將執行的(內部函數的不算,因為你不知道函數何時執行)變量、函數聲明都拿出來。變量先暫時賦值為`undefined`,函數則先聲明好可使用。這一步做完了,然后再開始正式執行程序。再次強調,這是在代碼執行之前才開始的工作。 我們來看下上面的面試小題目,為什么`a`是`undefined`,而`b`卻報錯了,實際 JS 在代碼執行之前,要「全文解析」,發現`var a`,知道有個`a`的變量,存入了執行上下文,而`b`沒有找到`var`關鍵字,這時候沒有在執行上下文提前「占位」,所以代碼執行的時候,提前報到的`a`是有記錄的,只不過值暫時還沒有賦值,即為`undefined`,而`b`在執行上下文沒有找到,自然會報錯(沒有找到`b`的引用)。 另外,一個函數在執行之前,也會創建一個 **函數執行上下文** 環境,跟 **全局上下文** 差不多,不過 **函數執行上下文** 中會多出`this` `arguments`和函數的參數。參數和`arguments`好理解,這里的`this`咱們需要專門講解。 總結一下: * 范圍:一段`<script>`、js 文件或者一個函數 * 全局上下文:變量定義,函數聲明 * 函數上下文:變量定義,函數聲明,`this`,`arguments` ### `this` 先搞明白一個很重要的概念 —— **`this`的值是在執行的時候才能確認,定義的時候不能確認!** 為什么呢 —— 因為`this`是執行上下文環境的一部分,而執行上下文需要在代碼執行之前確定,而不是定義的時候。看如下例子 ``` var a = { name: 'A', fn: function () { console.log(this.name) } } a.fn() // this === a a.fn.call({name: 'B'}) // this === {name: 'B'} var fn1 = a.fn fn1() // this === window ``` `this`執行會有不同,主要集中在這幾個場景中 * 作為構造函數執行,構造函數中 * 作為對象屬性執行,上述代碼中`a.fn()` * 作為普通函數執行,上述代碼中`fn1()` * 用于`call` `apply` `bind`,上述代碼中`a.fn.call({name: 'B'})` 下面再來講解下什么是作用域和作用域鏈,作用域鏈和作用域也是常考的題目。 > 題目:如何理解 JS 的作用域和作用域鏈 ### 作用域 ES6 之前 JS 沒有塊級作用域。例如 ``` if (true) { var name = 'zhangsan' } console.log(name) ``` 從上面的例子可以體會到作用域的概念,作用域就是一個獨立的地盤,讓變量不會外泄、暴露出去。上面的`name`就被暴露出去了,因此,**JS 沒有塊級作用域,只有全局作用域和函數作用域**。 ``` var a = 100 function fn() { var a = 200 console.log('fn', a) } console.log('global', a) fn() ``` 全局作用域就是最外層的作用域,如果我們寫了很多行 JS 代碼,變量定義都沒有用函數包括,那么它們就全部都在全局作用域中。這樣的壞處就是很容易撞車、沖突。 ``` // 張三寫的代碼中 var data = {a: 100} // 李四寫的代碼中 var data = {x: true} ``` 這就是為何 jQuery、Zepto 等庫的源碼,所有的代碼都會放在`(function(){....})()`中。因為放在里面的所有變量,都不會被外泄和暴露,不會污染到外面,不會對其他的庫或者 JS 腳本造成影響。這是函數作用域的一個體現。 附:ES6 中開始加入了塊級作用域,使用`let`定義變量即可,如下: ``` if (true) { let name = 'zhangsan' } console.log(name) // 報錯,因為let定義的name是在if這個塊級作用域 ``` ### 作用域鏈 首先認識一下什么叫做 **自由變量** 。如下代碼中,`console.log(a)`要得到`a`變量,但是在當前的作用域中沒有定義`a`(可對比一下`b`)。當前作用域沒有定義的變量,這成為 **自由變量** 。自由變量如何得到 —— 向父級作用域尋找。 ``` var a = 100 function fn() { var b = 200 console.log(a) console.log(b) } fn() ``` 如果父級也沒呢?再一層一層向上尋找,直到找到全局作用域還是沒找到,就宣布放棄。這種一層一層的關系,就是 **作用域鏈** 。 ``` var a = 100 function F1() { var b = 200 function F2() { var c = 300 console.log(a) // 自由變量,順作用域鏈向父作用域找 console.log(b) // 自由變量,順作用域鏈向父作用域找 console.log(c) // 本作用域的變量 } F2() } F1() ``` ### 閉包 講完這些內容,我們再來看一個例子,通過例子來理解閉包。 ``` function F1() { var a = 100 return function () { console.log(a) } } var f1 = F1() var a = 200 f1() ``` 自由變量將從作用域鏈中去尋找,但是 **依據的是函數定義時的作用域鏈,而不是函數執行時**,以上這個例子就是閉包。閉包主要有兩個應用場景: * **函數作為返回值**,上面的例子就是 * **函數作為參數傳遞**,看以下例子 ``` function F1() { var a = 100 return function () { console.log(a) } } function F2(f1) { var a = 200 console.log(f1()) } var f1 = F1() F2(f1) ``` 至此,對應著「作用域和閉包」這部分一開始的點擊彈出`alert`的代碼再看閉包,就很好理解了。 </br> ## 異步 異步和同步也是面試中常考的內容,下面筆者來講解下同步和異步的區別。 ### 同步 vs 異步 先看下面的 demo,根據程序閱讀起來表達的意思,應該是先打印`100`,1秒鐘之后打印`200`,最后打印`300`。但是實際運行根本不是那么回事。 ``` console.log(100) setTimeout(function () { console.log(200) }, 1000) console.log(300) ``` 再對比以下程序。先打印`100`,再彈出`200`(等待用戶確認),最后打印`300`。這個運行效果就符合預期要求。 ``` console.log(100) alert(200) // 1秒鐘之后點擊確認 console.log(300) ``` 這倆到底有何區別?—— 第一個示例中間的步驟根本沒有阻塞接下來程序的運行,而第二個示例卻阻塞了后面程序的運行。前面這種表現就叫做 **異步**(后面這個叫做 **同步** ),即**不會阻塞后面程序的運行**。 ### 異步和單線程 JS 需要異步的根本原因是 **JS 是單線程運行的**,即在同一時間只能做一件事,不能“一心二用”。 一個 Ajax 請求由于網絡比較慢,請求需要 5 秒鐘。如果是同步,這 5 秒鐘頁面就卡死在這里啥也干不了了。異步的話,就好很多了,5 秒等待就等待了,其他事情不耽誤做,至于那 5 秒鐘等待是網速太慢,不是因為 JS 的原因。 講到單線程,我們再來看個真題: > 題目:講解下面代碼的執行過程和結果 ``` var a = true; setTimeout(function(){ a = false; }, 100) while(a){ console.log('while執行了') } ``` 這是一個很有迷惑性的題目,不少候選人認為`100ms`之后,由于`a`變成了`false`,所以`while`就中止了,實際不是這樣,因為JS是單線程的,所以進入`while`循環之后,沒有「時間」(線程)去跑定時器了,所以這個代碼跑起來是個死循環! ### 前端異步的場景 * 定時 `setTimeout` `setInterval` * 網絡請求,如 `Ajax` `<img>`加載 Ajax 代碼示例 ``` console.log('start') $.get('./data1.json', function (data1) { console.log(data1) }) console.log('end') ``` img 代碼示例(常用于打點統計) ``` console.log('start') var img = document.createElement('img') // 或者 img = new Image() img.onload = function () { console.log('loaded') img.onload = null } img.src = '/xxx.png' console.log('end') ``` * * * </br> ## ES6/7 新標準的考查 > 題目:ES6 箭頭函數中的`this`和普通函數中的有什么不同 ### 箭頭函數 箭頭函數是 ES6 中新的函數定義形式,`function name(arg1, arg2) {...}`可以使用`(arg1, arg2) => {...}`來定義。示例如下: ``` // JS 普通函數 var arr = [1, 2, 3] arr.map(function (item) { console.log(index) return item + 1 }) // ES6 箭頭函數 const arr = [1, 2, 3] arr.map((item, index) => { console.log(index) return item + 1 }) ``` 箭頭函數存在的意義,第一寫起來更加簡潔,第二可以解決 ES6 之前函數執行中`this`是全局變量的問題,看如下代碼 ``` function fn() { console.log('real', this) // {a: 100} ,該作用域下的 this 的真實的值 var arr = [1, 2, 3] // 普通 JS arr.map(function (item) { console.log('js', this) // window 。普通函數,這里打印出來的是全局變量,令人費解 return item + 1 }) // 箭頭函數 arr.map(item => { console.log('es6', this) // {a: 100} 。箭頭函數,這里打印的就是父作用域的 this return item + 1 }) } fn.call({a: 100}) ``` > 題目:ES6 模塊化如何使用? ### Module ES6 中模塊化語法更加簡潔,直接看示例。 如果只是輸出一個唯一的對象,使用`export default`即可,代碼如下 ``` // 創建 util1.js 文件,內容如 export default { a: 100 } // 創建 index.js 文件,內容如 import obj from './util1.js' console.log(obj) ``` 如果想要輸出許多個對象,就不能用`default`了,且`import`時候要加`{...}`,代碼如下 ``` // 創建 util2.js 文件,內容如 export function fn1() { alert('fn1') } export function fn2() { alert('fn2') } // 創建 index.js 文件,內容如 import { fn1, fn2 } from './util2.js' fn1() fn2() ``` > 題目:ES6 class 和普通構造函數的區別 ### class class 其實一直是 JS 的關鍵字(保留字),但是一直沒有正式使用,直到 ES6 。 ES6 的 class 就是取代之前構造函數初始化對象的形式,從語法上更加符合面向對象的寫法。例如: JS 構造函數的寫法 ``` function MathHandle(x, y) { this.x = x; this.y = y; } MathHandle.prototype.add = function () { return this.x + this.y; }; var m = new MathHandle(1, 2); console.log(m.add()) ``` 用 ES6 class 的寫法 ``` class MathHandle { constructor(x, y) { this.x = x; this.y = y; } add() { return this.x + this.y; } } const m = new MathHandle(1, 2); console.log(m.add()) ``` 注意以下幾點,全都是關于 class 語法的: * class 是一種新的語法形式,是`class Name {...}`這種形式,和函數的寫法完全不一樣 * 兩者對比,構造函數函數體的內容要放在 class 中的`constructor`函數中,`constructor`即構造器,初始化實例時默認執行 * class 中函數的寫法是`add() {...}`這種形式,并沒有`function`關鍵字 使用 class 來實現繼承就更加簡單了,至少比構造函數實現繼承簡單很多。看下面例子 JS 構造函數實現繼承 ``` // 動物 function Animal() { this.eat = function () { console.log('animal eat') } } // 狗 function Dog() { this.bark = function () { console.log('dog bark') } } Dog.prototype = new Animal() // 哈士奇 var hashiqi = new Dog() ``` ES6 class 實現繼承 ``` class Animal { constructor(name) { this.name = name } eat() { console.log(`${this.name} eat`) } } class Dog extends Animal { constructor(name) { super(name) this.name = name } say() { console.log(`${this.name} say`) } } const dog = new Dog('哈士奇') dog.say() dog.eat() ``` 注意以下兩點: * 使用`extends`即可實現繼承,更加符合經典面向對象語言的寫法,如 Java * 子類的`constructor`一定要執行`super()`,以調用父類的`constructor` > 題目:ES6 中新增的數據類型有哪些? ### Set 和 Map Set 和 Map 都是 ES6 中新增的數據結構,是對當前 JS 數組和對象這兩種重要數據結構的擴展。由于是新增的數據結構,目前尚未被大規模使用,但是作為前端程序員,提前了解是必須做到的。先總結一下兩者最關鍵的地方: * Set 類似于數組,但數組可以允許元素重復,Set 不允許元素重復 * Map 類似于對象,但普通對象的 key 必須是字符串或者數字,而 Map 的 key 可以是任何數據類型 **Set** Set 實例不允許元素有重復,可以通過以下示例證明。可以通過一個數組初始化一個 Set 實例,或者通過`add`添加元素,元素不能重復,重復的會被忽略。 ``` // 例1 const set = new Set([1, 2, 3, 4, 4]); console.log(set) // Set(4)?{1, 2, 3, 4} // 例2 const set = new Set(); [2, 3, 5, 4, 5, 8, 8].forEach(item => set.add(item)); for (let item of set) { console.log(item); } // 2 3 5 4 8 ``` Set 實例的屬性和方法有 * `size`:獲取元素數量。 * `add(value)`:添加元素,返回 Set 實例本身。 * `delete(value)`:刪除元素,返回一個布爾值,表示刪除是否成功。 * `has(value)`:返回一個布爾值,表示該值是否是 Set 實例的元素。 * `clear()`:清除所有元素,沒有返回值。 ``` const s = new Set(); s.add(1).add(2).add(2); // 添加元素 s.size // 2 s.has(1) // true s.has(2) // true s.has(3) // false s.delete(2); s.has(2) // false s.clear(); console.log(s); // Set(0)?{} ``` Set 實例的遍歷,可使用如下方法 * `keys()`:返回鍵名的遍歷器。 * `values()`:返回鍵值的遍歷器。不過由于 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),所以`keys()`和`values()`返回結果一致。 * `entries()`:返回鍵值對的遍歷器。 * `forEach()`:使用回調函數遍歷每個成員。 ``` let set = new Set(['aaa', 'bbb', 'ccc']); for (let item of set.keys()) { console.log(item); } // aaa // bbb // ccc for (let item of set.values()) { console.log(item); } // aaa // bbb // ccc for (let item of set.entries()) { console.log(item); } // ["aaa", "aaa"] // ["bbb", "bbb"] // ["ccc", "ccc"] set.forEach((value, key) => console.log(key + ' : ' + value)) // aaa : aaa // bbb : bbb // ccc : ccc ``` **Map** Map 的用法和普通對象基本一致,先看一下它能用非字符串或者數字作為 key 的特性。 ``` const map = new Map(); const obj = {p: 'Hello World'}; map.set(obj, 'OK') map.get(obj) // "OK" map.has(obj) // true map.delete(obj) // true map.has(obj) // false ``` 需要使用`new Map()`初始化一個實例,下面代碼中`set` `get` `has` `delete`顧名即可思義(下文也會演示)。其中,`map.set(obj, 'OK')`就是用對象作為的 key (不光可以是對象,任何數據類型都可以),并且后面通過`map.get(obj)`正確獲取了。 Map 實例的屬性和方法如下: * `size`:獲取成員的數量 * `set`:設置成員 key 和 value * `get`:獲取成員屬性值 * `has`:判斷成員是否存在 * `delete`:刪除成員 * `clear`:清空所有 ``` const map = new Map(); map.set('aaa', 100); map.set('bbb', 200); map.size // 2 map.get('aaa') // 100 map.has('aaa') // true map.delete('aaa') map.has('aaa') // false map.clear() ``` Map 實例的遍歷方法有: * `keys()`:返回鍵名的遍歷器。 * `values()`:返回鍵值的遍歷器。 * `entries()`:返回所有成員的遍歷器。 * `forEach()`:遍歷 Map 的所有成員。 ``` const map = new Map(); map.set('aaa', 100); map.set('bbb', 200); for (let key of map.keys()) { console.log(key); } // "aaa" // "bbb" for (let value of map.values()) { console.log(value); } // 100 // 200 for (let item of map.entries()) { console.log(item[0], item[1]); } // aaa 100 // bbb 200 // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // aaa 100 // bbb 200 ``` ### Promise `Promise`是 CommonJS 提出來的這一種規范,有多個版本,在 ES6 當中已經納入規范,原生支持 Promise 對象,非 ES6 環境可以用類似 Bluebird、Q 這類庫來支持。 `Promise` 可以將回調變成鏈式調用寫法,流程更加清晰,代碼更加優雅。 簡單歸納下 Promise:**三個狀態、兩個過程、一個方法**,快速記憶方法:**3-2-1** 三個狀態:`pending`、`fulfilled`、`rejected` 兩個過程: * pending→fulfilled(resolve) * pending→rejected(reject) 一個方法:`then` 當然還有其他概念,如`catch`、 `Promise.all/race`,這里就不展開了。 關于 ES6/7 的考查內容還有很多,本小節就不逐一介紹了,如果想繼續深入學習,可以在線看《[ES6入門](http://es6.ruanyifeng.com/)》。 </br> ## 小結 本小節主要總結了 ES 基礎語法中面試經常考查的知識點,包括之前就考查較多的原型、異步、作用域,以及 ES6 的一些新內容,這些知識點希望大家都要掌握。
                  <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>

                              哎呀哎呀视频在线观看