原文:https://dev.to/maxpou/typical-javascript-interview-exercises-explained
題目一
下面的代碼,我想要打印出hey amy,結(jié)果卻打印出hey arnold,為什么?
function greet (person) {
if (person == { name: 'amy' }) {
return 'hey amy'
} else {
return 'hey arnold'
}
}
greet({ name: 'amy' })
答案
這里問(wèn)題出在{ name: 'amy' } != { name: 'amy' }。當(dāng)比較兩個(gè)對(duì)象時(shí),JavaScript都會(huì)比較對(duì)象在內(nèi)存中的引用地址粘衬。這個(gè)例子中,兩個(gè)對(duì)象雖然都有相同的屬性和值咳促,但它們?cè)趦?nèi)存中地址是不同的稚新,所以是兩個(gè)不同的對(duì)象。
正確的解決方法應(yīng)該是比較對(duì)象屬性的值:
function greet (person) {
if (person.name === 'amy') {
return 'hey amy'
}
return 'hey arnold'
}
greet({ name: 'amy' }) // "hey amy"
題目二
我想讓下面的代碼按順序輸出0跪腹,1褂删,2,3冲茸,但是運(yùn)行結(jié)果卻不符合我的預(yù)期屯阀,為什么以及怎么解決?
for (var i = 0; i < 4; i++) {
setTimeout(() => console.log(i), 0)
}
問(wèn)題
我喜歡這道題轴术,因?yàn)樗悬c(diǎn)棘手难衰,并且它涉及到作用域和JavaScript的事件循環(huán)。
這里的陷阱是零延遲逗栽。setTimeout(callback, 0)并不意味著callback函數(shù)會(huì)在0毫秒后執(zhí)行盖袭。
下面是事件循環(huán)的經(jīng)過(guò):
- 當(dāng)前調(diào)用棧設(shè)為第一個(gè)setTimeout()
- window.setTimeout()是一個(gè)web api,在經(jīng)過(guò)0毫秒后彼宠,回調(diào)函數(shù)(在這里是匿名函數(shù))會(huì)被放到隊(duì)列中鳄虱,而不是調(diào)用棧中。
- 當(dāng)調(diào)用椘鞠浚空閑時(shí)拙已,for循環(huán)會(huì)將第二個(gè)setTimeout放進(jìn)來(lái),然后將第二個(gè)callback放到隊(duì)列中摧冀。咪奖。。直到循環(huán)結(jié)束钥勋。
- 當(dāng)for循環(huán)結(jié)束i === 4時(shí),JS就會(huì)執(zhí)行隊(duì)列里的callback函數(shù)了。每一個(gè)console.log(i)會(huì)將i打印出來(lái)楼镐,也就是4癞志。
用動(dòng)畫(huà)演示整個(gè)事件循環(huán)的過(guò)程:Loupe (try it it's fun!)
還有一個(gè)問(wèn)題是變量i的作用域的問(wèn)題,我們可以看到4個(gè)setTimeout函數(shù)都共享著同一個(gè)i = 4(并不是第一個(gè)setTimeout的i = 0框产,第二個(gè)i = 1等等)
答案
針對(duì)以上問(wèn)題我們有不同的解決方案:
- 使用立即自執(zhí)行函數(shù)(IIFE)凄杯,這種“包裹”函數(shù)會(huì)在它被定義時(shí)立即執(zhí)行:
for (let i = 0; i < 4; i++) {
(function (i) {
setTimeout(() => console.log(i), 0)
})(i)
}
- 用let代替var。let作用于塊作用域秉宿,能讓作用域更容易理解戒突。
for (let i = 0; i < 4; i++) {
setTimeout(() => console.log(i), 0)
}
題目三
我想讓下面的代碼輸出doggo,但結(jié)果卻輸出undefined!
let dog = {
name: 'doggo',
sayName () {
console.log(this.name)
}
}
let sayName = dog.sayName
sayName()
答案
上面的代碼執(zhí)行返回undefined描睦,為什么呢膊存?首先,我們定義了一個(gè)對(duì)象名叫dog忱叭,這個(gè)對(duì)象有兩個(gè)屬性隔崎,分別是name和sayName方法。然后我們?cè)俣x了一個(gè)變量叫做sayName韵丑,并將dog對(duì)象的sayName方法賦值給它爵卒。最后在當(dāng)前環(huán)境也即是全局環(huán)境執(zhí)行sayName方法,這時(shí)函數(shù)里的this指向的是window撵彻,而window對(duì)象下面并沒(méi)有name屬性钓株,所以返回undefined.
- 如果我們要訪問(wèn)dog對(duì)象的name屬性,我們就要將sayName的執(zhí)行環(huán)境變成dog:
sayName.bind(dog)()
// 或者:
dog.sayName.bind(dog)()
- 直接在dog對(duì)象上調(diào)用sayName方法:
let dog = {
name: 'doggo',
sayName () {
console.log(this.name)
}
}
dog.sayName() // will log "doggo"
題目四
我想執(zhí)行bark方法陌僵,但卻報(bào)錯(cuò)了轴合,為什么?
function Dog (name) {
this.name = name
}
Dog.bark = function () {
console.log(this.name + ' says woof')
}
let fido = new Dog('fido')
fido.bark()
答案
執(zhí)行上面代碼碗短,我們會(huì)得到如下錯(cuò)誤:TypeError: fido.bark is not a function. 上面的代碼我們給函數(shù)Dog(同時(shí)也是構(gòu)造器)增加一個(gè)bark方法值桩。這在JavaScript中是可以的,因?yàn)楹瘮?shù)也是對(duì)象豪椿。(譯者補(bǔ)充:bark是Dog的靜態(tài)方法奔坟,fido是Dog的實(shí)例對(duì)象,實(shí)例對(duì)象無(wú)法訪問(wèn)構(gòu)造器的靜態(tài)方法)搭盾。
- 雖然fido.bark不是一個(gè)方法咳秉,但Dog.bark是,所以我們可以用之前的方法來(lái)處理鸯隅,就是用function.prototype.bind來(lái)改變this的指向:
var boundedBark = Dog.bark.bind(fido)
boundedBark() // "fido says woof"
- 將bark方法放在Dog的原型上:
function Dog (name) {
this.name = name
}
Dog.prototype.bark = function () {
console.log(this.name + ' says woof')
}
let fido = new Dog('fido')
fido.bark() // "fido says woof"
我們還可以使用ES2015中的class關(guān)鍵字澜建,但其實(shí)它是上面代碼的語(yǔ)法糖向挖。
class Dog {
constructor (name) {
this.name = name
}
bark () {
console.log(this.name + ' says woof')
}
}
let fido = new Dog('fido')
fido.bark() // "fido says woof"
題目五
為什么下面代碼執(zhí)行的結(jié)果是這樣的?
function isBig (thing) {
if (thing == 0 || thing == 1 || thing == 2) {
return false
}
return true
}
isBig(1) // false
isBig([2]) // false
isBig([3]) // true
答案
因?yàn)槲覀冊(cè)谶@里使用了==而不是嚴(yán)格相等===炕舵,也就是不進(jìn)行類型的比較:
- 調(diào)用isBig(1)何之,這時(shí)thing == 1,結(jié)果符合我們的預(yù)期咽筋,返回false溶推。
- 調(diào)用isBig([2]),thing == [2]奸攻,這時(shí)發(fā)生了什么呢蒜危?當(dāng)比較一個(gè)數(shù)組和數(shù)字時(shí),數(shù)組會(huì)轉(zhuǎn)化成數(shù)字睹耐。這是Abstract Equality Comparison Algorithm的一部分辐赞,所以[2] == 2。
我們應(yīng)該避免使用==
// weird results
[] == ![] // true
[] == false // true
// Non transitive relation
"1" == true // true
"01" == true // true
"01" == "1" // false
題目六(彩蛋無(wú)答案)
如何讓heroes數(shù)組不可變硝训?
const heroes = [
{ name: 'Wolverine', family: 'Marvel', isEvil: false },
{ name: 'Deadpool', family: 'Marvel', isEvil: false },
{ name: 'Magneto', family: 'Marvel', isEvil: true },
{ name: 'Charles Xavier', family: 'Marvel', isEvil: false },
{ name: 'Batman', family: 'DC Comics', isEvil: false },
{ name: 'Harley Quinn', family: 'DC Comics', isEvil: true },
{ name: 'Legolas', family: 'Tolkien', isEvil: false },
{ name: 'Gandalf', family: 'Tolkien', isEvil: false },
{ name: 'Saruman', family: 'Tolkien', isEvil: true }
]
const newHeroes = heroes.map(h => {
h.name = h.name.toUpperCase()
return h
})
你的答案是什么响委?