国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

摸索 JS 內(nèi)深拷貝的最佳實(shí)踐

Jonathan Shieber / 3005人閱讀

摘要:想要簡(jiǎn)單點(diǎn)難道我深拷貝一個(gè)變量還要引入這么麻煩嗎沒(méi)有簡(jiǎn)單點(diǎn)的辦法嗎嗯,可能有點(diǎn)不是那么酷炫,但是他確實(shí)可以滿足要求,而且也無(wú)須引入其他的庫(kù)。

問(wèn)題

由于 js 的傳參方式有時(shí)會(huì)遇到這樣的場(chǎng)景:

function setTime(data) {
  let result = {};
  result.obj = data.obj || {};
  result.obj.time = Date.now();
  return result
}

let data = {
  title:"loooook!",
  obj: {
    name: "keo",
    age: "12"
  }
}

let res = setTime(data);

console.log("res",res);
//res { obj: { name: "keo", age: "12", time: 1533625350183 } }
console.log("data",data);
//data { title: "loooook!", obj: { name: "keo", age: "12", time: 1533625350183 } }

我只是想繼承參數(shù)的部分?jǐn)?shù)據(jù),并在此基礎(chǔ)添加一些東西,但是參數(shù) data 的源數(shù)據(jù)也被我改動(dòng)了,如果之后有其他人想要從data獲取數(shù)據(jù),他可能還需要注意是否有像 setTime 這樣的函數(shù)調(diào)用它。

一點(diǎn)修改
function setTime(data) {
  let result = {};
  result.obj =  {};
  Object.assign(result.obj,data.obj)
  result.obj.time = Date.now();
  return result
}

嗯,或者你也可以用 for...in,注意下二者的不同。
我們知道 Object.assign 只是淺拷貝,如果 data.obj 的屬性值仍然有引用類型的話,那么還是會(huì)遇見(jiàn)同樣的問(wèn)題。
那要怎么辦?難道要遍歷data下每個(gè)屬性的值?一個(gè)個(gè)復(fù)制過(guò)來(lái)?我們看看 lodash 是怎么做的

你猜的沒(méi)錯(cuò),的確是要深度遍歷的。
baseClone方法內(nèi),拿到要拷貝的對(duì)象 value 后,先檢查其類型,然后由對(duì)應(yīng)的 handler 來(lái)處理,比如value是數(shù)組類型,則使 result 為同樣長(zhǎng)度的數(shù)據(jù),然后對(duì)每一項(xiàng)都遞歸調(diào)用 baseClone,直到 value 是非引用類型,返回 value的值;如果是普通對(duì)象類型,則使 result 為空數(shù)組,然后拿取valuekey,對(duì)每個(gè)key的賦值也是遞歸調(diào)用baseClone

想要簡(jiǎn)單點(diǎn)

難道我深拷貝一個(gè)變量還要引入 lodash 這么麻煩嗎 ?沒(méi)有簡(jiǎn)單點(diǎn)的辦法嗎?

JSON.parse(JSON.stringify(param))

嗯,可能有點(diǎn)不是那么酷炫,但是他確實(shí)可以滿足要求,而且也無(wú)須引入其他的庫(kù)。但如果它真的這么完美,為什么 lodash 不這么寫呢?
的確,它的缺點(diǎn)還挺多的,這里取幾個(gè)我覺(jué)得比較重要的:

Set 類型、Map 類型以及 Buffer 類型會(huì)被轉(zhuǎn)換成 {}

undefined、任意的函數(shù)以及 symbol 值,在序列化過(guò)程中會(huì)被忽略(出現(xiàn)在非數(shù)組對(duì)象的屬性值中時(shí))或者被轉(zhuǎn)換成 null(出現(xiàn)在數(shù)組中時(shí))

對(duì)包含循環(huán)引用的對(duì)象(對(duì)象之間相互引用,形成無(wú)限循環(huán))執(zhí)行此方法,會(huì)拋出錯(cuò)誤

所有以 symbol 為屬性鍵的屬性都會(huì)被完全忽略掉,即便 replacer 參數(shù)中強(qiáng)制指定包含了它們

是啊,畢竟JSON的兩個(gè)方法本身就只是用來(lái)轉(zhuǎn)換 js 內(nèi)的對(duì)象為 JSON 格式的,上述幾點(diǎn)甚至都不是缺點(diǎn),是我們想借用其他方法做深拷貝時(shí)遇到的問(wèn)題。

既然是問(wèn)題那應(yīng)該可以解決吧,比如第一條和第二條,在 stringify 時(shí)判斷類型,轉(zhuǎn)化成 帶類型標(biāo)識(shí)符的對(duì)象字符串如:Set [1,2,3,4,5],然后在parse的時(shí)候?qū)ψ址M(jìn)行解析,特別的類型調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)... 聽起來(lái)變得更麻煩了,沒(méi)關(guān)系,忍忍把各個(gè)類型的處理都寫了;針對(duì)第三條,拋錯(cuò)了?沒(méi)關(guān)系,我 try catch 包起來(lái)...,什么?循環(huán)引用?

循環(huán)引用?
function parse (param){
  return JSON.parse(JSON.stringify(param))
}

var a = {}
var b = {}
a["b"] = b
b["a"] = a

console.log(parse(a))
//TypeError: Converting circular structure to JSON at JSON.stringify

如上代碼, 變量ab 互相引用對(duì)方,此時(shí)如果借用 JSON 的方法來(lái)進(jìn)行深拷貝的話,會(huì)報(bào)循環(huán)結(jié)構(gòu)轉(zhuǎn)換轉(zhuǎn)換 JSON 錯(cuò)誤。這個(gè)問(wèn)題怎么解決呢?我們?cè)俜?lodash 的源碼看看...

      // Check for circular references and return its corresponding clone.
      stack || (stack = new Stack);
      var stacked = stack.get(value);
      if (stacked) {
        return stacked;
      }
      stack.set(value, result);

這里的 valueresult 分別是是一次遍歷中 要拷貝的值 和 拷貝的結(jié)果。stack 是一個(gè)用來(lái)儲(chǔ)存每次對(duì)應(yīng)的 valueresult 的對(duì)象, stack下有一塊用于儲(chǔ)存的數(shù)組結(jié)構(gòu),該數(shù)組的每一項(xiàng)記錄了單次遍歷中的 valueresult,后二者再次以數(shù)組的形式存儲(chǔ),以 value 做為下標(biāo) 0 的項(xiàng),result 為下標(biāo) 1 的項(xiàng)(這里不用對(duì)象的 key-value 形式可能是因?yàn)檠h(huán)引用的變量無(wú)法使用 JSON.stringify 轉(zhuǎn)換成字符串,只能 toString 轉(zhuǎn)成 object Object);stack 是做為參數(shù)貫穿整個(gè)遍歷過(guò)程的,每次遍歷時(shí)都會(huì)以當(dāng)前的 value 值進(jìn)行查找(這里的查找直接是判斷內(nèi)存地址相等),如果能在 stack 中查到到對(duì)應(yīng)的結(jié)果,則直接返回記錄中的result,不再繼續(xù)遞歸。
好了,循環(huán)引用的問(wèn)題我們解決了,鼓掌!但是我也放棄使用 JSON 方法了...還有沒(méi)有其他直接點(diǎn)的方法呢?

其他方法

結(jié)構(gòu)化克隆算法是由HTML5規(guī)范定義的用于復(fù)制復(fù)雜JavaScript對(duì)象的算法,它通過(guò)遞歸輸入對(duì)象來(lái)構(gòu)建克隆,同時(shí)保持先前訪問(wèn)過(guò)的引用的映射,以避免無(wú)限遍歷循環(huán)。

怎么用?
emmm... 它還不能直接使用,你得依靠一些其他的 API ,間接的使用它。

postMessage()

function StructuredClone(param) {
  return new Promise(function (res, rej) {
    const {port1, port2} = new MessageChannel();
    port2.onmessage = ev => res(ev.data);
    port1.postMessage(param);
  })
}

StructuredClone(objects).then(result => console.log(result))

什么??還是異步的... 不,我希望能使用同步的方法使用它。

history()

function structuralClone(obj) {
  const oldState = history.state;
  history.replaceState(obj, document.title);
  const copy = history.state;
  history.replaceState(oldState, document.title);
  return copy;
}
const clone = structuralClone(objects);

如你所見(jiàn),我們要借用一下 history.replaceState 這個(gè)方法,但是我們不能改變 history 原有的狀態(tài),所以用完就要恢復(fù)原狀,當(dāng)無(wú)事發(fā)生過(guò)。
至少,這是個(gè)同步的方法...,如果是同步的場(chǎng)景可以考慮一下...

性能展示

這里的測(cè)試代碼是使用的 [Deep-copying in JavaScript] (https://dassur.ma/things/deep... 一文中的,并再次基礎(chǔ)做了一些修改。

結(jié)果! (很懶就不畫圖表了)

單位 μs (繆斯),計(jì)算時(shí)間的用的接口是 performance.now()結(jié)果精確到5微秒。

chrome

safari

...em...Safari瀏覽器在調(diào)用完 postMessage 方法后就...沒(méi)有然后了...表格都沒(méi)刷出來(lái)...等了 40 s 終于刷出第一欄...
注釋完 postMessage 又發(fā)現(xiàn)不能頻繁的調(diào)用 history 。

firefox

...em.. 調(diào)用 history 相關(guān) api 對(duì) firefox 好像壓力很大,以至于循環(huán)都有些錯(cuò)亂...于是注釋了相關(guān)代碼

就結(jié)果而言好像看不出什么區(qū)別,可能是我的數(shù)據(jù)不好,大家可以去看看原文,有展示閱讀性更好的圖表,盡管沒(méi)有 lodash 就是了。

結(jié)果

回到我們最初的問(wèn)題,我們只是想深拷貝一個(gè) js 對(duì)象,如果只是一個(gè)比較"普通"的對(duì)象,用JSON的方法簡(jiǎn)單又快捷,但是如果這個(gè)對(duì)象有些“復(fù)雜”,似乎使用 lodash 的方法是比較好的選擇,而且 lodash 連 Structured Clone 算法忽視的 symbol 類型 和 Function 也考慮其中,兼容性也沒(méi)問(wèn)題,也不會(huì)在不同的瀏覽器發(fā)生意外的狀況...
lodash 萬(wàn)歲!lol!!

參考閱讀:
Deep-copying in JavaScript

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/96722.html

相關(guān)文章

  • 我用到ES6

    let和const webpack構(gòu)建的項(xiàng)目,直接廢棄var,直接使用let代替var for循環(huán)中使用let而不是var 變量聲明之后就不會(huì)改變,請(qǐng)使用const 解構(gòu)賦值 概念: 先解構(gòu)再賦值,先從一堆數(shù)據(jù)中找出自己需要的數(shù)據(jù),然后將找到的數(shù)據(jù)賦值給事先定義好的變量 // 對(duì)象的解構(gòu)賦值 // 使用場(chǎng)景 // 1,等號(hào)右邊是大json,等號(hào)左邊是變量,這樣可快速獲取大json中數(shù)據(jù),后續(xù)可...

    libin19890520 評(píng)論0 收藏0
  • 平時(shí)積累前端資源,持續(xù)更新中。。。

    本文收集學(xué)習(xí)過(guò)程中使用到的資源。 持續(xù)更新中…… 項(xiàng)目地址 https://github.com/abc-club/f... 目錄 vue react react-native Weex typescript Taro nodejs 常用庫(kù) css js es6 移動(dòng)端 微信公眾號(hào) 小程序 webpack GraphQL 性能與監(jiān)控 高質(zhì)文章 趨勢(shì) 動(dòng)效 數(shù)據(jù)結(jié)構(gòu)與算法 js core 代碼規(guī)范...

    acrazing 評(píng)論0 收藏0
  • vuex重置所有state(可定制)

    摘要:這里為什么是一個(gè)數(shù)組呢因?yàn)檫@就是標(biāo)題所描述的可定制,如果頁(yè)面內(nèi)重置絕大部分狀態(tài),但需要保留其中一些狀態(tài)的時(shí)候我們可以通過(guò)我們傳遞過(guò)來(lái)的值來(lái)剔除相應(yīng)的,使其不被更新。 在正式場(chǎng)景中我們經(jīng)常遇到一個(gè)問(wèn)題,就是登出頁(yè)面或其他操作的時(shí)候,我們需要重置所有的vuex,讓其變?yōu)槌跏紶顟B(tài),那么,就涉及到了多種方法:1、頁(yè)面刷新: window.location.reload() 這個(gè)方法通過(guò)路由判斷...

    singerye 評(píng)論0 收藏0
  • vuex重置所有state(可定制)

    摘要:這里為什么是一個(gè)數(shù)組呢因?yàn)檫@就是標(biāo)題所描述的可定制,如果頁(yè)面內(nèi)重置絕大部分狀態(tài),但需要保留其中一些狀態(tài)的時(shí)候我們可以通過(guò)我們傳遞過(guò)來(lái)的值來(lái)剔除相應(yīng)的,使其不被更新。 在正式場(chǎng)景中我們經(jīng)常遇到一個(gè)問(wèn)題,就是登出頁(yè)面或其他操作的時(shí)候,我們需要重置所有的vuex,讓其變?yōu)槌跏紶顟B(tài),那么,就涉及到了多種方法:1、頁(yè)面刷新: window.location.reload() 這個(gè)方法通過(guò)路由判斷...

    LeviDing 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<