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

資訊專欄INFORMATION COLUMN

不可變數(shù)據(jù)

lanffy / 1182人閱讀

摘要:為什么要有不可變數(shù)據(jù)首先,不可變數(shù)據(jù)類型是源于函數(shù)式編程中的,是一條必備的準(zhǔn)則。另外在中的廣泛應(yīng)用,也讓函數(shù)式編程火熱,而函數(shù)式編程最重要的原則之一就是不可變數(shù)據(jù),所以你在使用的時候,改變必須返回新的。

不可變數(shù)據(jù) 引入

我是通過使用 React 才去關(guān)注 immutable data 這個概念的。事實上,你去搜 immutable 的 JS 相關(guān)文章,也基本都是近兩年的,大概是隨著 React 的推廣才備受關(guān)注。但是這篇文章不會去介紹 React 是如何在意 immutable data 的,而是從原生 JS,寫一些自己的思考。

個人 blog,歡迎 star。https://github.com/sunyongjian

可變/不可變對象

可變對象是一個可在其創(chuàng)建后修改狀態(tài)的對象,而不可變對象則是創(chuàng)建之后,不能再修改狀態(tài),對其任何刪改操作,都應(yīng)返回一個新的對象。

一個例子開始:

var x = {
    a: 1
}
var y = x;
x.a = 2;
console.log(y); //{ a: 2 }

這在我們剛開始學(xué) js 的時候就知道了,js 中的對象都是參考(reference)類型,x = y 是對象賦值引用,兩者共用一個對象的空間,所以 x 改動了,y 自然也改變。

數(shù)組也是一樣的:

var ary = [1, 2, 3];
var list = ary;
ary.push(4);
console.log(list); // [1, 2, 3, 4]

在 JS 中,objects, arrays,functions, classes, sets, maps 都是可變數(shù)據(jù)。
不過字符串和數(shù)字就不會。

var str = "hello world";
var sub = str;
str = str.slice(0, 5);
console.log(sub); // "hello world"

var a = 1;
var b = a;
a += 2;
console.log(b); // 1

像這樣,sub = strb = a 的賦值操作,都不會影響之前的數(shù)據(jù)。

為什么要有不可變數(shù)據(jù)

首先,不可變數(shù)據(jù)類型是源于函數(shù)式編程中的,是一條必備的準(zhǔn)則。函數(shù)式對數(shù)據(jù)處理的時候,通過把問題抽象成一個個的純函數(shù),每個純函數(shù)的操作都會返回新的數(shù)據(jù)類型,都不會影響之前的數(shù)據(jù),保證了變量/參數(shù)的不可變性,增加代碼可讀性。

另外,js 中對象可變的好處可能是為了節(jié)約內(nèi)存,相比字符串、數(shù)字,它承載的數(shù)據(jù)量更大更多,不可變帶來每次操作都要產(chǎn)生新的對象,新的數(shù)據(jù)結(jié)構(gòu),這與 js 設(shè)計之初用來做網(wǎng)頁中表單驗證等簡單操作是有悖的。而且,我們最開始也確實感受到可變帶來的便捷,但是反之它帶來的副作用遠(yuǎn)超過這種便捷,程序越大代碼的可讀性,復(fù)雜度也越來越高。

舉一個栗子:

const data = {
  name: "syj",
  age: 24,
  hobby: "girl",
  location: "beijing"
}
// 有一個改變年齡的方法
function addAge(obj) {
    obj.age += 1;
    return obj;
}

// 一個改變地址的方法
function changeLocation(obj, v) {
    obj.location = v;
    return obj;
}

// 這兩個方法我期待的是得到只改變想改變的屬性的 data
console.log(addAge(data));
console.log(changeLocation(obj, "shanghai"));

但實際上 addAge 已經(jīng)把原始數(shù)據(jù) data 改變了,當(dāng)我再去使用的時候,已經(jīng)是被污染的數(shù)據(jù)。這個栗子其實沒有那么的典型,因為沒有結(jié)合業(yè)務(wù),但是也可以說明一些問題,就是可變數(shù)據(jù)帶來的不確定影響。這兩個函數(shù)都是有“副作用”的,即對傳入數(shù)據(jù)做了修改,當(dāng)你調(diào)用兩次 addAge,得到的卻是兩個完全不同的結(jié)果,這顯然不是我們想要的。如果遵循不可變數(shù)據(jù)的原則,每次對原始數(shù)據(jù)結(jié)構(gòu)的修改、操作,都返回新的數(shù)據(jù)結(jié)構(gòu),就不會出現(xiàn)這種情況。關(guān)于返回新的數(shù)據(jù)結(jié)構(gòu),就需要用到數(shù)據(jù)拷貝。

數(shù)據(jù)拷貝

之前 y = x 這樣的操作,顯然是無法完成數(shù)據(jù)拷貝的,這只是賦值引用,為了避免這種對象間的賦值引用,我們應(yīng)該更多的使用 const 定義數(shù)據(jù)對象,去避免這種操作。
而我們要給新對象(數(shù)據(jù))創(chuàng)建一個新的引用,也就是需要數(shù)據(jù)拷貝。然而對象的數(shù)據(jù)結(jié)構(gòu)通常是不同的(嵌套程度等),在數(shù)據(jù)拷貝的時候,需要考慮到這個問題,如果對象是深層次的

比較一下 JS 中幾種原生的拷貝方法,了解他們能實現(xiàn)的程度。

Object.assign

像這樣:

const x = { a: 1 };

const y = Object.assign({}, x);
x.a = 11;
console.log(y); // { a: 1 }

誠然,此次對 y 的賦值,再去改變 x.a 的時候,y.a 并沒有發(fā)生變化,保持了不變性。你以為就這么簡單嗎?看另一個栗子:

const x = { a: 1, b: { c: 2 } };

const y = Object.assign({}, x);

x.b.c = 22;

console.log(y); // { a: 1, b: { c: 22}}

對 x 的操作,使 y.b.c 也變成了 22。為什么?因為 Object.assign 是淺拷貝,也就是它只會賦值對象第一層的 kv,而當(dāng)?shù)谝粚拥?value 出現(xiàn) object/array 的時候,它還是會做賦值引用操作,即 x,y 的 b 共用一個 {c: 2} 的地址。還有幾個方法也是這樣的。

Object.freeze
const x = { a: 1, b: { c: 2 } };
const y = Object.freeze(x);
x.a = 11;
console.log(y);

x.b.c = 22;

console.log(y); // { a: 1, b: { c: 22}}

freeze,看起來是真的“凍結(jié)”了,不可變了,其實效果是一樣的,為了效率,做的淺拷貝。

deconstruction 解構(gòu)
const x = { a: 1, b: { c: 2 } };
const y = { ...x };
x.a = 11;
console.log(y);

x.b.c = 22;

console.log(y);

es6 中的新方法,解構(gòu)。數(shù)組也一樣:

const x = [1, 2, [3, 4]];
const y = [...x];
x[2][0] = 33;
console.log(y); // [1, 2, [33, 4]]

同樣是淺拷貝。

JS 原生對象的方法,是沒有給我們提供深拷貝功能的。

deep-clone

如何去做深拷貝

原生

拿上面的栗子來說,我們?nèi)崿F(xiàn)深拷貝。

const x = { a: 1, b: { c: 2 } };
const y = Object.assign({}, x, {
  b: Object.assign({}, x.b)
})

x.b.c = 22;

console.log(y); // { a: 1, b: { c: 2 } }

不過這只是嵌套不多的時候,而更深層次的,就需要更復(fù)雜的操作了。實際上,deep-clone 確實沒有一個統(tǒng)一的方法,需要考慮的地方挺多,比如效率,以及是否應(yīng)用場景(是否每次都需要 deep-clone)。還有在 js 中,還要加上 hasOwnProperty 這樣的判斷。寫個簡單的方法:

function clone(obj) {
  // 類型判斷。 isActiveClone 用來防止重復(fù) clone,效率問題。
  if (obj === null || typeof obj !== "object" || "isActiveClone" in obj) {
    return obj;
  }

  //可能是 Date 對象
  const result = obj instanceof Date ? new Date(obj) : {};

  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      obj["isActiveClone"] = null;
      result[key] = clone(obj[key]);
      delete obj["isActiveClone"];
    }
  }

  return result;
}

var x = {
  a: 1,
  b: 2,
  c: {
    d: 3
  }
}
console.log(clone(x));

JSON

最簡單,偷懶的一種方式,JSON 的序列化再反序列化。

const y = JSON.parse(JSON.stringify(x));

普通的 string,number,object,array 都是可以做深拷貝的。不過這個方法比較偷懶,是存在坑的,比如不支持 NaN,正則,function 等。舉個栗子:

const x = {
  a: function() {
    console.log("aaa")
  },
  b: NaN,
}

const y = JSON.parse(JSON.stringify(x));
console.log(y.b);
y.a()

試一下就知道了。

Library

通常實現(xiàn) deep-clone 的庫:lodash$.extend(true, )... 目前最好用的是 immutable.js。 關(guān)于 immutable 的常用用法,之后會整理一下。

數(shù)據(jù)持久化

不變性可以讓數(shù)據(jù)持久化變得容易。當(dāng)數(shù)據(jù)不可變的時候,我們的每次操作,都不會引起初始數(shù)據(jù)的改變。也就是說在一定時期內(nèi),這些數(shù)據(jù)是永久存在的,而你可以通過讀取,實現(xiàn)類似于“回退/切換快照”般的操作。這是我們從函數(shù)式編程來簡單理解這個概念,而不涉及硬盤存儲或者數(shù)據(jù)庫存儲的概念。

首先,無論數(shù)據(jù)結(jié)構(gòu)的深淺,每次操作都對整個數(shù)據(jù)結(jié)構(gòu)進(jìn)行完整的深拷貝,效率會很低。這就牽扯到在做數(shù)據(jù)拷貝的時候,利用數(shù)據(jù)結(jié)構(gòu),做一些優(yōu)化。例如,我們可以觀察某次操作,到底有沒有引起深層次數(shù)據(jù)結(jié)構(gòu)的變化,如果沒有,我們是不是可以只做部分改變,而沒變化的地方,還是可以共用的。這就是部分持久化。我知道的 immutable 就是這么做的,兩個不可變數(shù)據(jù)是會共用某部分的。

思考

js 的對象天生是可變的?

我覺得作者應(yīng)該是設(shè)計之初就把 js 作為一種靈活性較高的語言去做的,而不可變數(shù)據(jù)涉及到數(shù)據(jù)拷貝的算法問題,深拷貝是可以實現(xiàn)的,但是如何最優(yōu)、效率最高的實現(xiàn)拷貝,并保持?jǐn)?shù)據(jù)不可變。這個地方是可以繼續(xù)研究的。

為什么不可變數(shù)據(jù)的熱度越來越高?

隨著 js 應(yīng)用的場景越來越多,業(yè)務(wù)場景也越來越復(fù)雜,一些早就沉淀下來的編程思維,也被引入 js 中,像 MVC,函數(shù)式等等。經(jīng)典的編程思想,設(shè)計模式永遠(yuǎn)都是不過時的,而不可變數(shù)據(jù)結(jié)構(gòu)也是如此。而我覺得真正讓它受關(guān)注的,還是 React 的推出,因為 React 內(nèi)部就是通過 state/props 比較(===)去判斷是否 render 的,三個等號的比較就要求新的 state 必須是新的引用。另外 Redux 在 React 中的廣泛應(yīng)用,也讓函數(shù)式編程火熱,而函數(shù)式編程最重要的原則之一就是不可變數(shù)據(jù),所以你在使用

Redux 的時候,改變 store 必須返回新的 state。所以,React-Redux 全家桶,讓 immutable data 備受關(guān)注,而 immutable,就是目前最好的實現(xiàn)方案。

最后

之后會探究 immutable data 在 React 中的重要性,包括 diff,re-render,redux。自然而然也可以總結(jié)出這方面的 React 性能優(yōu)化。

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

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/89779.html

相關(guān)文章

  • 第3章:抽象數(shù)據(jù)類型(ADT)和面向?qū)ο缶幊蹋∣OP) 3.1數(shù)據(jù)類型和類型檢查

    摘要:所有變量的類型在編譯時已知在程序運行之前,因此編譯器也可以推導(dǎo)出所有表達(dá)式的類型。像變量的類型一樣,這些聲明是重要的文檔,對代碼讀者很有用,并由編譯器進(jìn)行靜態(tài)檢查。對象類型的值對象類型的值是由其類型標(biāo)記的圓。 大綱 1.編程語言中的數(shù)據(jù)類型2.靜態(tài)與動態(tài)數(shù)據(jù)類型3.類型檢查4.易變性和不變性5.快照圖6.復(fù)雜的數(shù)據(jù)類型:數(shù)組和集合7.有用的不可變類型8.空引用9.總結(jié) 編程語言中的數(shù)據(jù)...

    zhangqh 評論0 收藏0
  • React 狀態(tài)管理庫: Mobx

    摘要:關(guān)心性能的情況下,需要手動設(shè)置這時就需要引入狀態(tài)管理庫。現(xiàn)在常用的狀態(tài)管理庫有和,本文會重點介紹,然后會將和進(jìn)行對比,最后展望下未來的狀態(tài)管理方面趨勢。如果在任何地方都修改可觀察數(shù)據(jù),將導(dǎo)致頁面狀態(tài)難以管理。 React 是一個專注于視圖層的庫。React 維護了狀態(tài)到視圖的映射關(guān)系,開發(fā)者只需關(guān)心狀態(tài)即可,由 React 來操控視圖。 在小型應(yīng)用中,單獨使用 React 是沒什么問題...

    liujs 評論0 收藏0
  • String:String類型為什么可變

    摘要:性能當(dāng)字符串是不可變時,字符串常量池才有意義。字符串常量池的出現(xiàn),可以減少創(chuàng)建相同字面量的字符串,讓不同的引用指向池中同一個字符串,為運行時節(jié)約很多的堆內(nèi)存。 在學(xué)習(xí)Java的過程中,我們會被告知 String 被設(shè)計成不可變的類型。為什么 String 會被 Java 開發(fā)者有如此特殊的對待?他們的設(shè)計意圖和設(shè)計理念到底是什么?因此,我?guī)е韵氯齻€問題,對 String 進(jìn)行剖析: ...

    zhiwei 評論0 收藏0
  • WebGL2系列之可變紋理

    摘要:除此之外,還可以通過函數(shù)獨立指定紋理的每個的級別。這種繪圖時檢查可能代價很高,而使用不可變紋理可以避免這種情形。不可變紋理使用不可變紋理,可以減少上文中提到的因檢查而導(dǎo)致的性能開銷。不可變紋理指的是紋理的一種分配方式,而不是值紋理的內(nèi)容。 紋理背景知識 在WebGL1中,紋理包括2D紋理和立方體紋理,在實際的使用中,如果紋理的圖片是寬和高是2的冪,可以自動生成紋理的mipmap。除此之...

    xinhaip 評論0 收藏0

發(fā)表評論

0條評論

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