<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之旅 廣告
                [TOC] # let 與 const 的使用 ⒈不存在變量提升 ? 變量要在聲明之后再使用 ⒉暫時性死區 ? let 和 const 形成封閉作用域,該作用域(代碼塊)內使用變量之前都必須先聲明 ⒊不允許重復聲明 ? 不允許在相同作用域內重復聲明同一變量 ⒋塊級作用域 ? ① 防止內層變量覆蓋外層變量 ② 防止用于計數的循環變量泄露為全局變量 ⒌const 保證的是變量指向的內存地址不得改動,可以為 const 聲明的數組或對象添加元素 / 屬性但是不能指向另一個地址 ⒍let 和 const 聲明的變量不會添加到 window 對象中(var 會),而會添加到一個 Script 作用域 ![](https://box.kancloud.cn/6aafef41037f89d43f063b4aba30b298_554x240.png) ***** 例題:使用 var 聲明的變量來控制循環會泄漏為全局變量,每一層循環新的 i 值都會覆蓋舊的 i 值;換句話來講,所有的 i 指向一個全局變量 i ```js var a = [] for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[6]() // 10 ``` 使用 let,每一次循環的 i 其實都是一個新的變量;你可能會問,如果每一輪循環的變量 i 都是重新聲明的,那它怎么知道上一輪循環的值,從而計算出本輪循環的值?這是因為 JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量 i 時,就在上一輪循環的基礎上進行計算。 ```js var a = [] for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i) } } a[6]() // 6 ``` 另外,for 循環還有一個特別之處,就是設置循環變量的那部分是一個父作用域,而循環體內部是一個單獨的子作用域。 ```js for (let i = 0; i < 3; i++) { let i = 'abc' console.log(i) } // abc // abc // abc ``` # 解構賦值 ## 1、數組的解構賦值 例子:`let [a, b, c] = [1, 2, 3]` 兩邊都是方括號,從左往右依次匹配賦值 如果解構不成功,變量的值為 undefined;可以有 “不完全解構”,例如下面這段代碼: ```js let [a, [b], d] = [1, [2, 3], 4] a // 1 b // 2 d // 4 ``` 右邊不一定必須是數組,只要某種數據結構具有 Iterator 接口,都可以采用數組形式的解構賦值。 變量可以有默認值:`let [foo = true] = [ ]` ## 2、對象的解構賦值 例子:`let {foo, bar} = {foo:"aaa", bar:"bbb"}` 兩邊都是花括號,與數組的解構賦值的不同:變量的賦值是無序的 兩種寫法 ① 變量與屬性同名 ```js let {bar, foo} = {foo:'aaa', bar:'bbb'} ``` ② 變量名與屬性名不一致,在變量前給出匹配的 “模式”,即 ":" 之前的不是要賦值的變量而是給出了之后的變量應該匹配哪個屬性 ```js let obj = {first:'hello', last:'world'} let {first: f, last: l} = obj ``` 可以嵌套賦值,可以指定默認值 ```js let obj = {} let arr = {} ({foo:obj.prop, bar:arr[0]} = {foo: 123, bar: true}) // 這里外面必須加個圓括號不然報錯… ``` ## 3、字符串的解構賦值 字符串被轉換為一個類似數組的對象 ```js const[a,b,c,d,e] = 'hello' // h e l l o ``` 規則:只要等號右邊的值不是對象或數組,就先將其轉為對象。 ## 4、函數參數的解構賦值 ```js function add([x, y]) { return x + y } console.log(add([1, 2])) ``` 上面的代碼中,函數 add 的參數表面上是一個數組,但在傳入參數的那一刻,數組參數就被解構成變量 x 和 y 。 ## 用途 1、提取 JSON 數據 ```js let jsonData = { id: 42, status: 'OK', data: [867, 5309] } let {id, status, data:number} = jsonData console.log(id, status, number) // 42 'OK' [867, 5309] ``` 2、從函數返回多個值 ```js function example1() { return [1, 2, 3] } let [a, b, c] = example1() function example2() { return { foo: 1, bar: 2 } } let {foo, bar} = example2() console.log(a, b, c) // 1 2 3 console.log(foo, bar) // 1 2 ``` 3、函數參數的定義 解構賦值可以方便地將一組參數與變量名對應起來 ```js // 參數是一組有序的值 function f([x, y, z]) { ... } f([1, 2, 3]) // 參數是一組無序的值 funtion f({x, y, z}) { ... } f({z: 3, y: 2, x: 1}) ``` 4、輸入模塊的指定方法 加載模塊時,往往需要指定輸入的方法。解構賦值使得輸入語句非常清晰 ```js const { SourceMapConsumer, SourceNode } = require("source-map") ``` # 函數的擴展 ## 參數默認值 - ES6 允許函數的參數指定為默認值,參數默認值是惰性求值的,即每次調用函數都會重新計算 - 參數變量的聲明是默認的,在函數體中不能用 let 或 const 再次聲明 - 函數的 length 屬性:返回 **沒有指定默認值** 的參數個數 ```js console.log(function (a, b, c = 5) {}.length); // 2 ``` >length 屬性統計的是函數預期傳入的參數個數,指定了默認值的參數以及 rest 參數都不會計入 length 屬性 - 作用域問題:一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域。等到初始化結束,這個作用域就會消失。這種語法在不設置參數默認值時是不會出現的。 - 可以將參數默認值設為 undefined,表示這個參數是可省略的 - 定義了默認值的參數應該是函數的 **尾參數**,否則這個參數實際上是無法省略的 ## rest 參數 形式:(…變量名),用于獲取函數的多余參數,這樣就不需要 arguments 對象了。rest 參數搭配的變量是一個數組。 >[warning]rest 參數只能是最后一個參數,否則會報錯 ```js // arguments 變量的寫法 function sortNumbers() { return Array.prototype.slice.call(arguments).sort() } // rest 參數的寫法 function sortNumbers = (...numbers) => numbers.sort() ``` ## 箭頭函數 ES6 允許使用箭頭(=>)定義函數,箭頭函數對于使用 function 關鍵字創建的函數有以下區別 - 箭頭函數沒有 arguments(建議使用更好的語法,剩余運算符替代) - 箭頭函數沒有 prototype 屬性,不能用作構造函數(不能用 new 關鍵字調用) - 箭頭函數沒有自己 this,箭頭函數的 this 始終等于它上層上下文中的 this - 不可以使用 yield 命令,因此箭頭函數不能用作 Generator 函數 基礎語法: ```js (參數1, 參數2, …, 參數N) => { 函數聲明 } // 相當于:(參數1, 參數2, …, 參數N) => { return 表達式; } (參數1, 參數2, …, 參數N) => 表達式(單一) // 當只有一個參數時,圓括號是可選的: (單一參數) => {函數聲明} 單一參數 => {函數聲明} // 沒有參數的函數應該寫成一對圓括號。 () => {函數聲明} // 加括號的函數體返回對象字面表達式: 參數 => ({foo: bar}) // 支持剩余參數和默認參數 (參數1, 參數2, ...rest) => {函數聲明} (參數1 = 默認值1,參數2, …, 參數N = 默認值N) => {函數聲明} ``` ```js let controller = { a: 1, makeRequest: function () { // 這里的 this 使用默認綁定規則,綁定到全局對象上(非嚴格模式),具體見 this 章節 setTimeout(function () { console.log(this.a) // undefined }) } } controller.makeRequest() // 使用箭頭函數解決上面這個問題 let controller2 = { a: 1, makeRequest: function () { setTimeout(() => { console.log(this.a) // 1 }) } } controller2.makeRequest() ``` ## 尾調用優化 函數調用自身稱為遞歸,如果尾調用自身就稱為尾遞歸。 遞歸非常耗費內存,因為需要同時保存成百上千個調用幀,而 ES6 的設計讓尾遞歸只存在一個調用幀。 ```js // 非尾遞歸的 Fibonacci function Fibonacci(n) { if (n <= 1) { return 1 } return Fibonacci(n - 1) + Fibonacci(n - 2) } Fibonacci(100) // 堆棧溢出 // 使用尾遞歸 function Fibonacci2(n, ac1 = 1, ac2 = 1) { if (n <= 1) { return ac2 } return Fibonacci2(n - 1, ac2, ac1 + ac2) } console.log(Fibonacci2(100)) ``` 尾遞歸階乘: ```js function factorial (n, acc = 1) { if (n === 1) return acc return factorial(n - 1, acc * n) } ``` # 對象的擴展 比較重要的是 Object.assign() 方法的使用 `Object.assign(target, source1, source2)`:第一參數是目標對象,后面的參數都是源對象,該方法用于將源對象所有 **可枚舉屬性** 復制到目標對象 注意以下幾點: - 如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性 - 復制的屬性是有限制的,只復制源對象的自身屬性(不復制繼承屬性),不復制不可枚舉屬性(enumerable: false) - 實行的是淺復制,而不是深復制。即如果源對象某個屬性的值是對象,那么目標對象復制得到的是這個對象的引用 - 如果同名屬性是對象,Object.assign 的處理方法是替換而不是添加 ```js var target = { a: { b: 'c', d: 'e' } } var source = { a: { b: 'hello' } } Object.assign(target, source) // { a: { b: 'hello' } } 而不是 { a: { b: 'hello', d: 'e' } } ``` 常見用途: **為對象添加屬性:** ```js class Point { constructor(x, y) { Object.assign(this, {x, y}) // 將 x 屬性和 y 屬性添加到了 Point 類的對象實例中 } } ``` **為對象添加方法** ```js Object.assign(SomeClass.prototype, { someMethod(arg1, arg2) { ... } }) // 直接將方法添加到 SomeClass.prototype 中 ``` **克隆對象** ```js function clone(origin) { return Object.assign({}, origin) } // 將原始對象復制到一個空對象;如果想要保持繼承鏈,可以采用以下代碼 function clone(origin) { let originProto = Object.getPrototypeOf(origin) return Object.assign(Object.create(originProto), origin) } ``` **合并多個對象** ```js const merge = (target, ...sources) => Object.assign(target, ...sources) // 如果希望合并后返回一個新對象,可以這么改寫 const merge = (...sources) => Object.assign({}, ...sources) ``` **為屬性指定默認值** ```js const DEFAULTS = { logLevel: 0, outputFormat: 'html' } function processContent(options) { options = Object.assign({}, DEFAULTS, options) console.log(options) } // DEFAULT對象是默認值,options對象是用戶提供的參數,如果兩者有同名屬性則options的屬性值會覆蓋DEFAULTS的屬性值 // 這么使用的前提是DEFAULTS對象和options對象的所有屬性的值只能是簡單類型 ``` # 擴展運算符的使用 這里總結下擴展運算符用的比較多的地方 1、將數組轉換為用逗號分隔的參數序列 ```js const arr = [] arr.push(...[1, 2, 3, 4, 5]) console.log(arr) // [1, 2, 3, 4, 5] ``` 2、某些場合可以替代函數的 apply 方法 ```js // ES5 的寫法 Math.max.apply(null, [14, 3, 77]) // ES6 的寫法 Math.max(...[14, 3, 77]) ``` 3、可用于將具有 iterator 接口的類似數組的對象轉換為真正的數組 ```js let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'] ]) let arr = [...map.keys()] // [1, 2, 3] ``` 4、用于對象的解構賦值:擴展運算符此時的作用相當于,將所有可遍歷的、但尚未被讀取的屬性(鍵值對)分配到指定的對象上面,注意這是淺復制 ```js let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 } x // 1 y // 2 z // { a: 3, b: 4 } ``` # 異步編程 ## Promise Promise 簡單來說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上來說,Promise 是一個對象,從它可以獲取異步操作的消息。 Promise 的提出是為了解決傳統異步編程的解決方案 - 回調函數和事件中回調地獄的問題。 比如下面這個串行讀取文件的例子: ```js fs.readFile(path1, function (err, data) { // read file1 fs.readFile(path2, function (err, data) { // read file2 fs.readFile(path3, function (err, data) { // read file3 // 更多的回調...... }) }) }) ``` 下面我們用 Promise 來實現相同的效果。 首先需要將 readFile 方法封裝為一個 Promise 對象 ```js function readFile_promise (path) { return new Promise((resolve, reject) => { fs.readFile(path, 'UTF-8', (err, data) => { if (data) { resolve(data) } else { reject(err) } }) }) } ``` 然后鏈式調用: ```js // 使用 Promise 的鏈式調用 readFile_promise('foo.txt').then(value => { // ... console.log(value) return readFile_promise('bar.txt') }).then(value => { // ... console.log(value) return readFile_promise('baz.txt') }).then(value => { // ... console.log(value) }) ``` 如果使用 async / await 語法則更直觀了: ```js async function readFile () { let result1 = await readFile_promise('foo.txt') // 返回的是該異步操作的結果 let result2 = await readFile_promise('bar.txt') let result3 = await readFile_promise('baz.txt') } ``` 下面簡單梳理下 Promise、Generator、async / await 的 API 和使用 1、三種狀態:Pending(進行中)、Fulfilled(已成功)、Rejected(已失敗) 2、構造函數:Promise() ```js let promise = new Promise(function (resolve, reject) { // ... some code if (/* 異步操作成功 */) { resolve(value) } else { reject (error) } }) ``` Promise 構造函數接受一個函數作為參數,該函數的兩個參數分別是 resolve 和 reject,其是兩個函數,由 JavaScript 引擎提供,不需要自己部署 - resolve 函數將 Promise 對象的狀態從 Pending 變為 Fulfilled (完成) - reject 函數則將 Pending 變為 Rejected 狀態 可以用 then 方法指定 Resolved 狀態(Fulfilled)和 Rejected 狀態的回調函數,通過參數接收 resolve 函數和 reject 函數傳出的值 簡單來說,是 Promise 的狀態變為 Resolved 時會觸發 then 方法綁定的回調函數(相當于事件監聽) ***** 3、`Promise.prototype.then()` Promise 的實例的 then 方法是定義在原型對象 Promise.prototype 上的,then 方法的第一個參數是 Resolved 狀態的回調函數,第二個參數(可選)是 Rejected 狀態的回調函數,then 方法返回的是一個新的 Promise 實例,因此可以采用鏈式寫法,如下 ```js getJSON('/posts.json').then(function(json) { return json.post }).then(function(post) { // ... }) ``` 前一個回調函數的返回結果會作為參數傳遞給下一個回調函數 ***** 4、`Promise.prototype.catch()` 其實這個方法是 .then(null, rejection) 的別名,使用這個方法可以更簡潔地指定發生錯誤時的回調函數 then 方法指定的回調函數如果在運行中拋出錯誤,也會被 catch 方法捕獲 ```js let promise = new Promise((resolve, reject) => { throw new Error('test') }) promise.catch(error => { console.log(error) // Error: test }) ``` 如果 Promise 狀態已經變成 Resolved,再拋出錯誤是無效的 ```js let promise = new Promise((resolve, reject) => { resolve('ok') throw new Error('test') }) promise.then(value => { console.log(value) // ok }).catch(error => { console.log(error) }) ``` Promise 對象的錯誤具有 “冒泡” 性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個 catch 語句捕獲 ```js getJSON('/post/1.json').then(post => { return getJSON(post.commentURL) }).then(comments => { // some code }).catch(error => { // 處理前面 3 個 Promise 產生的錯誤 }) ``` ***** 5、`Promise.all()` 該方法接收一個數組作為參數,數組元素都為 Promise 對象(或者是具有 Iterator 接口,且返回的每個成員都是 Promise 實例的對象) ```js var p = Promise.all([p1, p2, p3] ``` p 的狀態由 p1 p2 p3 決定,有兩種可能: ① 只有 p1 p2 p3 都變為 Fulfilled,p 才會變為 Fulfilled,此時p1 p2 p3 的返回值組成一個數組,傳遞給 p 的回調函數 ② 只要 p1 p2 p3 中有一個變為 Rejected,p 就變為 Rejected,此時第一個被 Rejected 的實例的返回值會傳遞給 p 的回調函數 ***** 6、`Promise.race()` 與 all 類似,接受一個 Promise 對象組成的數組作為參數,只要其中有一個 Promise 對象率先變為 Resolved 狀態那么 p 的狀態就跟著改變,率先改變的 Promise 實例的返回值會被傳遞給 p 的回調函數 7、`Promise.resolve()` 將一個現有的對象轉為 Promise 對象 8、`finally()` 不管狀態如何改變最后都會執行 finally 指定的回調方法 ## Generator 特征:function 命令與函數名之間有一個星號,函數體內部使用 yield 語句定義不同的內狀態。 Generator 函數實際上是利用了遍歷器對象(Iterator Object),當執行一個 Generator 函數時,會有一個指向內部狀態的指針對象,調用遍歷器對象的 next 方法會使指針移動到下一個狀態,next()方法返回一個對象,value 屬性是當前 yield 語句的值, done 屬性是一個布爾值表示遍歷是否結束。 <span style="font-family: 楷體; font-size: 18px; font-weight: bold;">yield* 表達式</span> 如果在 Generator 函數內部調用另一個 Generator 函數,默認情況下是沒有效果的。 ```js function* foo () { yield 'a' yield 'b' } function* bar () { yield 'x' foo() yield 'y' } for (let v of bar()) { // Generator 函數返回 iterator 對象,因此可以用 for...of 遍歷 console.log(v) } // x // y // yield* 語句用來在一個 Generator 函數里面執行另一個 Generator 函數 function* bar2 () { yield 'x' yield* foo() yield 'y' } for (let v of bar2()) { console.log(v) } // x // a // b // y ``` 從語法角度看,如果 yield 命令后跟的是一個遍歷器對象,那么需要在 yield 命令后加上星號,表明返回的是一個遍歷器對象。這被稱為 yield* 語句。 <span style="font-family: 楷體; font-size: 18px; font-weight: bold;">co 模塊</span> co 模塊用于 Generator 函數的自動執行 ```js var gen = function* () { var f1 = yield readFile('...') var f2 = yield readFile('...') } var co = require('co') co(gen) ``` <span style="font-family: 楷體; font-size: 18px; font-weight: bold;">應用:使用 Generator 函數部署 iterator 接口</span> ```js function* iterEntries (obj) { let keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { let key = keys[i] yield [key, obj[key]] } } let myObj = { foo: 3, bar: 7 } for (let [key, value] of iterEntries(myObj)) { console.log(key, value) } // foo 3 // bar 7 ``` 上述代碼中,myObj 是一個普通對象,通過 iterEntries 函數就有了 Iterator 接口。 ## async await async await 其實就相當于 Generator 函數的語法糖 其相比于 Generator 的改進如下: ① 內置執行器,Generator 函數的執行必須依靠執行器,async 函數自帶執行器,與普通函數一樣調用即可 ② 更好的語義,async 表示函數里有異步操作,await 表示緊跟在后面的表達式需要等待 ③ 更廣的適用性,await 命令后面可以是 Promise 對象和原始類型的值(如果不是 Promise 對象會被轉換為一個 Promise 對象并立即 resolve) ④ 返回值是 Promise,async 函數返回一個 Promise 對象,可以用 then 方法指定下一步操作 例子:按順序完成異步操作(依次遠程讀取一組 URL,然后按照讀取的順序輸出結果) ```js // 繼發寫法,只有前一個 URL 返回結果后才會去讀取下一個 URL async function logInOrder (urls) { for (const url of urls) { const response = await fetch(url) console.log(await response.text()) } } // 并發寫法,同時發出遠程請求 async function logInOrder (urls) { const textPromises = urls.map(async url => { const response = await fetch(url) return response.text() }) // 按次序輸出 for (const textPromise of textPromises) { console.log(await textPromise) } } ``` # Proxy 與 Reflect `var proxy = new Proxy(target, handler)`:生成一個 Proxy 實例,target 參數表示所要攔截的目標對象,handler 參數也是一個對象,用來定值攔截行為; 對于可以設置但沒有設置攔截的操作,則直接落在目標對象上,按照原先的方式產生結果 Proxy 支持的攔截操作:參數 propKey 即要操作的對象的屬性名 - get(target, propKey, receiver):攔截對象屬性的讀取 - set(target, propKey, value, receiver):攔截對象屬性的設置 - has(target, propKey):攔截 propKey in proxy的操作,返回一個布爾值 - deleteProperty(target, propKey):攔截 delete proxy[propKey] 的操作,返回一個布爾值 ....... 詳細的 API 請閱讀 [ES6 標準入門](http://es6.ruanyifeng.com/#docs/proxy) 就記一下與 defineProperty 的區別:當使用 defineProperty,我們修改原來的 obj 對象就可以觸發攔截,而使用 proxy,就必須修改代理對象,即 Proxy 的實例才可以觸發攔截。另外,Proxy 的可攔截屬性更多。 [https://github.com/mqyqingfeng/Blog/issues/107](https://github.com/mqyqingfeng/Blog/issues/107) ``` // 利用 Proxy 攔截構造函數的執行方法來實現單例模式 function proxy(func) { let instance let handler = { // Proxy 構造函數的第二個參數是一個對象,定制攔截的行為 construct(target, args) { if (!instance) { instance = Reflect.construct(func, args) } return instance } } return new Proxy(func, handler) // 攔截構造函數 } ``` ## Reflect Reflect 對象的設計目的: - 將 Object 對象的一些明顯屬于語言內部的方法放到 Reflect 對象上,如 Object.defineProperty - 修改某些 Object 方法的返回結果,讓其變得更為合理。比如 Object.defineProperty(obj, name, desc) 在無法定義屬性時會拋出一個錯誤,而 Reflect.defineProperty(obj, name, desc) 則會返回 false - 讓 Object 操作都變成函數行為。如 name in obj 和 delete obj[name] 改為 Reflect.has(obj, name) 和 Reflect.deleteProperty(obj, name) - Reflect 對象的方法與 Proxy 對象的方法一一對應,只要是 Proxy 對象的方法,就能在 Reflect 對象上找到對應的方法 ``` var obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}`) return Reflect.get(target, key, receiver) }, set: function (target, key, value, receiver) { console.log(`setting ${key}`) return Reflect.set(target, key, value, receiver) } }) obj.count = 1 // setting count console.log(++obj.count) // getting count // setting count // 2 ``` # iterator 和 for...of 循環 ## 迭代器 所謂迭代器,其實就是一個具有 next() 方法的對象,每次調用 next() 都會返回一個結果對象,該結果對象有兩個屬性,value 表示當前的值,done 表示遍歷是否結束。 我們直接用 ES5 的語法創建一個迭代器: ```js function createIterator(items) { var i = 0; return { next: function () { var done = i >= item.length; var value = !done ? items[i++] : undefined; return { done: done, value: value }; } }; } // iterator 就是一個迭代器對象 var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // { done: false, value: 1 } console.log(iterator.next()); // { done: false, value: 2 } console.log(iterator.next()); // { done: false, value: 3 } console.log(iterator.next()); // { done: true, value: undefined } ``` ## for of 除了迭代器之外,我們還需要一個可以遍歷迭代器對象的方式,ES6 提供了 for of 語句,我們直接用 for of 遍歷一下我們上節生成的遍歷器對象試試: ```js var iterator = createIterator([1, 2, 3]); for (let value of iterator) { console.log(value); } ``` 結果報錯 `TypeError: iterator is not iterable`,表明我們生成的 iterator 對象并不是 iterable(可遍歷的),那什么才是可遍歷的呢? 其實一種數據結構只要部署了 Iterator 接口,我們就稱這種數據結構是“可遍歷的”(iterable)。 ES6 規定,默認的 Iterator 接口部署在數據結構的`Symbol.iterator`屬性,或者說,一個數據結構只要具有`Symbol.iterator`屬性,就可以認為是 "可遍歷的"(iterable)。 舉個例子: ```js const obj = { value: 1 }; for (value of obj) { console.log(value); } // TypeError: iterator is not iterable ``` 我們直接 for of 遍歷一個對象,會報錯,然而如果我們給該對象添加`Symbol.iterator`屬性: ```js const obj = { value: 1 }; obj[Symbol.iterator] = function () { return createIterator([1, 2, 3]); }; for (value of obj) { console.log(value); } // 1 // 2 // 3 ``` 由此,我們也可以發現 for of 遍歷的其實是對象的 Symbol.iterator 屬性 ## 默認有 iterator 接口的對象 * Array * Map * Set * String * TypedArray(類數組對象),如 arguments 對象,NodeList 對象 * Generator 對象 ## for...of 與 for...in - 對于數組的遍歷,for ... in 會返回數組中所有可枚舉的屬性(包括原型鏈上可枚舉的屬性), for ... of 只返回數組的下標對應的屬性值 - for...in 循環出的是 key,for...of 循環出的是 value,且 for...in 會遍歷對象的整個原型鏈,for...of 只遍歷當前對象 - for...of 不能循環普通的對象,需要通過和 Object.keys() 搭配使用,需要搭配具有 iterator 接口的對象使用,準確地說,for of 能遍歷數組(的值)是因為數組默認有 iterator 接口 ```js let myArray = [3, 5, 7, 9] myArray.name = '數組' // for in for of 應用于數組中的區別 for(let index in myArray) { console.log(index) // 0 1 2 3 name } for(let value of myArray) { console.log(value) // 3 5 7 9 } // 應用于對象中 let myObject = { property1: 'P1', property2: 'P2', property3: 'P3' } for(let value in myObject) { console.log(value) // property1 property2 property3 } for(let value of myObject) { console.log(value) // myObject is not iterable } // How to make myObejct iterable? kind of complex // Or we can use Object.keys() for(let key of Object.keys(myObject)) { // 使用Object.keys()方法獲取對象的key組成的數組 console.log(key) // property1 property2 property3 } function A() { this.property1 = 1 this.property2 = 2 this.property3 = 3 } A.prototype.speak = function() { console.log('speak') } let B = new A() for(let key in B) { console.log(key) // property1 property2 property3 speak } ```
                  <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>

                              哎呀哎呀视频在线观看