閉包
什么叫閉包?
閉包的定義其實很簡單:函數(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。就是這么簡單秒啦。