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

資訊專欄INFORMATION COLUMN

從JS對象開始,談一談“不可變數(shù)據(jù)”和函數(shù)式編程

Batkid / 3362人閱讀

摘要:下面,我就從基本對象說起,聊一聊不可變數(shù)據(jù)和的一切。可變和共享是萬惡之源不可變數(shù)據(jù)其實是函數(shù)式編程相關的重要概念。相對的,函數(shù)式編程中認為可變性是萬惡之源。針對于此,我推薦一款已經(jīng)大名鼎鼎的類庫來處理不可變數(shù)據(jù)。

作為前端開發(fā)者,你會感受到JS中對象(Object)這個概念的強大。我們說“JS中一切皆對象”。最核心的特性,例如從String,到數(shù)組,再到瀏覽器的APIs,“對象”這個概念無處不在。在這里你可以了解到JS Objects中的一切。

同時,隨著React的強勢崛起,不管你有沒有關注過這個框架,也一定聽說過一個概念—不可變數(shù)據(jù)(immutable.js)。究竟什么是不可變數(shù)據(jù)?這篇文章會從JS源頭—對象談起,讓你逐漸了解這個函數(shù)式編程里的重要概念。

JS中的對象是那么美妙:我們可以隨意復制他們,改變并刪除他們的某項屬性等。但是要記住一句話:

“伴隨著特權,隨之而來的是更大的責任。”
(With great power comes great responsibility)

的確,JS Objects里概念太多了,我們切不可隨意使用對象。下面,我就從基本對象說起,聊一聊不可變數(shù)據(jù)和JS的一切。

這篇文章緣起于Daniel Leite在2017年3月16日的新鮮出爐文章:Things you should know about Objects and Immutability in JavaScript,我進行了大致翻譯并進行大范圍“改造”,同時改寫了用到的例子,進行了大量更多的擴展。

“可變和共享”是萬惡之源

不可變數(shù)據(jù)其實是函數(shù)式編程相關的重要概念。相對的,函數(shù)式編程中認為可變性是萬惡之源。但是,為什么會有這樣的結論呢?

這個問題可能很多程序員都會有。其實,如果你的代碼邏輯可變,不要驚慌,這并不是“政治錯誤”的。比如JS中的數(shù)組操作,很對都會對原數(shù)組進行直接改變,這當然并沒有什么問題。比如:

let arr = [1, 2, 3, 4, 5];
arr.splice(1, 1); // 返回[2];
console.log(arr); // [1, 3, 4, 5];

這是我們常用的“刪除數(shù)組某一項”的操作。好吧,他一點問題也沒有。

問題其實出現(xiàn)在“濫用”可變性上,這樣會給你的程序帶來“副作用”。先不必關心什么是“副作用”,他又是一個函數(shù)式編程的概念。

我們先來看一下代碼實例:

const student1 = {
    school: "Baidu",
    name: "HOU Ce",
    birthdate: "1995-12-15",
}

const changeStudent = (student, newName, newBday) => {
    const newStudent = student;
    newStudent.name = newName;
    newStudent.birthdate = newBday;
    return newStudent;
}

const student2 = changeStudent(student1, "YAN Haijing", "1990-11-10");

// both students will have the name properties
console.log(student1, student2);
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"} 
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"}

我們發(fā)現(xiàn),盡管創(chuàng)建了一個新的對象student2,但是老的對象student1也被改動了。這是因為JS對象中的賦值是“引用賦值”,即在賦值過程中,傳遞的是在內存中的引用(memory reference)。具體說就是“棧存儲”和“堆存儲”的問題。具體圖我就不畫了,理解不了可以單找我。

不可變數(shù)據(jù)的強大和實現(xiàn)

我們說的“不可變”,其實是指保持一個對象狀態(tài)不變。這樣做的好處是使得開發(fā)更加簡單,可回溯,測試友好,減少了任何可能的副作用。
函數(shù)式編程認為:

只有純的沒有副作用的函數(shù),才是合格的函數(shù)。

好吧,現(xiàn)在開始解釋下“副作用”(Side effect):

在計算機科學中,函數(shù)副作用指當調用函數(shù)時,除了返回函數(shù)值之外,還對主調用函數(shù)產(chǎn)生附加的影響。例如修改全局變量(函數(shù)外的變量)或修改參數(shù)。
-維基百科

函數(shù)副作用會給程序設計帶來不必要的麻煩,給程序帶來十分難以查找的錯誤,并降低程序的可讀性。嚴格的函數(shù)式語言要求函數(shù)必須無副作用。

那么我們避免副作用,創(chuàng)建不可變數(shù)據(jù)的主要實現(xiàn)思路就是:一次更新過程中,不應該改變原有對象,只需要新創(chuàng)建一個對象用來承載新的數(shù)據(jù)狀態(tài)。

我們使用純函數(shù)(pure functions)來實現(xiàn)不可變性。純函數(shù)指無副作用的函數(shù)。
那么,具體怎么構造一個純函數(shù)呢?我們可以看一下代碼實現(xiàn),我對上例進行改造:

const student1 = {
    school: "Baidu", 
    name: "HOU Ce",
    birthdate: "1995-12-15",
}

const changeStudent = (student, newName, newBday) => {
    return {
        ...student, // 使用解構
        name: newName, // 覆蓋name屬性
        birthdate: newBday // 覆蓋birthdate屬性
    }
}

const student2 = changeStudent(student1, "YAN Haijing", "1990-11-10");

// both students will have the name properties
console.log(student1, student2);
// Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15"} 
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"}

需要注意的是,我使用了ES6中的解構(destructuring)賦值。
這樣,我們達到了想要的效果:根據(jù)參數(shù),產(chǎn)生了一個新對象,并正確賦值,最重要的就是并沒有改變原對象。

創(chuàng)建純函數(shù),過濾副作用

現(xiàn)在,我們知道了“不可變”到底指的是什么。接下來,我們就要分析一下純函數(shù)應該如何實現(xiàn),進而生產(chǎn)不可變數(shù)據(jù)。

其實創(chuàng)建不可變數(shù)據(jù)方式有很多,在使用原生JS的基礎上,我推薦的方法是使用現(xiàn)有的Objects API和ES6當中的解構賦值(上例已經(jīng)演示)。現(xiàn)在看一下Objects.assign的實現(xiàn)方式:

const student1 = {
    school: "Baidu", 
    name: "HOU Ce",
    birthdate: "1995-12-15",
}

const changeStudent = (student, newName, newBday) => Object.assign({}, student, {name: newName, birthdate: newBday})

const student2 = changeStudent(student1, "YAN Haijing", "1990-11-10");

// both students will have the name properties
console.log(student1, student2);
// Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15"};
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10"};

同樣,如果是處理數(shù)組相關的內容,我們可以使用:.map, .filter或者.reduce去達成目標。這些APIs的共同特點就是不會改變原數(shù)組,而是產(chǎn)生并返回一個新數(shù)組。這和純函數(shù)的思想不謀而合。

但是,再說回來,使用Object.assign請務必注意以下幾點:
1)他的復制,是將所有可枚舉屬性,復制到目標對象。換句話說,不可枚舉屬性是無法完成復制的。
2)對象中如果包含undefined和null類型內容,會報錯。
3)最重要的一點:Object.assign方法實行的是淺拷貝,而不是深拷貝。

第三點很重要,也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個屬性對象的引用。這也就意味著,當對象存在嵌套時,還是有問題的。比如下面代碼:

const student1 = {
    school: "Baidu", 
    name: "HOU Ce",
    birthdate: "1995-12-15",
    friends: {
        friend1: "ZHAO Wenlin",
        friend2: "CHENG Wen"
    }
}

const changeStudent = (student, newName, newBday, friends) => Object.assign({}, student, {name: newName, birthdate: newBday})

const student2 = changeStudent(student1, "YAN Haijing", "1990-11-10");

// both students will have the name properties
console.log(student1, student2); 
// Object {school: "Baidu", name: "HOU Ce", birthdate: "1995-12-15", friends: Object}
// Object {school: "Baidu", name: "YAN Haijing", birthdate: "1990-11-10", friends: Object}

student2.friends.friend1 = "MA xiao";
console.log(student1.friends.friend1); // "MA xiao"

對student2 friends列表當中的friend1的修改,同時也影響了student1 friends列表當中的friend1。

JS本身的蒼白無力VS不可變數(shù)據(jù)類庫

以上,我們分析了純JS如何實現(xiàn)不可變數(shù)據(jù)。這樣處理帶來的一個負面影響在于:一些經(jīng)典APIs都是shallow處理,比如上文提到的Object.assign就是典型的淺拷貝。如果遇到嵌套很深的結構,我們就需要手動遞歸。這樣做呢,又會存在性能上的問題。

比如我自己動手用遞歸實現(xiàn)一個深拷貝,需要考慮循環(huán)引用的“死環(huán)”問題,另外,當使用大規(guī)模數(shù)據(jù)結構時,性能劣勢盡顯無疑。我們熟悉的jquery extends方法,某一版本(最新版本情況我不太了解)的實現(xiàn)是進行了三層拷貝,也沒有達到完備的deep copy。

總之,實現(xiàn)不可變數(shù)據(jù),我們必然要關心性能問題。針對于此,我推薦一款已經(jīng)“大名鼎鼎”的——immutable.js類庫來處理不可變數(shù)據(jù)。

他的實現(xiàn)既保證了不可變性,又保證了性能大限度優(yōu)化。原理很有意思,下面這段話,我摘自camsong前輩的文章:

Immutable實現(xiàn)的原理是Persistent Data Structure(持久化數(shù)據(jù)結構),也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時,要保證舊數(shù)據(jù)同時可用且不變。

同時為了避免deepCopy把所有節(jié)點都復制一遍帶來的性能損耗,Immutable使用了Structural Sharing(結構共享),即如果對象樹中一個節(jié)點發(fā)生變化,只修改這個節(jié)點和受它影響的父節(jié)點,其它節(jié)點則進行共享。

感興趣的讀者可以深入研究下,這是很有意思的。如果有需要,我也愿意再寫一篇immutable.js源碼分析。

總結

我們使用JavaScript操縱對象,這樣的方式很簡單便捷。但是,這樣操控的基礎是在JavaScript靈活機制的熟練掌握上。不然很容易使你“頭大”。

在我開發(fā)的百度某部門私信項目中,因為使用了React+Redux技術棧,并且數(shù)據(jù)結構較為負責,所以我也采用了immutable.js實現(xiàn)。

最后,在前端開發(fā)中,函數(shù)式編程越來越熱,并且在某種程度上已經(jīng)取代了“過程式”編程和面向對象思想。

我的感想是在某些特定的場景下,不要畏懼變化,擁抱未來。
就像我很喜歡的葡萄牙詩人安德拉德一首詩中那樣說的:

我同樣不知道什么是海,

赤腳站在沙灘上,
急切地等待著黎明的到來。

Happy Coding!

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

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/82132.html

相關文章

  • 攻克前端javascript面試:什么是函數(shù)編程

    摘要:僅在幾年以前,僅有少數(shù)的程序員知道函數(shù)式編程是什么。函數(shù)式編程是聲明性的而不是命令式的應用狀態(tài)流經(jīng)純函數(shù)中。函數(shù)式編程是一種編程模式。在理解軟件是如何使用函數(shù)式編程構建時,理解函數(shù)組合是非常重要的一步。不可變性是函數(shù)式編程的核心概念。 函數(shù)式編程已然變成了一個javascript語言中一個非常熱門的話題。僅在幾年以前,僅有少數(shù)的js程序員知道函數(shù)式編程是什么。但是在過去三年中,我所見過...

    wslongchen 評論0 收藏0
  • 一談Vuex

    摘要:是什么官方文檔說道是一個專為應用程序開發(fā)的狀態(tài)管理模式。觸發(fā)之別名篇觸發(fā)之對象展開運算符篇觸發(fā)之對象展開運算符別名篇先引用官方文檔的說法類似于,不同在于提交的是,而不是直接變更狀態(tài)。 Vuex是什么 官方文檔說道:Vuex 是一個專為 Vue.js 應用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應用的所有組件的狀態(tài),并以相應的規(guī)則保證狀態(tài)以一種可預測的方式發(fā)生變化 什么是狀態(tài)管理模式...

    nifhlheimr 評論0 收藏0
  • JavaScript - 收藏集 - 掘金

    摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應用添加全局功能的一種強大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內優(yōu)雅的實現(xiàn)文件分片斷點續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應用添加全局功能的一種強大而且簡單的方式。插....

    izhuhaodev 評論0 收藏0
  • JS凍結對象的《人間詞話》 完美實現(xiàn)究竟有幾層境界?

    摘要:王國維在人間詞話里談到了治學經(jīng)驗,他說古今之成大事業(yè)大學問者,必經(jīng)過三種之境界。其中談到中凍結一個對象幾種由淺入深的實踐。王國維已先自表明,吾人可以無勞糾葛。總結本文先后介紹了關于凍結一個對象的三種進階方法。 王國維在《人間詞話》里談到了治學經(jīng)驗,他說:古今之成大事業(yè)、大學問者,必經(jīng)過三種之境界。 巧合的是,最近受 git chat / git book 邀請,做了一個分享。其中談到J...

    YorkChen 評論0 收藏0

發(fā)表評論

0條評論

Batkid

|高級講師

TA的文章

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