新聞中心
第一題
let a = 1
function b(a) {
a = 2
console.log(a)
}
b(a)
console.log(a)
復(fù)制代碼
答案

創(chuàng)新互聯(lián)專注于泰和網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供泰和營銷型網(wǎng)站建設(shè),泰和網(wǎng)站制作、泰和網(wǎng)頁設(shè)計、泰和網(wǎng)站官網(wǎng)定制、微信小程序服務(wù),打造泰和網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供泰和網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
2、1
解析
首先基本類型數(shù)據(jù)是按值傳遞的,所以執(zhí)行b函數(shù)時,b的參數(shù)a接收的值為1,參數(shù)a相當(dāng)于函數(shù)內(nèi)部的變量,當(dāng)本作用域有和上層作用域同名的變量時,無法訪問到上層變量,所以函數(shù)內(nèi)無論怎么修改a,都不影響上層,所以函數(shù)內(nèi)部打印的a是2,外面打印的仍是1。
第二題
function a (b = c, c = 1) {
console.log(b, c)
}
a()
復(fù)制代碼
答案
報錯
解析
給函數(shù)多個參數(shù)設(shè)置默認(rèn)值實際上跟按順序定義變量一樣,所以會存在暫時性死區(qū)的問題,即前面定義的變量不能引用后面還未定義的變量,而后面的可以訪問前面的。
第三題
let a = b = 10
;(function(){
let a = b = 20
})()
console.log(a)
console.log(b)
復(fù)制代碼
答案
10、20
解析
連等操作是從右向左執(zhí)行的,相當(dāng)于b = 10、let a = b,很明顯b沒有聲明就直接賦值了,所以會隱式創(chuàng)建為一個全局變量,函數(shù)內(nèi)的也是一樣,并沒有聲明b,直接就對b賦值了,因為作用域鏈,會一層一層向上查找,找了到全局的b,所以全局的b就被修改為20了,而函數(shù)內(nèi)的a因為重新聲明了,所以只是局部變量,不影響全局的a,所以a還是10。
第四題
var a = {n:1}
var b = a
a.x = a = {n:2}
console.log(a.x)
console.log(b.x)
復(fù)制代碼
答案
undefined、{n: 2}
解析
恕筆者不才,這道題筆者做一次錯一次。
反正按照網(wǎng)上大部分的解釋是因為.運(yùn)算符優(yōu)先級最高,所以會先執(zhí)行a.x,此時a、b共同指向的{n: 1}變成了{(lán)n: 1, x: undefined},然后按照連等操作從右到左執(zhí)行代碼,a = {n: 2},顯然,a現(xiàn)在指向了一個新對象,然后a.x = a,因為a.x最開始就執(zhí)行過了,所以這里其實等價于:({n: 1, x: undefined}).x = b.x = a = {n: 2}。
第五題
var arr = [0, 1, 2]
arr[10] = 10
console.log(arr.filter(function (x) {
return x === undefined
}))
復(fù)制代碼
答案
[]
解析
這題比較簡單,arr[10]=10,那么索引3到9位置上都是undefined,arr[3]等打印出來也確實是undefined,但是,這里其實涉及到ECMAScript版本不同對應(yīng)方法行為不同的問題,ES6之前的遍歷方法都會跳過數(shù)組未賦值過的位置,也就是空位,但是ES6新增的for of方法就不會跳過。
第六題
var name = 'World'
;(function () {
if (typeof name === 'undefined') {
var name = "Jack"
console.info('Goodbye ' + name)
} else {
console.info('Hello ' + name)
}
})()
復(fù)制代碼
答案
Goodbye Jack
解析
這道題考察的是變量提升的問題,var聲明變量時會把變量自動提升到當(dāng)前作用域頂部,所以函數(shù)內(nèi)的name雖然是在if分支里聲明的,但是也會提升到外層,因為和全局的變量name重名,所以訪問不到外層的name,最后因為已聲明未賦值的變量的值都為undefined,導(dǎo)致if的第一個分支滿足條件。
第七題
console.log(1 + NaN)
console.log("1" + 3)
console.log(1 + undefined)
console.log(1 + null)
console.log(1 + {})
console.log(1 + [])
console.log([] + {})
復(fù)制代碼
答案
NaN、13、NaN、1、1[object Object]、1、[object Object]
解析
這道題考察的顯然是+號的行為:
1.如果有一個操作數(shù)是字符串,那么把另一個操作數(shù)轉(zhuǎn)成字符串執(zhí)行連接
2.如果有一個操作數(shù)是對象,那么調(diào)用對象的valueOf方法轉(zhuǎn)成原始值,如果沒有該方法或調(diào)用后仍是非原始值,則調(diào)用toString方法
3.其他情況下,兩個操作數(shù)都會被轉(zhuǎn)成數(shù)字執(zhí)行加法操作
第八題
var a={},
b={key:'b'},
c={key:'c'}
a[b]=123
a[c]=456
console.log(a[b])
復(fù)制代碼
答案
456
解析
對象有兩種方法設(shè)置和引用屬性,obj.name和obj['name'],方括號里可以字符串、數(shù)字和變量設(shè)置是表達(dá)式等,但是最終計算出來得是一個字符串,對于上面的b和c,它們兩個都是對象,所以會調(diào)用toString()方法轉(zhuǎn)成字符串,對象轉(zhuǎn)成字符串和數(shù)組不一樣,和內(nèi)容無關(guān),結(jié)果都是[object Obejct],所以a[b]=a[c]=a['[object Object]']。
第九題
var out = 25
var inner = {
out: 20,
func: function () {
var out = 30
return this.out
}
};
console.log((inner.func, inner.func)())
console.log(inner.func())
console.log((inner.func)())
console.log((inner.func = inner.func)())
復(fù)制代碼
答案
25、20、20、25
解析
這道題考察的是this指向問題:
1.逗號操作符會返回表達(dá)式中的最后一個值,這里為inner.func對應(yīng)的函數(shù),注意是函數(shù)本身,然后執(zhí)行該函數(shù),該函數(shù)并不是通過對象的方法調(diào)用,而是在全局環(huán)境下調(diào)用,所以this指向window,打印出來的當(dāng)然是window下的out
2.這個顯然是以對象的方法調(diào)用,那么this指向該對象
3.加了個括號,看起來有點迷惑人,但實際上(inner.func)和inner.func是完全相等的,所以還是作為對象的方法調(diào)用
4.賦值表達(dá)式和逗號表達(dá)式相似,都是返回的值本身,所以也相對于在全局環(huán)境下調(diào)用函數(shù)
第十題
let {a,b,c} = { c:3, b:2, a:1 }
console.log(a, b, c)
復(fù)制代碼
答案
1、2、3
解析
這題考察的是變量解構(gòu)賦值的問題,數(shù)組解構(gòu)賦值是按位置對應(yīng)的,而對象只要變量與屬性同名,順序隨意。
第十一題
console.log(Object.assign([1, 2, 3], [4, 5]))
復(fù)制代碼
答案
[4, 5, 3]
解析
是不是從來沒有用assign方法合并過數(shù)組?assign方法可以用于處理數(shù)組,不過會把數(shù)組視為對象,比如這里會把目標(biāo)數(shù)組視為是屬性為0、1、2的對象,所以源數(shù)組的0、1屬性的值覆蓋了目標(biāo)對象的值。
第十二題
var x=1
switch(x++)
{
case 0: ++x
case 1: ++x
case 2: ++x
}
console.log(x)
復(fù)制代碼
答案
4
解析
這題考查的是自增運(yùn)算符的前綴版和后綴版,以及switch的語法,后綴版的自增運(yùn)算符會在語句被求值后才發(fā)生,所以x會仍以1的值去匹配case分支,那么顯然匹配到為1的分支,此時,x++生效,x變成2,再執(zhí)行++x,變成3,因為沒有break語句,所以會進(jìn)入當(dāng)前case后面的分支,所以再次++x,最終變成4。
第十三題
console.log(typeof undefined == typeof NULL)
console.log(typeof function () {} == typeof class {})
復(fù)制代碼
答案
true、true
解析
1.首先不要把NULL看成是null,js的關(guān)鍵字是區(qū)分大小寫的,所以這就是一個普通的變量,而且沒有聲明,typeof對沒有聲明的變量使用是不會報錯的,返回'undefined',typeof對undefined使用也是'undefined',所以兩者相等
2.typeof對函數(shù)使用返回'function',class只是es6新增的語法糖,本質(zhì)上還是函數(shù),所以兩者相等
第十四題
var count = 0
console.log(typeof count === "number")
console.log(!!typeof count === "number")
復(fù)制代碼
答案
true、false
解析
1.沒啥好說的,typeof對數(shù)字類型返回'number'。
2.這題考查的是運(yùn)算符優(yōu)先級的問題,邏輯非!的優(yōu)先級比全等===高,所以先執(zhí)行!!typeof count,結(jié)果為true,然后執(zhí)行true === 'number',結(jié)果當(dāng)然為false,可以點擊這里查看優(yōu)先級列表:點我[1]。
第十五題
"use strict"
a = 1
var a = 2
console.log(window.a)
console.log(a)
復(fù)制代碼
答案
2、2
解析
var聲明會把變量提升到當(dāng)前作用域頂部,所以a=1并不會報錯,另外在全局作用域下使用var聲明變量,該變量會變成window的一個屬性,以上兩點都和是否在嚴(yán)格模式下無關(guān)。
第十六題
var i = 1
function b() {
console.log(i)
}
function a() {
var i = 2
b()
}
a()
復(fù)制代碼
答案
1
解析
這道題考察的是作用域的問題,作用域其實就是一套變量的查找規(guī)則,每個函數(shù)在執(zhí)行時都會創(chuàng)建一個執(zhí)行上下文,其中會關(guān)聯(lián)一個變量對象,也就是它的作用域,上面保存著該函數(shù)能訪問的所有變量,另外上下文中的代碼在執(zhí)行時還會創(chuàng)建一個作用域鏈,如果某個標(biāo)識符在當(dāng)前作用域中沒有找到,會沿著外層作用域繼續(xù)查找,直到最頂端的全局作用域,因為js是詞法作用域,在寫代碼階段就作用域就已經(jīng)確定了,換句話說,是在函數(shù)定義的時候確定的,而不是執(zhí)行的時候,所以a函數(shù)是在全局作用域中定義的,雖然在b函數(shù)內(nèi)調(diào)用,但是它只能訪問到全局的作用域而不能訪問到b函數(shù)的作用域。
第十七題
var obj = {
name: 'abc',
fn: () => {
console.log(this.name)
}
};
obj.name = 'bcd'
obj.fn()
復(fù)制代碼
答案
undefined
解析
這道題考察的是this的指向問題,箭頭函數(shù)執(zhí)行的時候上下文是不會綁定this的,所以它里面的this取決于外層的this,這里函數(shù)執(zhí)行的時候外層是全局作用域,所以this指向window,window對象下沒有name屬性,所以是undefined。
第十八題
const obj = {
a: {
a: 1
}
};
const obj1 = {
a: {
b: 1
}
};
console.log(Object.assign(obj, obj1))
復(fù)制代碼
答案
{a: {b: 1}}
解析
這道題很簡單,因為assign方法執(zhí)行的是淺拷貝,所以源對象的a屬性會直接覆蓋目標(biāo)對象的a屬性。
第十九題
console.log(a)
var a = 1
var getNum = function() {
a = 2
}
function getNum() {
a = 3
}
console.log(a)
getNum()
console.log(a)
復(fù)制代碼
答案
undefined、1、2
解析
首先因為var聲明的變量提升作用,所以a變量被提升到頂部,未賦值,所以第一個打印出來的是undefined。
接下來是函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別,函數(shù)聲明會有提升作用,在代碼執(zhí)行前就把函數(shù)提升到頂部,在執(zhí)行上下文上中生成函數(shù)定義,所以第二個getNum會被最先提升到頂部,然后是var聲明getNum的提升,但是因為getNum函數(shù)已經(jīng)被聲明了,所以就不需要再聲明一個同名變量,接下來開始執(zhí)行代碼,執(zhí)行到var getNum = fun...時,雖然聲明被提前了,但是賦值操作還是留在這里,所以getNum被賦值為了一個函數(shù),下面的函數(shù)聲明直接跳過,最后,getNum函數(shù)執(zhí)行前a打印出來還是1,執(zhí)行后,a被修改成了2,所以最后打印出來的2。
第二十題
var scope = 'global scope'
function a(){
function b(){
console.log(scope)
}
return b
var scope = 'local scope'
}
a()()
復(fù)制代碼
答案
undefined
解析
這題考查的還是變量提升和作用域的問題,雖然var聲明是在return語句后面,但還是會提升到a函數(shù)作用域的頂部,然后又因為作用域是在函數(shù)定義的時候確定的,與調(diào)用位置無關(guān),所以b的上層作用域是a函數(shù),scope在b自身的作用域里沒有找到,向上查找找到了自動提升的并且未賦值的scope變量,所以打印出undefined。
第二十一題
function fn (){
console.log(this)
}
var arr = [fn]
arr[0]()
復(fù)制代碼
答案
打印出arr數(shù)組本身
解析
函數(shù)作為某個對象的方法調(diào)用,this指向該對象,數(shù)組顯然也是對象,只不過我們都習(xí)慣了對象引用屬性的方法:obj.fn,但是實際上obj['fn']引用也是可以的。
第二十二題
var a = 1
function a(){}
console.log(a)
var b
function b(){}
console.log(b)
function b(){}
var b
console.log(b)
復(fù)制代碼
答案
1、b函數(shù)本身、b函數(shù)本身
解析
這三小題都涉及到函數(shù)聲明和var聲明,這兩者都會發(fā)生提升,但是函數(shù)會優(yōu)先提升,所以如果變量和函數(shù)同名的話,變量的提升就忽略了。
1.提升完后,執(zhí)行到賦值代碼,a被賦值成了1,函數(shù)因為已經(jīng)聲明提升了,所以跳過,最后打印a就是1。
2.和第一題類似,只是b沒有賦值操作,那么執(zhí)行到這兩行相當(dāng)于都沒有操作,b當(dāng)然是函數(shù)。
3.和第二題類似,只是先后順序換了一下,但是并不影響兩者的提升順序,仍是函數(shù)優(yōu)先,同名的var聲明提升忽略,所以打印出b還是函數(shù)。
第二十三題
function Foo() {
getName = function () { console.log(1) }
return this
}
Foo.getName = function () { console.log(2) }
Foo.prototype.getName = function () { console.log(3) }
var getName = function () { console.log(4) }
function getName() { console.log(5) }
//請寫出以下輸出結(jié)果:
Foo.getName()
getName()
Foo().getName()
getName()
new Foo.getName()
new Foo().getName()
new new Foo().getName()
復(fù)制代碼
答案
2、4、1、1、2、3、3
解析
這是一道綜合性題目,首先getName函數(shù)聲明會先提升,然后getName函數(shù)表達(dá)式提升,但是因為函數(shù)聲明提升在線,所以忽略函數(shù)表達(dá)式的提升,然后開始執(zhí)行代碼,執(zhí)行到var getName= ...時,修改了getName的值,賦值成了打印4的新函數(shù)。
1.執(zhí)行Foo函數(shù)的靜態(tài)方法,打印出2。
2.執(zhí)行g(shù)etName,當(dāng)前getName是打印出4的那個函數(shù)。
3.執(zhí)行Foo函數(shù),修改了全局變量getName,賦值成了打印1的函數(shù),然后返回this,因為是在全局環(huán)境下執(zhí)行,所以this指向window,因為getName已經(jīng)被修改了,所以打印出1。
4.因為getName沒有被重新賦值,所以再執(zhí)行仍然打印出1。
5.new操作符是用來調(diào)用函數(shù)的,所以new Foo.getName()相當(dāng)于new (Foo.getName)(),所以new的是Foo的靜態(tài)方法getName,打印出2。
6.因為點運(yùn)算符(.)的優(yōu)先級和new是一樣高的,所以從左往右執(zhí)行,相當(dāng)于(new Foo()).getName(),對Foo使用new調(diào)用會返回一個新創(chuàng)建的對象,然后執(zhí)行該對象的getName方法,該對象本身并沒有該方法,所以會從Foo的原型對象上查找,找到了,所以打印出3。
7.和上題一樣,點運(yùn)算符(.)的優(yōu)先級和new一樣高,另外new是用來調(diào)用函數(shù)的,所以new new Foo().getName()相當(dāng)于new ((new Foo()).getName)(),括號里面的就是上一題,所以最后找到的是Foo原型上的方法,無論是直接調(diào)用,還是通過new調(diào)用,都會執(zhí)行該方法,所以打印出3。
第二十六題
const person = {
address: {
country:"china",
city:"hangzhou"
},
say: function () {
console.log(`it's ${this.name}, from ${this.address.country}`)
},
setCountry:function (country) {
this.address.country=country
}
}
const p1 = Object.create(person)
const p2 = Object.create(person)
p1.name = "Matthew"
p1.setCountry("American")
p2.name = "Bob"
p2.setCountry("England")
p1.say()
p2.say()
復(fù)制代碼
答案
it's Matthew, from England
it's Bob, from England
解析
Object.create方法會創(chuàng)建一個對象,并且將該對象的__proto__屬性指向傳入的對象,所以p1和p2兩個對象的原型對象指向了同一個對象,接著給p1添加了一個name屬性,然后調(diào)用了p1的setCountry方法,p1本身是沒有這個方法的,所以會沿著原型鏈進(jìn)行查找,在它的原型上,也就是person對象上找到了這個方法,執(zhí)行這個方法會給address對象的country屬性設(shè)置傳入的值,p1本身也是沒有address屬性的,但是和name屬性不一樣,address屬性在原型對象上找到了,并且因為是個引用值,所以會成功修改它的country屬性,接著對p2的操作也是一樣,然后因為原型中存在引用值會在所有實例中共享,所以p1和p2它們引用的address也是同一個對象,一個實例修改了,會反映到所有實例上,所以p2的修改會覆蓋p1的修改,最終country的值為England。
第二十五題
setTimeout(function() {
console.log(1)
}, 0)
new Promise(function(resolve) {
console.log(2)
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve()
}
console.log(3)
}).then(function() {
console.log(4)
})
console.log(5)
復(fù)制代碼
答案
2、3、5、4、1
解析
這道題顯然考察的是事件循環(huán)的知識點。
js是一門單線程的語言,但是為了執(zhí)行一些異步任務(wù)時不阻塞代碼,以及避免等待期間的資源浪費(fèi),js存在事件循環(huán)的機(jī)制,單線程指的是執(zhí)行js的線程,稱作主線程,其他還有一些比如網(wǎng)絡(luò)請求的線程、定時器的線程,主線程在運(yùn)行時會產(chǎn)生執(zhí)行棧,棧中的代碼如果調(diào)用了異步api的話則會把事件添加到事件隊列里,只要該異步任務(wù)有了結(jié)果便會把對應(yīng)的回調(diào)放到【任務(wù)隊列】里,當(dāng)執(zhí)行棧中的代碼執(zhí)行完畢后會去讀取任務(wù)隊列里的任務(wù),放到主線程執(zhí)行,當(dāng)執(zhí)行??樟擞謺z查,如此往復(fù),也就是所謂的事件循環(huán)。
異步任務(wù)又分為【宏任務(wù)】(比如setTimeout、setInterval)和【微任務(wù)】(比如promise),它們分別會進(jìn)入不同的隊列,執(zhí)行棧為空完后會優(yōu)先檢查微任務(wù)隊列,如果有微任務(wù)的話會一次性執(zhí)行完所有的微任務(wù),然后去宏任務(wù)隊列里檢查,如果有則取出一個任務(wù)到主線程執(zhí)行,執(zhí)行完后又會去檢查微任務(wù)隊列,如此循環(huán)。
回到這題,首先整體代碼作為一個宏任務(wù)開始執(zhí)行,遇到setTimeout,相應(yīng)回調(diào)會進(jìn)入宏任務(wù)隊列,然后是promise,promise的回調(diào)是同步代碼,所以會打印出2,for循環(huán)結(jié)束后調(diào)用了resolve,所以then的回調(diào)會被放入微任務(wù)隊列,然后打印出3,最后打印出5,到這里當(dāng)前的執(zhí)行棧就空了,那么先檢查微任務(wù)隊列,發(fā)現(xiàn)有一個任務(wù),那么取出來放到主線程執(zhí)行,打印出4,最后檢查宏任務(wù)隊列,把定時器的回調(diào)放入主線程執(zhí)行,打印出1。
第二十六題
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
});
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5');
});
});
process.nextTick(function() {
console.log('6');
});
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8');
});
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
});
})
復(fù)制代碼
答案
1、7、6、8、2、4、9、11、3、10、5、12
解析
這道題和上一題差不多,但是出現(xiàn)了process.nextTick,所以顯然是在node環(huán)境下,node也存在事件循環(huán)的概念,但是和瀏覽器的有點不一樣,nodejs中的宏任務(wù)被分成了幾種不同的階段,兩個定時器屬于timers階段,setImmediate屬于check階段,socket的關(guān)閉事件屬于close callbacks階段,其他所有的宏任務(wù)都屬于poll階段,除此之外,只要執(zhí)行到前面說的某個階段,那么會執(zhí)行完該階段所有的任務(wù),這一點和瀏覽器不一樣,瀏覽器是每次取一個宏任務(wù)出來執(zhí)行,執(zhí)行完后就跑去檢查微任務(wù)隊列了,但是nodejs是來都來了,一次全部執(zhí)行完該階段的任務(wù)好了,那么process.nextTick和微任務(wù)在什么階段執(zhí)行呢,在前面說的每個階段的后面都會執(zhí)行,但是process.nextTick會優(yōu)先于微任務(wù),一圖勝千言:
理解了以后再來分析這道題就很簡單了,首先執(zhí)行整體代碼,先打印出1,setTimeout回調(diào)扔進(jìn)timers隊列,nextTick的扔進(jìn)nextTick的隊列,promise的回調(diào)是同步代碼,執(zhí)行后打印出7,then回調(diào)扔進(jìn)微任務(wù)隊列,然后又是一個setTimeout回調(diào)扔進(jìn)timers隊列,到這里當(dāng)前節(jié)點就結(jié)束了,檢查nextTick和微任務(wù)隊列,nextTick隊列有任務(wù),執(zhí)行后打印出6,微任務(wù)隊列也有,打印出8,接下來按順序檢查各個階段,check隊列、close callbacks隊列都沒有任務(wù),到了timers階段,發(fā)現(xiàn)有兩個任務(wù),先執(zhí)行第一個,打印出2,然后nextTick的扔進(jìn)nextTick的隊列,執(zhí)行promise打印出4,then回調(diào)扔進(jìn)微任務(wù)隊列,再執(zhí)行第二個setTimeout的回調(diào),打印出9,然后和剛才一樣,nextTick的扔進(jìn)nextTick的隊列,執(zhí)行promise打印出11,then回調(diào)扔進(jìn)微任務(wù)隊列,到這里timers階段也結(jié)束了,執(zhí)行nextTick隊列的任務(wù),發(fā)現(xiàn)又兩個任務(wù),依次執(zhí)行,打印出3和10,然后檢查微任務(wù)隊列,也是兩個任務(wù),依次執(zhí)行,打印出5和12,到這里是有隊列都清空了。
當(dāng)前題目:2022年JS燒腦面試題,你能答對幾道?
轉(zhuǎn)載來源:http://fisionsoft.com.cn/article/djjjhdg.html


咨詢
建站咨詢
