偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

面試官:你對(duì)Es6很了解?那實(shí)現(xiàn)一個(gè)Set吧

開發(fā) 架構(gòu)
es6新增了Set數(shù)據(jù)結(jié)構(gòu),它允許你存儲(chǔ)任何類型的唯一值,無(wú)論是原始值還是對(duì)象引用。這篇文章希望通過(guò)模擬實(shí)現(xiàn)一個(gè)Set來(lái)增加對(duì)它的理解。

[[440012]]

本文轉(zhuǎn)載自微信公眾號(hào)「前端胖頭魚」,作者前端胖頭魚。轉(zhuǎn)載本文請(qǐng)聯(lián)系前端胖頭魚公眾號(hào)。

前言

es6新增了Set數(shù)據(jù)結(jié)構(gòu),它允許你存儲(chǔ)任何類型的唯一值,無(wú)論是原始值還是對(duì)象引用。這篇文章希望通過(guò)模擬實(shí)現(xiàn)一個(gè)Set來(lái)增加對(duì)它的理解。

用在前面

實(shí)際工作和學(xué)習(xí)過(guò)程中,你可能也經(jīng)常用Set來(lái)對(duì)數(shù)組做去重處理

  1. let unique = (array) => { 
  2.   return [ ...new Set(array) ] 
  3.  
  4. console.log(unique([ 1, 2, 3, 4, 1, 2, 5 ])) // [1, 2, 3, 4, 5] 

基本語(yǔ)法

以下內(nèi)容基本出自MDN,這里寫出來(lái),純粹是為了便于后面的模擬操作。如果你已經(jīng)很熟悉了,可以直接略過(guò)。

  1. new Set([ iterable ]) 

可以傳遞一個(gè)可迭代對(duì)象,它的所有元素將被添加到新的 Set中。如果不指定此參數(shù)或其值為null,則新的 Set為空。

  1. let s = new Set([ 1, 2, 3 ]) // Set(3) {1, 2, 3} 
  2. let s2 = new Set() // Set(0) {} 
  3. let s3 = new Set(null /* or undefined */) // Set(0) {} 

實(shí)例屬性和方法

屬性

constructor Set的構(gòu)造函數(shù)

size Set 長(zhǎng)度

操作方法

Set.prototype.add(value) 在Set對(duì)象尾部添加一個(gè)元素。返回該Set對(duì)象。

Set.prototype.has(value) 返回一個(gè)布爾值,表示該值在Set中存在與否。

Set.prototype.delete(value) 移除Set中與這個(gè)值相等的元素,返回Set.prototype.has(value)在這個(gè)操作前會(huì)返回的值(即如果該元素存在,返回true,否則返回false)

Set.prototype.clear() 移除Set對(duì)象內(nèi)的所有元素。沒(méi)有返回值

栗子

  1. let s = new Set() 
  2.  
  3. s.add(1) // Set(1) {1} 
  4.   .add(2) // Set(2) {1, 2} 
  5.   .add(NaN) // Set(2) {1, 2, NaN} 
  6.   .add(NaN) // Set(2) {1, 2, NaN} 
  7.  
  8. // 注意這里因?yàn)樘砑油暝刂蠓祷氐氖窃?span id="5gk0f71da" class="keyword">Set對(duì)象,所以可以鏈?zhǔn)秸{(diào)用 
  9. // NaN === NaN 結(jié)果是false,但是Set中只會(huì)存一個(gè)NaN 
  10.  
  11. s.has(1) // true 
  12. s.has(NaN) // true 
  13.  
  14. s.size // 3 
  15.  
  16. s.delete(1) 
  17. s.has(1) // false 
  18. s.size // 2 
  19.  
  20. s.clear() 
  21.  
  22. s // Set(0) {} 

遍歷方法

Set.prototype.keys() 返回一個(gè)新的迭代器對(duì)象,該對(duì)象包含Set對(duì)象中的按插入順序排列的所有元素的值。

Set.prototype.values() 返回一個(gè)新的迭代器對(duì)象,該對(duì)象包含Set對(duì)象中的按插入順序排列的所有元素的值。

Set.prototype.entries() 返回一個(gè)新的迭代器對(duì)象,該對(duì)象包含Set對(duì)象中的按插入順序排列的所有元素的值的[value, value]數(shù)組。為了使這個(gè)方法和Map對(duì)象保持相似, 每個(gè)值的鍵和值相等。

Set.prototype.forEach(callbackFn[, thisArg]) 按照插入順序,為Set對(duì)象中的每一個(gè)值調(diào)用一次callBackFn。如果提供了thisArg參數(shù),回調(diào)中的this會(huì)是這個(gè)參數(shù)。

栗子

  1. let s = new Set([ 's''e''t' ]) 
  2.  
  3. s // SetIterator {"s""e""t"
  4. s.keys() // SetIterator {"s""e""t"
  5. s.values() // SetIterator {"s""e""t"
  6. s.entries() // SetIterator {"s""e""t"
  7.  
  8. // log 
  9. [ ...s ] // ["s""e""t"
  10. [ ...s.keys() ] //  ["s""e""t"
  11. [ ...s.values() ] //  ["s""e""t"
  12. [ ...s.entries() ] //  [["s""s"], ["e""e"], ["t""t"]] 
  13.  
  14. s.forEach(function (value, keyset) { 
  15.   console.log(value, keyset, this) 
  16. }) 
  17.  
  18. // s s Set(3) {"s""e""t"} Window 
  19. // e e Set(3) {"s""e""t"} Window 
  20. // t t Set(3) {"s""e""t"} Window 
  21.  
  22. s.forEach(function () { 
  23.   console.log(this) 
  24. }, { name'qianlongo' }) 
  25.  
  26. // {name"qianlongo"
  27. // {name"qianlongo"
  28. // {name"qianlongo"
  29.  
  30. for (let value of s) { 
  31.   console.log(value) 
  32. // s 
  33. // e 
  34. // t 
  35.  
  36. for (let value of s.entries()) { 
  37.   console.log(value) 
  38. // ["s""s"
  39. // ["e""e"
  40. // ["t""t"

整體結(jié)構(gòu)

以上回顧了一下Set的基本使用,我們可以開始嘗試模擬實(shí)現(xiàn)一把啦。你也可以直接點(diǎn)擊查看源碼。

目錄結(jié)構(gòu)

├──set-polyfill │ ├──iterator.js // 導(dǎo)出一個(gè)構(gòu)造函數(shù)Iterator,模擬創(chuàng)建可迭代對(duì)象 │ ├──set.js // Set類 │ ├──utils.js // 輔助函數(shù) │ ├──test.js // 測(cè)試

Set整體框架

  1. class Set { 
  2.  
  3.   constructor (iterable) {} 
  4.  
  5.   get size () {} 
  6.  
  7.   has () {} 
  8.  
  9.   add () {} 
  10.  
  11.   delete () {}   
  12.  
  13.   clear () {} 
  14.  
  15.   forEach () {} 
  16.  
  17.   keys () {} 
  18.  
  19.   values () {}   
  20.  
  21.   entries () {} 
  22.  
  23.   [ Symbol.iterator ] () {} 

輔助方法

開始實(shí)現(xiàn)Set細(xì)節(jié)前,我們先看一下會(huì)用到的一些輔助方法

assert, 這個(gè)方法是學(xué)習(xí)vuex源碼時(shí)候看到的,感覺(jué)蠻實(shí)用的,主要用來(lái)對(duì)某些條件進(jìn)行判斷,拋出錯(cuò)誤。

  1. const assert = (condition, msg) => { 
  2.   if (!condition) throw new Error(msg) 

isDef, 過(guò)濾掉null和undefined

  1. const isDef = (value) => { 
  2.   return value != void 0 

isIterable, 簡(jiǎn)單判斷value是否是迭代器對(duì)象.

  1. const isIterable = (value) => { 
  2.   return isDef(value) && typeof value[ Symbol.iterator ] === 'function' 

forOf, 模擬for of行為, 對(duì)迭代器對(duì)象進(jìn)行遍歷操作。

  1. const forOf = (iterable, callback, ctx) => { 
  2.   let result 
  3.  
  4.   iterable = iterable[ Symbol.iterator ]() 
  5.   result = iterable.next() 
  6.  
  7.   while (!result.done) { 
  8.     callback.call(ctx, result.value) 
  9.     result = iterable.next() 
  10.   } 

源碼實(shí)現(xiàn)

  1. class Set { 
  2.   constructor (iterable) { 
  3.     // 使用數(shù)組來(lái)存儲(chǔ)Set的每一項(xiàng)元素 
  4.     this.value = [] 
  5.     // 判斷是否使用new調(diào)用 
  6.     assert(this instanceof Set'Constructor Set requires "new"'
  7.     // 過(guò)濾掉null和undefined 
  8.     if (isDef(iterable)) { 
  9.       // 是可迭代對(duì)象才進(jìn)行下一步forOf元素添加 
  10.       assert(isIterable(iterable), `${iterable} is not iterable`) 
  11.       // 循環(huán)可迭代對(duì)象,初始化 
  12.       forOf(iterable, (value) => { 
  13.         this.add(value) 
  14.       }) 
  15.     } 
  16.   } 
  17.   // 獲取s.size時(shí)候會(huì)調(diào)用 size函數(shù),返回value數(shù)組的長(zhǎng)度 
  18.   // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/get 
  19.   get size () { 
  20.     return this.value.length 
  21.   } 
  22.   // 使用數(shù)組的includes方法判斷是否包含value 
  23.   // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes 
  24.   // [ NaN ].includes(NaN)會(huì)返回true,正好Set也只能存一個(gè)NaN 
  25.   has (value) { 
  26.     return this.value.includes(value) 
  27.   } 
  28.   // 通過(guò)has方法判斷value是否存在,不存在則添加進(jìn)數(shù)組,最后返回Set本身,支持鏈?zhǔn)秸{(diào)用 
  29.   add (value) { 
  30.     if (!this.has(value)) { 
  31.       this.value.push(value) 
  32.     } 
  33.  
  34.     return this 
  35.   } 
  36.   // 在刪除之前先判斷value是否存在用之當(dāng)做返回值,存在則通過(guò)splice方法移除 
  37.   delete (value) { 
  38.     let result = this.has(value) 
  39.  
  40.     if (result) { 
  41.       this.value.splice(this.value.indexOf(value), 1) 
  42.     } 
  43.  
  44.     return result 
  45.   } 
  46.   // 重新賦值一個(gè)空數(shù)組,即實(shí)現(xiàn)clear方法 
  47.   clear () { 
  48.     this.value = [] 
  49.   } 
  50.   // 通過(guò)forOf遍歷 values返回的迭代對(duì)象,實(shí)現(xiàn)forEach 
  51.   forEach (callback, thisArg) { 
  52.     forOf(this.values(), (value) => { 
  53.       callback.call(thisArg, value, value, this) 
  54.     }) 
  55.   } 
  56.   // 返回一個(gè)迭代對(duì)象,該對(duì)象中的值是Set中的value 
  57.   keys () { 
  58.     return new Iterator(this.value) 
  59.   } 
  60.   // 同keys 
  61.   values () { 
  62.     return this.keys() 
  63.   } 
  64.   // 返回一個(gè)迭代對(duì)象,不同keys和values的是其值是[value, value] 
  65.   entries () { 
  66.     return new Iterator(this.value, (value) => [ value, value ]) 
  67.   } 
  68.   // 返回一個(gè)新的迭代器對(duì)象,該對(duì)象包含Set對(duì)象中的按插入順序排列的所有元素的值。 
  69.   [ Symbol.iterator ] () { 
  70.     return this.values() 
  71.   } 

測(cè)試一把

執(zhí)行 node test.js

size屬性和操作方法

  1. const Set = require('./set'
  2. const s = new Set() 
  3.  
  4. s.add(1) 
  5.   .add(2) 
  6.   .add(NaN) 
  7.   .add(NaN) 
  8.  
  9. console.log(s)  // Set { value: [ 1, 2, NaN ] } 
  10. console.log(s.has(1)) // true 
  11. console.log(s.has(NaN)) // true 
  12. console.log(s.size) // 3 
  13.  
  14. s.delete(1) 
  15.  
  16. console.log(s.has(1)) // false 
  17. console.log(s.size) // 2 
  18.  
  19. s.clear() 
  20.  
  21. console.log(s) // Set { value: [] } 

上面的例子把Set的size屬性和操作方法過(guò)了一遍,打印出來(lái)的Set實(shí)例和原生的長(zhǎng)得不太一樣,就先不管了。

遍歷方法

  1. let s2 = new Set([ 's''e''t' ]) 
  2.  
  3. console.log(s2) // Set { value: [ 's''e''t' ] } 
  4. console.log(s2.keys()) // Iterator {} 
  5. console.log(s2.values()) //  Iterator {} 
  6. console.log(s2.entries()) //  Iterator {} 
  7.  
  8. console.log([ ...s2 ]) // [ 's''e''t' ] 
  9. console.log([ ...s2.keys() ]) // [ 's''e''t' ] 
  10. console.log([ ...s2.values() ]) // [ 's''e''t' ] 
  11. console.log([ ...s2.entries() ]) // [ [ 's''s' ], [ 'e''e' ], [ 't''t' ] ] 
  12.  
  13. s2.forEach(function (value, keyset) { 
  14.   console.log(value, keyset, this) 
  15. }) 
  16.  
  17. // s s Set { value: [ 's''e''t' ] } global 
  18. // e e Set { value: [ 's''e''t' ] } global 
  19. // t t Set { value: [ 's''e''t' ] } global 
  20.  
  21. s2.forEach(function () { 
  22.   console.log(this) 
  23. }, { name'qianlongo' }) 
  24.  
  25. // { name'qianlongo' } 
  26. // { name'qianlongo' } 
  27. // { name'qianlongo' } 
  28.  
  29. // {name"qianlongo"
  30. // {name"qianlongo"
  31. // {name"qianlongo"
  32.  
  33. for (let value of s) { 
  34.   console.log(value) 
  35. // s 
  36. // e 
  37. // t 
  38.  
  39. for (let value of s.entries()) { 
  40.   console.log(value) 
  41. // ["s""s"
  42. // ["e""e"
  43. // ["t""t"

遍歷方法看起來(lái)也可以達(dá)到和前面例子一樣的效果,源碼實(shí)現(xiàn)部分基本就到這里啦,但是還沒(méi)完...

  • 為什么[ ...s2 ]可以得到數(shù)組[ 's', 'e', 't' ]呢?
  • s2 為什么可以被for of循環(huán)呢?

iterator(迭代器)

從MDN找來(lái)這段話,在JavaScript中迭代器是一個(gè)對(duì)象,它提供了一個(gè)next() 方法,用來(lái)返回序列中的下一項(xiàng)。這個(gè)方法返回包含兩個(gè)屬性:done(表示遍歷是否結(jié)束)和 value(當(dāng)前的值)。

迭代器對(duì)象一旦被創(chuàng)建,就可以反復(fù)調(diào)用next()。

  1. function makeIterator(array){ 
  2.   var nextIndex = 0 
  3.  
  4.   return { 
  5.     nextfunction () { 
  6.       return nextIndex < array.length ? 
  7.         { done: false, value: array[ nextIndex++ ] } : 
  8.         { done: true, value: undefined } 
  9.     } 
  10.   }; 
  11.  
  12. var it = makeIterator(['yo''ya']) 
  13.  
  14. console.log(it.next()) // { done: false, value: "yo" } 
  15. console.log(it.next()) // { done: false, value: "ya" } 
  16. console.log(it.next()) // { done: true, value: undefined } 

這個(gè)時(shí)候可以講一下我們的iterator.js中的代碼了

  1. class Iterator { 
  2.   constructor (arrayLike, iteratee = (value) => value) { 
  3.     this.value = Array.from(arrayLike) 
  4.     this.nextIndex = 0 
  5.     this.len = this.value.length 
  6.     this.iteratee = iteratee 
  7.   } 
  8.  
  9.   next () { 
  10.     let done = this.nextIndex >= this.len 
  11.     let value = done ? undefined : this.iteratee(this.value[ this.nextIndex++ ]) 
  12.  
  13.     return { done, value } 
  14.   } 
  15.  
  16.   [ Symbol.iterator ] () { 
  17.     return this 
  18.   } 

Iterator的實(shí)例有一個(gè)next方法,每次調(diào)用都會(huì)返回一個(gè)done屬性和value屬性,其語(yǔ)意和前面的解釋是一樣的。

  1. let it = new Iterator(['yo''ya']) 
  2.  
  3. console.log(it.next()) // { done: false, value: "yo" } 
  4. console.log(it.next()) // { done: false, value: "ya" } 
  5. console.log(it.next()) // { done: true, value: undefined } 

看到這里你可能已經(jīng)知道了,Iterator要實(shí)現(xiàn)的功能之一就是提供一個(gè)迭代器。那這個(gè)又和上面的問(wèn)題1和2有啥關(guān)系呢?我們?cè)賮?lái)看看for of

for of

一個(gè)數(shù)據(jù)結(jié)構(gòu)只要部署了Symbol.iterator屬性,就被視為具有iterator接口,就可以用for...of循環(huán)遍歷它的成員。也就是說(shuō),for...of循環(huán)內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator方法 for...of 循環(huán)

默認(rèn)只有(Array,Map,Set,String,TypedArray,arguments)可被for of迭代。我們自定義的Set類不在這其中,前面的例子中卻在for of循環(huán)中打印出了想要的值。原因就是我們給Iterator類部署了Symbol.iterator方法,執(zhí)行該方法便返回Iterator實(shí)例本身,它是一個(gè)可以被迭代的對(duì)象。

  1. [ Symbol.iterator ] () { 
  2.   return this 

到這里上面的問(wèn)題2就可以解釋通了。

再看看問(wèn)題1為什么[ ...s2 ]可以得到數(shù)組[ 's', 'e', 't' ]呢?,原因也是我們給Set、keys、values、entries部署了Symbol.iterator,使之具有“iterator”接口,而擴(kuò)展運(yùn)算符...的特點(diǎn)之一就是任何具有Iterator接口的對(duì)象,都可以用擴(kuò)展運(yùn)算符轉(zhuǎn)為真正的數(shù)組。

結(jié)尾

模擬過(guò)程中可能會(huì)有相應(yīng)的錯(cuò)誤,也不是和原生的實(shí)現(xiàn)完全一致。僅當(dāng)學(xué)習(xí)之用,歡迎大家拍磚。

參考

  • Set
  • 迭代器和生成器
  • ES6 系列之模擬實(shí)現(xiàn)一個(gè) Set 數(shù)據(jù)結(jié)構(gòu)
  • 展開語(yǔ)法
  •  for...of 循環(huán)

 

責(zé)任編輯:武曉燕 來(lái)源: 前端胖頭魚
相關(guān)推薦

2022-03-21 09:05:18

volatileCPUJava

2024-09-09 08:30:56

代碼

2020-07-03 14:19:01

Kafka日志存儲(chǔ)

2024-05-28 10:14:31

JavaScrip模板引擎

2024-01-15 06:42:00

高并發(fā)熱點(diǎn)賬戶數(shù)據(jù)庫(kù)

2022-04-08 08:26:03

JavaHTTP請(qǐng)求

2021-09-28 13:42:55

Chrome Devwebsocket網(wǎng)絡(luò)協(xié)議

2020-06-19 15:32:56

HashMap面試代碼

2021-11-25 10:18:42

RESTfulJava互聯(lián)網(wǎng)

2021-08-09 07:47:40

Git面試版本

2025-01-13 09:24:32

2019-12-25 11:22:19

負(fù)載均衡集群算法

2020-02-18 15:15:05

Handler消息隊(duì)列執(zhí)行任務(wù)

2019-07-15 16:35:43

MySQL索引阿里

2015-08-13 10:29:12

面試面試官

2020-02-28 15:42:26

AOPJDKCGLib

2020-12-01 08:47:36

Java異常開發(fā)

2025-02-21 15:25:54

虛擬線程輕量級(jí)

2024-09-27 15:43:52

零拷貝DMAIO

2020-06-12 15:50:56

options前端服務(wù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)