兩道前端的面試題,求各位大神解答一下?
第一題:
var a = [];
for (var i = 0; i &< 10; i++) {a[i] = function () {return i;}
}a[8]();問:結果是多少?
正確答案是10,我也知道是怎麼來的,可是面試官說,上面的代碼如何能獲取正確結果,請用ES5和ES6分別作答,這個我就不懂了。
第二題:
console.log(text)
function text() {}var text = 0;console.log(text);結果我是知道的,第一個就是function,第二個是0,但是面試官問我為什麼?如果把第二行和第三行互換,結果是怎樣,為什麼?
第一題是標準的變數作用域問題,es5用閉包解決,es6把 var 換成 let
第二題是標準的變數提升問題,寫成變數提升後等價代碼,很容易理解輸出結果
function text() {}
var text; // 因為已有同名函數,此行會被 js 引擎忽略
console.log(text)
text = 0;
console.log(text);
第一道題考你對作用域的理解,結果是10,因為作用域分為全局作用域和函數作用域,這裡的i當你調用這個函數的時候,已經是循環後的i了,也就是10。
es5的方法是用立即執行函數創建獨立作用域,如下
for (var i = 0; i &< 10; i++) {
a[i] = function bar(i){
return function () {
return i;
};
}(i);
}
a[8]();
es6的方法直接var改為let,這樣子for 的 i 變數就有塊作用域,如下
var a = [];
for (leti = 0; i &< 10; i++) {
a[i] = function () {
return i;
}
}
第二道題考你變數名和函數聲明都要提升,為什麼?因為代碼解釋器會預編譯成:
function text() {}
var text;
console.log(text)
text = 0;
console.log(text);
交換第二行和第三行位置,如下:
console.log(text)
var text = 0;
function text() {}
console.log(text);
解釋器同樣會預編譯成:
function text() {}
var text;
console.log(text)
text = 0;
console.log(text);
所以結果還是一樣
....不知道我這樣解釋能看懂么。。
謝謝指正,已經修改。函數優先順序高所以在變數名之前,變數名不會覆蓋函數名。
為什麼面試官們總是糾結在js的設計缺陷上做文章呢?這樣做意義何在?
考察基本功有演算法和數據結構。
考察業務能力,可以讓面試者寫個樹形菜單什麼的。
var根本就是應該禁用的東西好嗎!var 沒有塊級作用域,所以 i 是聲明在 for 循環之外的
在調用方法時,依次向上查找,找到的最近的一個 i 已經是循環結束的 i 了
ES5 的解決方法就是使用 IIFE 創造一個新的作用域,讓每次循環的 i 都保留在這個作用域中
ES6 的解決方法很簡單, for 中的 var 改成 let 即可,此時 let 的作用域為塊級作用域,定義在循環體內。
第二個問題
變數提升
編譯器會對即將執行的代碼做以下處理
1. 對不管在哪裡定義的變數進行聲明並定義為 undefined
2. 如果是函數聲明,則定義為聲明的函數(以 function 開頭,而函數表達式不會被提升)
3. 按照順序執行代碼
首先上代碼。
// for 循環
var a = [];
for (var i = 0; i &< 10; i++) {
a[i] = function() {
return i;
}
}
console.log(a[1]()); // 10
console.log(a[2]()); // 10
console.log(a[8]()); // 10
console.log(i); // 10
// 手動循環
var a = [];
var i = 0;
a[i] = function() {
return i;
}
i++; // i = 1;
a[i] = function() { // a[i] 等同於 a[0]
return i; // i 會在函數使用時再查詢 而不是等於 1
}
i++; // i = 2;
a[i] = function() {
return i;
}
i++; // i = 3;
console.log(i); // 3
console.log(a[0]()); // 3
console.log(a[2]()); // 3
結論:
- for循環中的 i 是全局變數。
- js採取使用時查詢變數的值,(這點很重要)舉個栗子。
var a = [];
var i = 0;
a[i] = function() {
return i;
}
i++; // i = 1;
a[i] = function() { // a[i] 等同於 a[0]
return i; // i 會在函數使用時再查詢 而不是等於 1
}
再上代碼。
console.log(text); // [Function: text] (node 環境)
function text() {}
var text = 0;
console.log(text); // 0
console.log(text); // [Function: text] (node 環境)
var text = 0;
function text() {}
console.log(text); // 0
結論:
- 變數提升。
- 函數聲明(shua)提(liu)升(mang)——函數是JavaScript的一等公liu民mang,編譯器會忽略同名變數聲明(同名變數在前面函數聲明直接覆蓋,在後面編譯器忽略。)
變數:這就是你說的忽略,確定不是shuaLiuMang?。
第二題,看其他回答吧。
var b=[];
for(var j=0;j&<10;j++){
/* 正常情況下應該是這樣的 方式1*/
// b[j]=j;
/* 正常情況下應該是這樣的 方式2 */
// b.push(j);
/* 面試過程基本上就變成了這樣 方式3 */
b[j]=(function(j){
var ret=j;
return function(){
return ret;
}
})(j);
}
// console.log(b)
// console.log(j)
// console.log(b[6]());
function exam(age){
this.age=age;
return this.age;
}
// 正常情況下使用
var exam_1=new exam(1);
//面試過程中基本就變成了這樣了
var exam_2=new exam((new exam(2)));
var exam_3=new exam(new exam((new exam(3))));
console.log(exam_1.age)
console.log(exam_2)
console.log(exam_3)
針對題主第一題:考察的是作用域
針對題主第二題:考察的是函數聲明和函數表達式的區別和作用場景。
我也有過幾次面試經歷,覺得比較觸動的一件事情就是,先找些相關面試題過濾一下自己。例如:
前端基礎面試題(JS部分)
第一題 閉包解決
var a = [];for (var i = 0; i &< 10; i++) { (function(arg){ a[i] = function () { return arg; } })(i)}a[8](); 第二題 hosting 變數提升 就醬!(評論里有人指出是hoisting 畢竟我英語不好 原諒我!)關於第一題,很多人都回答的很好,我就不多說了。
第二題,很多人回答的也對,但是總感覺講的不夠明白。我建議題主認真看看 方應杭-我用了兩個月的時間才理解 let。我這裡也簡單按自己的理解說下這篇文章的結論:
1) 變數是使用 var/let/const/function(不含函數表達式) 定義的。
2) 變數有 創建、初始化、賦值的過程(const 沒有賦值過程),其中賦值可以多次,創建和初始化只有一次。其中初始化有差異,見下面代碼描述:
var a = 5;
// 可分解為下面的步驟:
// 1. 創建 a
// 2. 初始化 a 為 undefined
// 3. 給 a 賦值為 5
let b = 6;
// 可分解為:
// 1. 創建 b
// 2. 初始化 b 為 6
const c = 7;
// 可分解為:
// 1. 創建 c
// 2. 初始化 c 為 7
function foo () {}
// 可分解為:
// 1. 創建 foo
// 2. 初始化 foo 為 undefined
// 3. 給 foo 賦值「函數」
3) 變數都會「提升」,只是程度不一樣:
var 會提升創建和初始化;
let/const 會提升創建; (可以解釋暫時死區問題:變數未初始化無法使用)
function 會提升創建、初始化、賦值。
根據這些可以完全解釋第二個問題的現象。
建議看一下 《你不知道的Javascript》
第一個,es5解決就是閉包,es6解決用let第二個,你需要了解變數提升原理,權威指南和高級程序設計上都沒有,涉及到ec ao vo的執行創建過程,自己找文章看一下吧
第一題:
// es5 使用閉包
var a = [];
for (var i = 0; i &< 10; i++) {
(function(i){
a[i]= function (){
return i;
}
})(i)
}
a[8](); // --- 8
// es6 for每循環一次都會形成一個單獨的作用域,
let a = [];
for (let i = 0; i &< 10; i++) {
a[i]= function (){
return i;
}
}
a[8](); // --- 8
第二題:
1. js引擎去解析一代碼的時候, 會先去掃描一下當前作用域的所有的var,並執行var( var a; var b; var c;等);但是不會執行賦值操作,這時這些a、b、c的值都是undefined,相當於在執行代碼前,會把當前作用域裡面所有用var聲明的變數提前先給你聲明好,但是賦值操作還是老老實實在原地待著,蝦兵蟹將沒有插隊優先權。
2. 不僅僅是var, 它還會去掃描一下所有的函數聲明(並非函數表達式),也把他們提前聲明好。如果有同名的function,那就聲明的結果是最後一個function。如果剛好有同名字的var聲明的變數,那就幹掉它,function才是老大,var小弟一邊去,不過var a = 0中var被幹掉了,賦值操作(a = 0)還是在的,function老大挺忙的,看不上 =。
console.log(text) // text(){}
function text() {}
var text = 0;
console.log(text) // 0
// 調換位置還是一個樣, var總會被幹掉,function聲明會被提前
console.log(text) // text(){}
var text = 0;
function text() {}
console.log(text) // 0
所以就是: var聲明和function聲明有插隊優先權,function是老大,可以幹掉var,賦值操作職位太低,沒人關注,就老老實實的原地排隊。
第二題涉及到JS的執行上下文,變數對象了。簡單的說:JS執行首先進入變數對象,此時函數和變數都申明了(函數是正常的,但是變數test此時是undefined,還未賦值,這就是變數的申明提升),接著代碼進入活動對象,此時就開始賦值(test賦值了)。
模擬偽代碼如下:
function test(){};
var test =undefined ;
console.log(text);
test = 0;
console.log(text);
注意:函數名優先順序高於變數聲明(不會被覆蓋)
好多人把提升(hoisting)的英文寫錯了,看看我寫錯了沒?
參考 前端基礎進階(三):變數對象詳解
第一題:
for循環中的i其實存在於全局變數(Global)中,for循環執行完畢後,也就是i=10,該值才被傳入函數中,因此,10次結果均為10。以下提供兩個解決辦法:let/閉包
建議通讀一下該文章:JavaScript核心
第二題:
屬於現學現賣:
建議閱讀一下該文章:javascript - js 中變數優先順序的問題? - SegmentFault
over.
額額額,第一個es6的話,var換成let可破,es5高票回答寫的很清楚了,第二個的話,聲明時定義的變數不存在變數提升,因此不管怎麼寫第二三行,都只有函數聲明提升了,因此第一個一直是函數,第二個一直是零
第二題的方式我很討厭,我覺得這就是一種失敗的設計,現在不說不用反而要搞懂這種失敗設計的怪異思維。感覺很low啊
第一題 考察閉包 與 let
第二題 考察變數提升啊 我也想去面試 還招人嗎 實習生第一,變數作用域沒有被鎖住,拿到的都是最後一次循環的引用,每個函數作用域外部變數i ,第二function hosting 和變數聲明hosting。搜索相關文章即可,看到直接答案對你幫助不大。
第一個問題作用域問題,es5中用閉包解決,es6中把var 變成 let,其他答主講的很清楚了不再贅述。第二個問題屬於變數與函數聲明的提升問題。需要注意的是,函數變數名的提升是優先於普通變數的,所以你那段代碼等價於
function text() {}
var text;
console.log(text);
text = 0;
console.log(text);
所以第一次列印是function,第二次列印是0。
第一個的ES6方案是for的var換let,ES5方案你不知道的話暫時還不適合去面試(非初級職位)。
第二個就是變數申明提升和函數申明提升的區別,不知道可以去看JavaScript Garden
推薦閱讀:
※前端界有哪些越早知道越好的小技巧、小知識?
※在飢人谷學習前端是一種怎樣的體驗?
※如何練習前端技術?
※在上海培訓前端,達內,火星,傳智,千鋒綜合考慮哪家好一點?有知道的大神嗎?謝謝
※如何快速學習並掌握一項職業技能?
TAG:前端開發 | JavaScript | 前端入門 | 精通web前端 |