寫在前面
唉,我還是前端小渣渣一個。
最近一次的面試:
是一家雲服務產品公司,面試流程也比較簡單,前端負責人拿了一塊白板和記號筆,心想一會要手寫程式碼了。。。。
不過這種面試方式還是不錯的。
去之前以為自己準備的差不多,其實準備的遠遠不夠充足啊。
關於自我介紹
一開始一般會讓你進行一個簡單的自我介紹,我就主要說了一下上家公司的工作經歷,描述了一下我負責過的專案,每個專案的業務功能,主要詳細介紹每個專案用到的技術。
雖然我說了很多,但是事後覺得我在表達的時候,語言的條理性、邏輯性、準確性都不是很好,表達能力的強弱也是面試官比較看中的一部分,畢竟有可能以後和他共事交流工作內容,探討技術等,所以,自我介紹這一塊,覺得也挺重要的,自我介紹代表了面試官對你的第一印象,即便你面試很多次了,也說過很多次了,這方面還是應該好好梳理下自己的語言。
接下來考察js了,面試官慢慢的拿起了他的小白板,我的心裡默默的有點小緊張。。。。
考察方式也比較中規中矩,按照js裡的大類挨個考察。
- 首先是函式方面
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}複製程式碼
“咦?。。。,上一次的面試不是也碰到了這個?”
然後我淡定的說出了答案:
“setTimeout
函式會延遲執行,那麼等到執行console.log()
的時候,i
已經變成5了,所以最後會一次性列印出5個5;”
問:“那我要輸出0~4呢?”
“怎麼也有這個問題?是不是要問我有幾種改法。。。”
我直接說var
改成let
就可以了
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}複製程式碼
或者加個閉包:
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i)
}
複製程式碼
或者這樣:
for(var i = 1;i < 5;i++){
var a = function(){
var j = i;
setTimeout(function(){
console.log(j);
},1000)
}
a();
}複製程式碼
這也是用了閉包的方法,其實就相當於把var化成了let效果一樣,這樣i的值一開始不會被預設繫結,每執行一次迴圈的時候都會給i賦予新值。
問:“如果去掉function
裡的 i
呢?”
“這樣其實對記憶體沒有保持引用, i 最後還是5。”
由於類似的面試題頻繁遇到,整理了一些相關的面試題:
比如這個:
for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), i * 1000);
}複製程式碼
延時函式的第一個引數變成了一個立即執行函式,在這裡應該是一個undefined
,等價於:
setTimeout( undefined, … )
;
立即函式會立馬執行,所以是立馬就輸出0~4;
Promise考察:
setTimeout(function() {
console.log(1)}, 0);
new Promise(function executor(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);複製程式碼
考察執行機制,首先Promise
是非同步的,Promise
只有三中狀態,pending、fulfilled(成功)、rejected(失敗)
,後兩者統稱為resolve
(已定型),一旦狀態定下句就無法再改變。
第一個延時函式首先會設定一個定時,在定時結束後傳遞這個函式將它放到任務佇列中去,因此一開始不會輸出1;
Promise
裡的函式會按順序執行,輸出2 3 ,Promise
裡的then
就是會非同步執行,放到當前Promise
任務佇列的最後執行,而console.log(5)
是按順序執行的,所以先輸出5,再輸出4。Promise.then()
裡面的回撥屬於 microtask, 會在當前 Event Loop 的最後執行, 而 SetTimeout
內的回撥屬於 macrotask, 會在下一個 Event Loop 中執行
最後,才會輸出1。
最後輸出: 2 3 5 4 1
- 物件考察
先來個最簡單的
var obj = {
a: "1"
};
var obj2 = obj;
obj2.a = "2";
console.log(obj.a);複製程式碼
“肯定是輸出 2 啊”
ojb2只是對obj例項的一個引用,到最後還是修改的obj的值。
this指標
name = "name of window";
function show() {
var name = "name in function show()";
alert(this.name);
}
show();複製程式碼
這裡定義了一個全域性變數 name
,這個變數是屬於 window
的,在 show
函式裡也宣告瞭一個name
變數。
this
的定義是指向呼叫當前函式的那個物件, show()
函式是在全域性被呼叫的,所以this
應該指向的是當前 window
物件。
再看下一個:
var myObj = {
name: " my Object",
show: function() {
var name = "my Object in function";
alert(this.name);
},
};
myObj.show();複製程式碼
這次,呼叫 myObj.show()
的物件是 myObj
, 所以就會輸出 my Object
- 陣列Array
先來個排序吧:
var arr = [ 2, 4, 50, 20, 3 ];複製程式碼
氣泡排序:
var arr = [ 2, 4, 50, 20, 3 ];
for(var i = 0 ; i < arr.length-1; i ++){
for(var j = 0; j < arr.length-i; j++ ){
var ls;
if(arr[j] > arr[j+1]){
ls = arr[j];
arr[j] = arr[j+1]
arr[j+1] = ls
}
}
}
console.log(arr);複製程式碼
其實我要開始想到的是用sort()
,但是sort
排出來的不是很穩定,他預設排序是根據字串Unicode碼點。
還問我sort有幾個引數,第二個引數是什麼?
我當時沒想起來,後續整理出來吧!
可選引數是一個比較函式, compareFunction
,用來指定按某種順序進行排列的函式。如果省略,元素按照轉換為的字串的各個字元的Unicode位點進行排序。
- 如果
compareFunction(a, b)
小於 0 ,那麼 a 會被排列到 b 之前;
- 如果
compareFunction(a, b)
等於 0 , a 和 b 的相對位置不變;
- 如果
compareFunction(a, b)
大於 0 , b 會被排列到 a 之前。 compareFunction(a, b)
必須總是對相同的輸入返回相同的比較結果,否則排序的結果將是不確定的。
要比較數字而非字串,比較函式可以簡單的以 a 減 b,如下的函式將會將陣列升序排列
function compareNumbers(a, b) {
return a - b;
}複製程式碼
所以上邊那個排序就可以簡單的寫成:
var arr = [ 2, 4, 50, 20, 3 ];arr.sort(function (a, b) {
return a - b;
});
console.log(arr);複製程式碼
或者更簡潔一點:
var arr = [ 2, 4, 50, 20, 3 ];
arr.sort((a, b) => a -b );
console.log(arr);複製程式碼
又給我改了一下,說怎麼用sort 來排序一個物件?
var obj = [
{ name: 'a', value: 21 },
{ name: 'b', value: 37 },
{ name: 'c', value: 45 },
{ name: 'd', value: -12 },
{ name: 'e' }
];複製程式碼
應該是這樣:
obj.sort(function (a, b) {
return (a.value - b.value);
});複製程式碼
零零碎碎的問了不少,其他的記不太清了,都是一些知識性的東西,不羅列了。
然後問了一些react、 redux相關的技術原理,生命週期、單向資料流、虛擬DOM等等。。。
這些我回答的還好,因為工作天天用這個,也比較熟悉,js基礎類的一些題回答的不太好,我也知道我js基礎不紮實,再者,面試題看少了,雖然這些基礎的知識點在工作中用的不多,有的記不清楚的就google了,也不會影響開發進度,但是,即便react 用的再熟練,重中之重的js基礎!才是最重要的,框架都是js寫的。
反思一下,之前工作中碰到了一些不會的或者生疏不常用的技術,google出來答案後直接就用上了,也沒有仔細的去研究其中的原理,知其然不知其所以然!
事後也沒有總結出來,下次再碰到這個技術點,可能又要google了。。。
不說廢話了!
整理一些網上看到的不錯的面試題:(轉載)
- 關於變數宣告提升和函式宣告提升
alert(a);
a();
var a=3;
function a(){
alert(10)
}
alert(a);
a=6;
a();
複製程式碼
果不其然,我自己看一遍的時候就分析錯了。
關鍵的一點就是:var 宣告的變數 和function生命的函式都會提前,只是提前到作用域開始的位置,但不會進行賦值操作;
function a(){alert(10)}
,就會看到這個函式。2.
a()
,執行函式,就是出現alert(10)
3.執行了
var a=3;
所以alert(a)
就是顯示3
4.由於
a
不是一個函式了,所以往下在執行到a()
的時候, 報錯。 再來看:
alert(a);
a();
var a=3;
var a=function(){
alert(10)
}
alert(a);
a=6;
a();
複製程式碼
區別是,函式這次用var宣告瞭,根據原理,他只是宣告被提前,但a 沒有被賦值為一個函式,
所以,一開始alert(a) 是undefined
;
a() 報錯;
類似的題目:
var a=0;
function aa(){
alert(a);
var a=3;
}
aa();複製程式碼
在aa函式裡面,有var a=3,那麼在aa作用域裡面,就是把a這個變數宣告提前,但是不會賦值,所以是undefined
;
var a=0;
function aa(a){
alert(a);
var a=3;
};
aa(5);
alert(a);
//5,0 複製程式碼
在函式體內,引數a的優先順序高於變數a ;
var a=0;
function aa(a){
alert(a);
a=3;
alert(a);
}
aa();
alert(a);
複製程式碼
首先沒有傳引數進去,aa()
是 undefined
;
a=3,實際上修改的是形參a的值,並不是全域性變數a,往下alert(a)
也是形參a;
最後的alert(a)
; 是全域性的a;
寫在最後
不說了!。。。
我要去刷面試題,繼續啃我的JavaScript 語言精粹了。