成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

從零到有模擬實現(xiàn)一個Set類

PAMPANG / 1735人閱讀

摘要:過濾掉和簡單判斷是否是迭代器對象模擬行為對迭代器對象進(jìn)行遍歷操作。看到這里你可能已經(jīng)知道了,要實現(xiàn)的功能之一就是提供一個迭代器。原文鏈接參考迭代器和生成器系列之模擬實現(xiàn)一個數(shù)據(jù)結(jié)構(gòu)展開語法循環(huán)

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

原文鏈接

用在前面
實際工作和學(xué)習(xí)過程中,你可能也經(jīng)常用Set來對數(shù)組做去重處理
let unique = (array) => {
  return [ ...new Set(array) ]
}

console.log(unique([ 1, 2, 3, 4, 1, 2, 5 ])) // [1, 2, 3, 4, 5]
基本語法
以下內(nèi)容基本出自MDN,這里寫出來,純粹是為了便于后面的模擬操作。如果你已經(jīng)很熟悉了,可以直接略過。
new Set([ iterable ])

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

let s = new Set([ 1, 2, 3 ]) // Set(3)?{1, 2, 3}
let s2 = new Set() // Set(0)?{}
let s3 = new Set(null /* or undefined */) // Set(0)?{}
實例屬性和方法

屬性

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

size Set 長度

操作方法

Set.prototype.add(value)

在Set對象尾部添加一個元素。返回該Set對象。

Set.prototype.has(value)

返回一個布爾值,表示該值在Set中存在與否。

Set.prototype.delete(value)

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

Set.prototype.clear()

移除Set對象內(nèi)的所有元素。沒有返回值

栗子

let s = new Set()

s.add(1) // Set(1) {1}
  .add(2) // Set(2) {1, 2}
  .add(NaN) // Set(2) {1, 2, NaN}
  .add(NaN) // Set(2) {1, 2, NaN}

// 注意這里因為添加完元素之后返回的是該Set對象,所以可以鏈?zhǔn)秸{(diào)用
// NaN === NaN 結(jié)果是false,但是Set中只會存一個NaN

s.has(1) // true
s.has(NaN) // true

s.size // 3

s.delete(1)
s.has(1) // false
s.size // 2

s.clear()

s // Set(0)?{}

遍歷方法

Set.prototype.keys()

返回一個新的迭代器對象,該對象包含Set對象中的按插入順序排列的所有元素的值。

Set.prototype.values()

返回一個新的迭代器對象,該對象包含Set對象中的按插入順序排列的所有元素的值。

Set.prototype.entries()

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

Set.prototype.forEach(callbackFn[, thisArg])

按照插入順序,為Set對象中的每一個值調(diào)用一次callBackFn。如果提供了thisArg參數(shù),回調(diào)中的this會是這個參數(shù)。

栗子

let s = new Set([ "s", "e", "t" ])

s // SetIterator?{"s", "e", "t"}
s.keys() // SetIterator?{"s", "e", "t"}
s.values() // SetIterator?{"s", "e", "t"}
s.entries() // SetIterator?{"s", "e", "t"}

// log
[ ...s ] // ["s", "e", "t"]
[ ...s.keys() ] // ?["s", "e", "t"]
[ ...s.values() ] // ?["s", "e", "t"]
[ ...s.entries() ] // ?[["s", "s"], ["e", "e"], ["t", "t"]]

s.forEach(function (value, key, set) {
  console.log(value, key, set, this)
})

// s s Set(3)?{"s", "e", "t"} Window
// e e Set(3)?{"s", "e", "t"} Window
// t t Set(3)?{"s", "e", "t"} Window

s.forEach(function () {
  console.log(this)
}, { name: "qianlongo" })

// {name: "qianlongo"}
// {name: "qianlongo"}
// {name: "qianlongo"}

for (let value of s) {
  console.log(value)
}
// s
// e
// t

for (let value of s.entries()) {
  console.log(value)
}
// ["s", "s"]
// ["e", "e"]
// ["t", "t"]
整體結(jié)構(gòu)
以上回顧了一下Set的基本使用,我們可以開始嘗試模擬實現(xiàn)一把啦。你也可以直接點擊查看源碼。

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

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

Set整體框架

class Set {

  constructor (iterable) {}

  get size () {}

  has () {}

  add () {}

  delete () {}  

  clear () {}

  forEach () {}

  keys () {}

  values () {}  

  entries () {}

  [ Symbol.iterator ] () {}
}

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

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

const assert = (condition, msg) => {
  if (!condition) throw new Error(msg)
}

isDef, 過濾掉nullundefined

const isDef = (value) => {
  return value != void 0
}

isIterable, 簡單判斷value是否是迭代器對象.

const isIterable = (value) => {
  return isDef(value) && typeof value[ Symbol.iterator ] === "function"
}

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

const forOf = (iterable, callback, ctx) => {
  let result

  iterable = iterable[ Symbol.iterator ]()
  result = iterable.next()

  while (!result.done) {
    callback.call(ctx, result.value)
    result = iterable.next()
  }
}
源碼實現(xiàn)
class Set {
  constructor (iterable) {
    // 使用數(shù)組來存儲Set的每一項元素
    this.value = []
    // 判斷是否使用new調(diào)用
    assert(this instanceof Set, "Constructor Set requires "new"")
    // 過濾掉null和undefined
    if (isDef(iterable)) {
      // 是可迭代對象才進(jìn)行下一步forOf元素添加
      assert(isIterable(iterable), `${iterable} is not iterable`)
      // 循環(huán)可迭代對象,初始化
      forOf(iterable, (value) => {
        this.add(value)
      })
    }
  }
  // 獲取s.size時候會調(diào)用 size函數(shù),返回value數(shù)組的長度
  // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/get
  get size () {
    return this.value.length
  }
  // 使用數(shù)組的includes方法判斷是否包含value
  // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
  // [ NaN ].includes(NaN)會返回true,正好Set也只能存一個NaN
  has (value) {
    return this.value.includes(value)
  }
  // 通過has方法判斷value是否存在,不存在則添加進(jìn)數(shù)組,最后返回Set本身,支持鏈?zhǔn)秸{(diào)用
  add (value) {
    if (!this.has(value)) {
      this.value.push(value)
    }

    return this
  }
  // 在刪除之前先判斷value是否存在用之當(dāng)做返回值,存在則通過splice方法移除
  delete (value) {
    let result = this.has(value)

    if (result) {
      this.value.splice(this.value.indexOf(value), 1)
    }

    return result
  }
  // 重新賦值一個空數(shù)組,即實現(xiàn)clear方法
  clear () {
    this.value = []
  }
  // 通過forOf遍歷 values返回的迭代對象,實現(xiàn)forEach
  forEach (callback, thisArg) {
    forOf(this.values(), (value) => {
      callback.call(thisArg, value, value, this)
    })
  }
  // 返回一個迭代對象,該對象中的值是Set中的value
  keys () {
    return new Iterator(this.value)
  }
  // 同keys
  values () {
    return this.keys()
  }
  // 返回一個迭代對象,不同keys和values的是其值是[value, value]
  entries () {
    return new Iterator(this.value, (value) => [ value, value ])
  }
  // 返回一個新的迭代器對象,該對象包含Set對象中的按插入順序排列的所有元素的值。
  [ Symbol.iterator ] () {
    return this.values()
  }
}

測試一把

執(zhí)行 node test.js

size屬性和操作方法

const Set = require("./set")
const s = new Set()

s.add(1)
  .add(2)
  .add(NaN)
  .add(NaN)

console.log(s)  // Set { value: [ 1, 2, NaN ] }
console.log(s.has(1)) // true
console.log(s.has(NaN)) // true
console.log(s.size) // 3

s.delete(1)

console.log(s.has(1)) // false
console.log(s.size) // 2

s.clear()

console.log(s) // Set { value: [] }

上面的例子把Set的size屬性和操作方法過了一遍,打印出來的Set實例和原生的長得不太一樣,就先不管了。

遍歷方法

let s2 = new Set([ "s", "e", "t" ])

console.log(s2) // Set { value: [ "s", "e", "t" ] }
console.log(s2.keys()) // Iterator {}
console.log(s2.values()) //  Iterator {}
console.log(s2.entries()) //  Iterator {}

console.log([ ...s2 ]) // [ "s", "e", "t" ]
console.log([ ...s2.keys() ]) // [ "s", "e", "t" ]
console.log([ ...s2.values() ]) // [ "s", "e", "t" ]
console.log([ ...s2.entries() ]) // [ [ "s", "s" ], [ "e", "e" ], [ "t", "t" ] ]

s2.forEach(function (value, key, set) {
  console.log(value, key, set, this)
})

// s s Set { value: [ "s", "e", "t" ] } global
// e e Set { value: [ "s", "e", "t" ] } global
// t t Set { value: [ "s", "e", "t" ] } global

s2.forEach(function () {
  console.log(this)
}, { name: "qianlongo" })

// { name: "qianlongo" }
// { name: "qianlongo" }
// { name: "qianlongo" }

// {name: "qianlongo"}
// {name: "qianlongo"}
// {name: "qianlongo"}

for (let value of s) {
  console.log(value)
}
// s
// e
// t

for (let value of s.entries()) {
  console.log(value)
}
// ["s", "s"]
// ["e", "e"]
// ["t", "t"]

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

為什么[ ...s2 ]可以得到數(shù)組[ "s", "e", "t" ]呢?

s2 為什么可以被for of循環(huán)呢?

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

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

function makeIterator(array){
  var nextIndex = 0

  return {
    next: function () {
      return nextIndex < array.length ?
        { done: false, value: array[ nextIndex++ ] } :
        { done: true, value: undefined }
    }
  };
}

var it = makeIterator(["yo", "ya"])

console.log(it.next()) // { done: false, value: "yo" }
console.log(it.next()) // { done: false, value: "ya" }
console.log(it.next()) // { done: true, value: undefined }

這個時候可以講一下我們的iterator.js中的代碼了

class Iterator {
  constructor (arrayLike, iteratee = (value) => value) {
    this.value = Array.from(arrayLike)
    this.nextIndex = 0
    this.len = this.value.length
    this.iteratee = iteratee
  }

  next () {
    let done = this.nextIndex >= this.len
    let value = done ? undefined : this.iteratee(this.value[ this.nextIndex++ ])

    return { done, value }
  }

  [ Symbol.iterator ] () {
    return this
  }
}

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

let it = new Iterator(["yo", "ya"])

console.log(it.next()) // { done: false, value: "yo" }
console.log(it.next()) // { done: false, value: "ya" }
console.log(it.next()) // { done: true, value: undefined }

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

for of
一個數(shù)據(jù)結(jié)構(gòu)只要部署了Symbol.iterator屬性,就被視為具有iterator接口,就可以用for...of循環(huán)遍歷它的成員。也就是說,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實例本身,它是一個可以被迭代的對象。

[ Symbol.iterator ] () {
  return this
}

到這里上面的問題2就可以解釋通了。

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

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

原文鏈接

參考

Set

迭代器和生成器

ES6 系列之模擬實現(xiàn)一個 Set 數(shù)據(jù)結(jié)構(gòu)

展開語法

for...of 循環(huán)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/97783.html

相關(guān)文章

  • 零到一,擼一個在線斗地主(上篇)

    摘要:原文從零到一,擼一個在線斗地主上篇作者背景朋友來深圳玩,若說到在深圳有什么好玩的,那當(dāng)然是宅在家里斗地主了可是天算不如人算,撲克牌丟了幾張不全大熱天的,誰愿意出去買牌啊。 原文:從零到一,擼一個在線斗地主(上篇) | AlloyTeam作者:TAT.vorshen 背景:朋友來深圳玩,若說到在深圳有什么好玩的,那當(dāng)然是宅在家里斗地主了!可是天算不如人算,撲克牌丟了幾張不全……大熱天的,...

    raoyi 評論0 收藏0
  • java高并發(fā)之零到放棄(四)

    摘要:前言本篇主要講解如何去優(yōu)化鎖機制或者克服多線程因為鎖可導(dǎo)致性能下降的問題線程變量有這樣一個場景,前面是一大桶水,個人去喝水,為了保證線程安全,我們要在杯子上加鎖導(dǎo)致大家輪著排隊喝水,因為加了鎖的杯子是同步的,只能有一個人拿著這個唯一的杯子喝 前言 本篇主要講解如何去優(yōu)化鎖機制或者克服多線程因為鎖可導(dǎo)致性能下降的問題 ThreadLocal線程變量 有這樣一個場景,前面是一大桶水,10個...

    Alex 評論0 收藏0
  • java高并發(fā)之零到放棄(五)

    摘要:前言這篇主要來講解多線程中一個非常經(jīng)典的設(shè)計模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲生產(chǎn)者消費者模式簡述此設(shè)計模式中主要分兩類線程生產(chǎn)者線程和消費者線程生產(chǎn)者提供數(shù)據(jù)和任務(wù)消費者處理數(shù)據(jù)和任務(wù)該模式的核心就是數(shù)據(jù)和任務(wù)的交互點共享內(nèi)存緩 前言 這篇主要來講解多線程中一個非常經(jīng)典的設(shè)計模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲 生產(chǎn)者-消費者模式簡述 此設(shè)計模式中主要分兩類線程:生產(chǎn)者...

    meislzhua 評論0 收藏0
  • Day02 - JavaScript + CSS Clock

    摘要:作者簡介是推出的一個天挑戰(zhàn)。完整指南在從零到壹全棧部落。通過時分秒對一圈度,進(jìn)行映射,確定每一個指針?biāo)栊D(zhuǎn)的角度。此前的代碼中,每秒都會重新一個對象,用來計算角度值,但如果讓這個角度值一直保持增長,也就不會出現(xiàn)逆時針回旋的問題了。 Day02 - JavaScript + CSS Clock 作者:?liyuechun 簡介:JavaScript30 是 Wes Bos 推出的一個...

    zzbo 評論0 收藏0

發(fā)表評論

0條評論

PAMPANG

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<