1.談?wù)勛兞刻嵘?/h3>
考察點(diǎn):js基礎(chǔ)知識(shí),js執(zhí)行機(jī)制,變量的提升
答:執(zhí)行js代碼時(shí)钱烟,會(huì)生成執(zhí)行環(huán)境,在函數(shù)中的代碼會(huì)產(chǎn)生函數(shù)執(zhí)行環(huán)境嫡丙,函數(shù)外的代碼執(zhí)行全局執(zhí)行環(huán)境拴袭,我們看一個(gè)例子:
b() // call b 執(zhí)行b函數(shù),此時(shí)函數(shù)提升曙博,已存入內(nèi)存
console.log(a) // undefined 變量提升但未賦值
var a = 'Hello world'
function b() {
console.log('call b')
}
此題需要注意拥刻,函數(shù)的變量提升直接將整個(gè)函數(shù)存入內(nèi)存中,而變量則是只聲明變量但是賦值undefined父泳,我們來(lái)看下一題:
b() // call b second
//執(zhí)行b函數(shù)般哼,此時(shí)相同函數(shù)會(huì)覆蓋上一函數(shù),函數(shù)提升優(yōu)于變量提升惠窄。
function b() {
console.log('call b fist')
}
function b() {
console.log('call b second')
}
var b = 'Hello world'
同時(shí)還需注意蒸眠,es6中l(wèi)et聲明也會(huì)被js解析是進(jìn)行變量提升,但是由于let的特性導(dǎo)致聲明的變量不能在聲明前使用杆融。
2.函數(shù)的深拷貝
常用方式:JSON.parse(JSON.stringify(object)) 楞卡、 lodash中的cloneDeep函數(shù)
存在問(wèn)題:
1.該方法會(huì)忽略u(píng)ndefined和symbol
2.不能夠序列化函數(shù)
3.不能解決循環(huán)引用問(wèn)題
接下來(lái)我們看看面試常考的手寫遞歸函數(shù)進(jìn)行深克隆。
我們先從一個(gè)對(duì)象的深克隆流程進(jìn)行分析蒋腮,
分析:首先淘捡,我們清晰對(duì)象中可以包含基礎(chǔ)數(shù)據(jù)類型(string,number,null,undefined,bollen,function)和復(fù)雜數(shù)據(jù)類型(RegExp,Date,Synbol,Set,Map等等),還有dom節(jié)點(diǎn)也可以存在于對(duì)象中池摧,
因此案淋,對(duì)于我們整個(gè)函數(shù)的設(shè)計(jì),我們傳入兩個(gè)參數(shù)险绘,一個(gè)為目標(biāo)對(duì)象source踢京,一個(gè)為我們需要復(fù)制到的目標(biāo)變量target,未傳時(shí)我們?cè)O(shè)計(jì)為undefined宦棺。
1.判斷傳入的target是否為undefined瓣距,如果是的話,我們判斷source是不是dom節(jié)點(diǎn)代咸,是的話蹈丸,使用cloneNode方法進(jìn)行復(fù)制,不是的話進(jìn)行new source.constructor進(jìn)行給target賦值source的相同類型呐芥。
2.通過(guò)Object.getOwnPrototyNames的方法獲取到source的所有屬性逻杖,包括不可枚舉屬性。
3.遍歷我們獲取到的屬性思瘟,再次判斷荸百,有三種情況,第一種:dom節(jié)點(diǎn)滨攻,第二種:基本數(shù)據(jù)類型够话,第三種:復(fù)雜數(shù)據(jù)類型。
4.聲明desc光绕,通過(guò)Object.getOwnPropertypertyDescriptor獲取到此屬性的描述屬性 女嘲,第一種dom節(jié)點(diǎn),使用cloneNode進(jìn)行賦值诞帐,第二種設(shè)計(jì)一個(gè)數(shù)組欣尼,包含所有基本類型,如果使用Includes去處理typeof后的遍歷的項(xiàng)返回值是true停蕉,那么則為基礎(chǔ)類型愕鼓,使用Object.defineProperty去復(fù)制對(duì)象的此條屬性,第三種:進(jìn)行switch判斷谷徙,找到指定的類型進(jìn)行相應(yīng)的處理拒啰,我們都設(shè)置一個(gè)對(duì)象o驯绎,將desc的描述屬性如果是true完慧,我們就添加給o,如果get,set也是屈尼,則同樣添加册着,最后通過(guò)Object.definePropertu加到target的此項(xiàng)復(fù)雜數(shù)據(jù)類型中,再去執(zhí)行遞歸脾歧。
talk is cheap , show me the code
function cloneObj(source, target) { //兩個(gè)參數(shù)甲捏,第一個(gè)是目標(biāo)對(duì)象,第二個(gè)是我們要將對(duì)象賦值的目標(biāo)
var list = ["string", "number", "null", "undefined", "bollen", "function"]
if (target === undefined) {
if (HTMLElement.prototype.isPrototypeOf(source)) {
target = source.cloneNode(false)
} else {
target = new source.constructor() //將target賦值為和傳入的source一樣的類型
}
}
//獲取對(duì)象的所有屬性鞭执,包括不可枚舉的屬性
var names = Object.getOwnPropertyNames(source)
for (var i = 0; i < names.length; i++) { //循環(huán)遍歷我們獲得的屬性
var desc = Object.getOwnPropertyDescriptor(source, names[i]) // 獲得每一個(gè)屬性的描述屬性
if (list.includes(typeof desc.value)) { //基本數(shù)據(jù)類型包括司顿,則添加到target中
Object.defineProperty(target, names[i], desc)
} else { //不是基本屬性
var t;
if (HTMLElement.prototype.isPrototypeOf(desc.value)) {
t = desc.value.cloneNode(false)
} else {
switch (desc.value.constructor) {
case RegExp:
t = new RegExp(desc.value.source, desc.value.flags)
break;
case Date:
t = new Date(desc.value)
break;
case Symbol:
t = Symbol
break;
case Set:
t = new Set(desc.value.values())
break;
case Map:
t = new Map(desc.value.entries())
break;
default:
t = new desc.value.constructor();
break;
}
}
var o = {}
o.value = t
desc.enumerable && (o.enumerable = desc.enumerable)
desc.writable && (o.writable = desc.writable)
desc.configurable && (o.configurable = desc.configurable)
desc.set && (o.set = desc.set)
desc.get && (o.get = desc.get)
Object.defineProperty(target, names[i], o)
cloneObj(desc.value, t)
}
}
return target
}