1. 程式人生 > 其它 >5道BAT大廠面試題,助你夯實js基礎

5道BAT大廠面試題,助你夯實js基礎

技術標籤:面試總結javascript

前言:

最近看到了一篇大廠面試題集錦,在這裡摘出來一些比較有意思的面試題跟大家分享,通過這些試題的分析,加深大家對js的理解,夯實基礎知識。

1.輸出以下程式碼的執行結果

var obj = {
  '2': 3,
  '3': 4,
  'length': 2,
  'splice': Array.prototype.splice,
  'push': Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);

考察點1:

對push的理解,push是根據length

來決定從哪裡開始插入給定的值。

我們來去除其他程式碼,僅保留push來看看控制檯輸出

var obj = {
  '2': 3,
  '3': 4,
  'length': 2,
  // 'splice': Array.prototype.splice,
  'push': Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);

輸出:

{2: 1, 3: 2, length: 4, push: ƒ}
      

可以看到因為length為2,所以push是從下標為2的地方開始插入,下標為2跟3的值被push進去的值覆蓋。

接下來我們修改length

var obj = {
  '2': 3,
  '3': 4,
  'length': 0,
  // 'splice': Array.prototype.splice,
  'push': Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);

輸出:

{0: 1, 1: 2, 2: 3, 3: 4, length: 2, push: ƒ}

可以看到如果length改為0,那麼push是從0開始插入

考察點2:

當一個物件擁有splice函式作為屬性時,控制檯會以陣列形式輸出

var obj = {
  '2': 3,
  '3': 4,
  'length': 2,
  'splice': Array.prototype.splice,
  'push': Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
      
輸出:

[empty × 2, 1, 2, splice: ƒ, push: ƒ]

雖然控制檯輸出的是陣列形式,但是obj依然是物件型別

var obj = {
  '2': 3,
  '3': 4,
  'length': 2,
  'splice': Array.prototype.splice,
  'push': Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
console.log(obj instanceof Array)
console.log(obj instanceof Object)

輸出:
[empty × 2, 1, 2, splice: ƒ, push: ƒ]
false
true

結合兩個考察點,我們可以分析出最終結果

[empty × 2, 1, 2, splice: ƒ, push: ƒ]

2.輸出以下程式碼的執行結果

var a = {n:1};
var b = a; 
a.x = a = {n:2};
console.log(a.x);
console.log(b.x);

考察點1:

連續賦值,順序從右向左。

考察點2:

運算子優先順序,點的優先順序要高於等號。更多運算子優先順序請參閱運算子優先順序

考察點3

引用資料型別的值是指向堆的指標。

我們先看輸出

undefined
{n: 2}

解析:

var a = {n:1};
var b = a;  //將b指向{n:1}
a.x = a = {n:2};
// 1. a.x 會在{n:1,x:undefined}
// 2. 將a重新指向{n:2}
// 3. 將{n:1,x:undefined}中的x指向{n:2}
// 4. 此時 b: {n:1,x:{n:2}}, a: {n:2}
console.log(a.x);
console.log(b.x);

3.輸出以下程式碼的執行結果

var a = 10;
(function(){
 console.log(a);
 a=5;
 console.log(window.a);
 var a = 20;
 console.log(a)
})()

考察點1:

變數提升,es6之前只存在全域性作用域跟函式作用域,變數提升就是將變數定義提升到當前作用域的開始。

考察點2:

作用域,es6之前只存在全域性作用域跟函式作用域,當賦值變數或者查詢變數時,會現在當前作用域查詢,如果沒有找到會向上級作用域查詢,直到找到全域性作用域。

解析:

var a = 10;
(function(){
 console.log(a);
 a=5;
 console.log(window.a);
 var a = 20;
 console.log(a)
})()
結果:
undefined 10 20
// 1. 立即執行函式內的 var a = 20; 會先進行變數提升。 var a = undefined;會提升到函式作用域頂部
// 2. 這時候console.log(a) 的a為undefined
// 3. console.log(window.a),這時候取的是window的a變數,這時候全域性物件下的a變數為10
// 4. 執行到var a = 20; 這時候會在當前作用域查詢a變數,找到了以後賦值為20
// 5. 這時候輸出console.log(a) 這時候a的值為20

4.下面程式碼中a在什麼情況下會列印1?

var a = ?;
if(a == 1 && a == 2 && a == 3){
 console.log(1);
}

考察點1:

隱式型別轉換,當進行±等運算或者==時,會進行隱式型別轉換。

考察點2:

物件轉為原始型別時會先呼叫自身valueOf方法,如果沒有返回原始值,會繼續呼叫自身的toString方法,如果還是沒有返回原始型別,則丟擲異常。

var a = {
 i:1,
 toString:function(){
   console.log('toString',this.i)
   return this.i++
 },
 valueOf:function(){
   console.log('valueOf',this.i)
   return this.i++
 }
};
if(a == 1 && a == 2 && a == 3){
 console.log(1);
}

輸出:
valueOf 1
valueOf 2
valueOf 3
1
// 1. a為一個物件,在轉換為數字時會呼叫valueOf
// 2. valueOf執行完畢以後再次轉換時會繼續呼叫valueOf
// 3. 當()內的表示式執行完畢後a內的i變為4

5.輸出以下程式碼的執行結果

var b = 10;
(function b(){
  b=20;
  console.log(b)
})()
console.log(b)

考察點1:

立即執行表示式會生成一個區域性作用域,起到隔離引數的作用。

考察點2:

函式提升要高於變數提升
例子:

var a = 1;
function a(){

}
console.log(a)
輸出:
1

由此可見,函式的優先順序要高於var變數定義。

考察點3:

具名函式表示式只能由內部訪問,並且具名函式表示式的name無法重新賦值

舉個例子:

var a = function b(){
console.log(b);
b=1;
console.log();
}
輸出:
ƒ b(){
  console.log(b);
  b=1;
  console.log(b)
}

ƒ b(){
  console.log(b);
  b=1;
  console.log(b)
}

解析:

var b = 10;
(function b(){
  b=20;
  console.log(b)
})()
console.log(b)
輸出:
ƒ b(){
  b=20;
  console.log(b)
}
10

// 1. 立即執行函式有兩種定義方式
      (1). (function(){})()
      (2). (function(){}())
      (3). !、,、+、-等符號 !function(){}()
// 2. 當函式被()包裹以後,這個函式就不是函式聲明瞭,就變成了一個函式表示式
// 3. 具名函式表示式內部的b無法重新定義,所以輸出的是b函式
// 4. b=20 賦值查詢當前作用域,查到了具名函式表示式的name,但是無法重新賦值,查詢到了不再繼續向上查詢
// 4. 最後的console輸出的是全域性變數b,所以是10

如果想獲取更多內容,可以掃描下方二維碼,一起學習,一起進步。
左道前端