2019-01-07

閉包

什么叫閉包?
閉包的定義其實很簡單:函數(shù) A 內(nèi)部有一個函數(shù) B,函數(shù) B 可以訪問到函數(shù) A 中的變量,那么函數(shù) B 就是閉包栅屏。

function f1(){
    var n = 999;
    nAdd = function(){
        n+=1;
    }
    function f2(){
        console.log(n)
    }
    return f2;
}

var result = f1();
result(); //999
nAdd();
result(); //1000

使用閉包的注意點
由于閉包會使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大堂鲜,所以不能濫用閉包栈雳,否則會造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露缔莲。解決方法是哥纫,在退出函數(shù)之前,將不使用的局部變量全部刪除痴奏。

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}//輸出一堆6

首先因為 setTimeout 是個異步函數(shù)蛀骇,所以會先把循環(huán)全部執(zhí)行完畢,這時候 i 就是 6 了读拆,所以會輸出一堆 6擅憔。
let不不允許在相同作?用域內(nèi),重復(fù)聲明同?一個變量量
只要塊級作?用域內(nèi)存在let命令檐晕,它所聲明的變量量就“綁定”這個區(qū)域暑诸,不不再受外部的影響,不不存在變 量量提升

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, i * 1000)
}//1,2,3,4,5
var a = [];
for (var i = 0; i < 10; i++) {//每執(zhí)行一遍for,都會重新聲明一遍變量i
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

a[0],…a[9]=? () {
    console.log(i);
  }

什么是淺拷貝个榕?如何實現(xiàn)淺拷貝啦逆?什么是深拷貝?如何實現(xiàn)深拷貝笛洛?

在我之前的文章中有關(guān)于對象類型在賦值的過程中其實是復(fù)制了地址,從而會導(dǎo)致改變了一方其他也都被改變的情況乃坤。通常在開發(fā)中我們不希望出現(xiàn)這樣的問題苛让,我們可以使用淺拷貝來解決這個情況。

let a = {
  age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2

淺拷貝

首先可以通過 Object.assign 來解決這個問題湿诊,很多人認(rèn)為這個函數(shù)是用來深拷貝的狱杰。其實并不是,Object.assign 只會拷貝所有的屬性值到新的對象中厅须,如果屬性值是對象的話仿畸,拷貝的是地址,所以并不是深拷貝朗和。

let a = {
  age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

另外我們還可以通過展開運(yùn)算符 ... 來實現(xiàn)淺拷貝

let a = {
  age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1

通常淺拷貝就能解決大部分問題了错沽,但是當(dāng)我們遇到如下情況就可能需要使用到深拷貝了

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native

淺拷貝只解決了第一層的問題,如果接下去的值中還有對象的話眶拉,那么就又回到最開始的話題了千埃,兩者享有相同的地址。要解決這個問題忆植,我們就得使用深拷貝了放可。

深拷貝
這個問題通常可以通過 JSON.parse(JSON.stringify(object)) 來解決朝刊。

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是該方法也是有局限性的:

  • 會忽略 undefined
  • 會忽略 symbol
  • 不能序列化函數(shù)
  • 不能解決循環(huán)引用的對象
let obj = {
  a: 1,
  b: {
    c: 2,
    d: 3,
  },
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)

如果你有這么一個循環(huán)引用對象耀里,你會發(fā)現(xiàn)并不能通過該方法實現(xiàn)深拷貝

在遇到函數(shù)、 undefined 或者 symbol 的時候拾氓,該對象也不能正常的序列化

let a = {
  age: undefined,
  sex: Symbol('male'),
  jobs: function() {},
  name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}

你會發(fā)現(xiàn)在上述情況中冯挎,該方法會忽略掉函數(shù)和 undefined 。

但是在通常情況下痪枫,復(fù)雜數(shù)據(jù)都是可以序列化的织堂,所以這個函數(shù)可以解決大部分問題。

如果你所需拷貝的對象含有內(nèi)置類型并且不包含函數(shù)奶陈,可以使用 MessageChannel

function structuralClone(obj) {
  return new Promise(resolve => {
    const { port1, port2 } = new MessageChannel()
    port2.onmessage = ev => resolve(ev.data)
    port1.postMessage(obj)
  })
}

var obj = {
  a: 1,
  b: {
    c: 2
  }
}

obj.b.d = obj.b

// 注意該方法是異步的
// 可以處理 undefined 和循環(huán)引用對象
const test = async () => {
  const clone = await structuralClone(obj)
  console.log(clone)
}
test()

當(dāng)然你可能想自己來實現(xiàn)一個深拷貝易阳,但是其實實現(xiàn)一個深拷貝是很困難的,需要我們考慮好多種邊界情況吃粒,比如原型鏈如何處理潦俺、DOM 如何處理等等,所以這里我們實現(xiàn)的深拷貝只是簡易版,并且我其實更推薦使用 lodash 的深拷貝函數(shù)事示。

function deepClone(obj) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非對象')
  }

  let isArray = Array.isArray(obj)
  let newObj = isArray ? [...obj] : { ...obj }
  Reflect.ownKeys(newObj).forEach(key => {
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
  })

  return newObj
}

let obj = {
  a: [1, 2, 3],
  b: {
    c: 2,
    d: 3
  }
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2

(function(j){})(i)

今天在看代碼時看到j(luò)s語句是這么寫的早像,看上去(function(j){})(i)比較神奇,其實很簡單肖爵,第一個括號定義了一個匿名函數(shù)卢鹦,后一個括號是指調(diào)用了這個函數(shù),并傳入?yún)?shù)i劝堪。當(dāng)然這個匿名函數(shù)接受一個參數(shù)冀自,命名為j。就是這么簡單秒啦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熬粗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子余境,更是在濱河造成了極大的恐慌驻呐,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芳来,死亡現(xiàn)場離奇詭異含末,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)绣张,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門答渔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侥涵,你說我怎么就攤上這事沼撕。” “怎么了芜飘?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵务豺,是天一觀的道長。 經(jīng)常有香客問我嗦明,道長笼沥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任娶牌,我火速辦了婚禮奔浅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诗良。我一直安慰自己汹桦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布鉴裹。 她就那樣靜靜地躺著舞骆,像睡著了一般钥弯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上督禽,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天脆霎,我揣著相機(jī)與錄音,去河邊找鬼狈惫。 笑死睛蛛,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的胧谈。 我是一名探鬼主播玖院,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼第岖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起试溯,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蔑滓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遇绞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體键袱,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年摹闽,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹄咖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡付鹿,死狀恐怖澜汤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舵匾,我是刑警寧澤俊抵,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站坐梯,受9級特大地震影響徽诲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吵血,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一谎替、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蹋辅,春花似錦钱贯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砍濒。三九已至,卻和暖如春硫麻,著一層夾襖步出監(jiān)牢的瞬間爸邢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工拿愧, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留杠河,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓浇辜,卻偏偏與公主長得像券敌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子柳洋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內(nèi)容