最近面試了不少家,苦於前端經驗薄弱,被各種血虐。做了不少家面試題,把各種不會的回來再做一遍,作為經驗總結吧。
1.如何最優效能去重一個陣列?
方法有好多,比如新建一個陣列,原陣列的內容依次往裡放,若該陣列元素已存在,則跳過;又或者先排序,依次比較前後兩個元素是否相等,若相等,則去掉刪除後一個元素。面試官有提到使用 filter
的方法,但是當場沒想到,發現這個方法其實屬於相當不錯的,這種函式式的思維在某些地方相當實用。
var arr = [3,5,2,6,2,3,5,8,6]
function distinct(arr) {
return arr.filter(function (elem,index,arr){
return arr.indexOf(elem,index+1) === -1;
});
}
console.log(distinct(arr));
思路擴充套件
比如說存在一個陣列,其中元素為物件,根據物件某個屬性進行排序。例如將以下data陣列按age正序排列,常規的辦法可能是通過比較age大小,操作物件來進行排序,這樣程式碼會比較複雜。而更優的方法則是通過 sort
方法。
var data = [
{name:`xiaoming`,age:18},
{name:`xiaohua`,age:20},
{name:`xiaoli`,age:25},
{name:`xiaozheng`,age:16}];
查閱 MDN 關於 sort
方法,此方法明顯程式碼量更少,含義更加清晰。
function asc_order(data){
return data.sort(function (a,b){
return a.age- b.age;
})
}
2.變數宣告和函式宣告提升
function fn(a){
console.log(a);
var a=2;
function a(){}
console.log(a);
}
fn(1);
以上程式碼輸出內容?
此前看書時有了解到變數宣告會提升到作用域頂部,但忘記了變數賦值保持在原處,同時變數宣告和函式宣告的先後關係不確定,所以此題對我來說比較難,只能瞎蒙答案。
function fn(a){
var a;
function a(){}
console.log(a);
a = 2;
console.log(a);
}
fn(1);
所有全域性變數都是window或Global的屬性
函式宣告會被提到範圍作用域的頂端
變數宣告被提到範圍作用域的頂端
變數宣告比函式宣告優先順序高,變數宣告優於函式宣告,如果兩者同時存在,後被提升的函式宣告會覆蓋被提升的變數宣告
變數賦值不會被提升,到執行行程式碼才開始賦值
參考部落格地址,根據以上五點共識,可將程式碼翻譯如上所示。
深入思考
為什麼JavaScript相比較其它語言會存在宣告提升?變數宣告時編譯器做了什麼?變數賦值時編譯器又做什麼了?
3.作用域和 this
var a = 10;
function test(){
a = 100;
console.log(a);
console.log(this.a);
var a;
console.log(a);
}
test();
var a = 100;function test(){
console.log(a);
var a = 10;
console.log(a);
}
test();
var a = 100;function test(){
console.log(a);
a = 10;
console.log(a);
}
test();
console.log(a);
在非嚴格環境下,以上三個程式碼分別輸出什麼?碰到這種題目我也是頭暈眼花,稍有不慎就掉坑了。當然實際業務中不會出現這樣的程式碼,但還是相當有必要以這樣的程式碼來檢查對 JavaScript
理解的程度。this
的用法參照阮一峰老師的部落格,主要分為三種情況,但總的原則是指向呼叫函式的那個物件。
全域性環境:呼叫函式的物件實際為
window
,所以函式內的this
指向window
;建構函式:通過構造函造函式生成了一個新物件,
this
指向這個新物件。物件的方法:函式作為物件的某個方法呼叫,
this
就指向這個上級物件。
故第一道題中屬於全域性環境, this
指向 window
,所以輸出結果為:100,10,100;
第二道題輸出結果為:undefined,10;第三道題輸出結果為:100,10,10;
4.setTimeout 深入分析其機制
for (var i = 0;i<=3;i++){
setTimeout(function (){
console.log(i);
},0);
}
此題輸出內容是什麼?
setTimeout
為 Window
物件方法,用來註冊在指定的事件之後單次或重複呼叫的函式。
setTimeout的作用是將程式碼推遲到指定時間執行,如果指定時間為0,即setTimeout(f,0),那麼會立刻執行嗎?
答案是不會。因為上一段說過,必須要等到當前指令碼的同步任務和“任務佇列”中已有的事件,全部處理完以後,才會執行setTimeout指定的任務。也就是說,setTimeout的真正作用是,在“訊息佇列”的現有訊息的後面再新增一個訊息,規定在指定時間執行某段程式碼。setTimeout新增的事件,會在下一次EventLoop執行。
setTimeout(f,0)將第二個引數設為0,作用是讓f在現有的任務(指令碼的同步任務和“訊息佇列”指定的任務)一結束就立刻執行。也就是說,setTimeout(f,0)的作用是,儘可能早地執行指定的任務。而並不是會立刻就執行這個任務。
所以最終的結果是當前的函式執行結束之後,再去執行 setTimeout
定義的內容。
5.class屬性覆蓋問題
<style>
.B {color: red}
.A {color: blue}
</style>
<body>
<p class="A B">XXXXXXXXXX</p>
</body>
最後實際的問題是什麼顏色?
存在多個類名時,類名的位置不會對屬性的渲染產生影響。只有在style中定義的位置才會有影響,同一條屬性,後面定義的會覆蓋前面定義的屬性。
6.實現類似 jquery 的 one
方法
即對一個元素繫結一個事件,操作一次後繫結事件失效。
HTML部分:
<body>
<p id="target">XXXXXXXXXX</p>
</body>
JS部分:
window.onload = function(){
var target = document.getElementById(`target`);
function fn(e){
alert(`hello`);
target.removeEventListener(`click`,fn);
};
target.addEventListener(`click`,fn);
}
此程式碼雖可行,但沒有進行封裝,不便於使用。
正統封裝後的JS程式碼,使用 arguments.callee
表示當前 function
,同時需對 event
上的屬性有所瞭解。
function oneTime(node,type,callback){
node.addEventListener(type,function (e){
e.target.removeEventListener(e.type,arguments.callee);
return callback(e);
});
}
function handle(e){
alert(`hello!`);
}
oneTime(p,`click`,handle);
學習前端一個月,上一週面試了大概10多家,收穫的 offer
卻是寥寥。
為了效率,前端各方面的內容都有涉獵,深度卻相當不足,面試時暴露各種問題。
還是需深入思考,欲速則不達啊!
大概是要加入大魚或者小悟,以後工作好好努力吧!