3月份是找工作的高峰期,最近也面試了很多前端,然后本人也不是什么技術大牛,很多時候都不知道該從那些方面去考察一個人的技術水平,希望這篇文章能夠拋磚引玉,聽聽各位大神的意見,那么就來說說我面試前端主要問些什么吧。
首先,css和html的考察只要是簡歷上有幾個項目我都不會去多問,我個人偏好問的是js,那么就只能先對這語言的認識開始了。
一、JavaScript的對象。
JavaScript 中所有變量都是對象:
字符串、數值、數組、函數...,除了兩個例外 null 和
undefined。JavaScript 的對象只是帶有
屬性和
方法的特殊數據類型,
可以作為
哈希表使用,主要用來保存命名的鍵與值的對應關系。JavaScript 提供多個
內建對象,比如 String、Date、Array 等等。
此外,JavaScript 允許自定義對象,有兩種不同的方法:
- 定義並創建對象的實例
- 使用函數來定義對象,然后創建新的對象實例
1、創建直接的實例
person=new Object();
person.firstname="Bill";
person.lastname="Gates";
person.age=56;
person.eyecolor="blue";
這個例子創建了對象的一個新實例,並向其添加了四個屬性;此外,我們也可以通過對象字面量直接創建對象實例:person={firstname:"John",lastname:"Doe",age:50,eyecolor:"blue"};
2、使用對象構造器
使用函數來構造對象:
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
}
參照來源:
http://www.w3school.com.cn/js/js_objects.asp
二、JavaScript的面向對象。
Javascript是一種基於對象(object-based)的語言,你遇到的所有東西幾乎都是對象。但是,它又不是一種真正的面向對象編程(OOP)語言,因為它的語法中沒有class(類)。JavaScript 不包含傳統的類繼承模型,而是使用 prototype 原型模型。雖然這經常被當作是 JavaScript 的缺點被提及,其實基於原型的繼承模型比傳統的類繼承還要強大。那么javascript是如何去實現繼承的呢?
比如,現在有一個"動物"對象的構造函數。
function Animal(){
this.species = "動物";
}
還有一個"貓"對象的構造函數。
function Cat(name,color){
this.name = name;
this.color = color;
}
怎樣才能使"貓"繼承"動物"呢?
使用prototype屬性。
如果"貓"的prototype對象,指向一個Animal的實例,那么所有"貓"的實例,就能繼承Animal了。
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
代碼的第一行,我們將Cat的prototype對象指向一個Animal的實例。
Cat.prototype = new Animal();
它相當於完全刪除了prototype 對象原先的值,然后賦予一個新值。但是,第二行又是什么意思呢?
Cat.prototype.constructor = Cat;
原來,任何一個prototype對象都有一個constructor屬性,指向它的構造函數。如果沒有"Cat.prototype = new Animal();"這一行,Cat.prototype.constructor是指向Cat的;加了這一行以后Cat.prototype.constructor指向Animal。
alert(Cat.prototype.constructor == Animal); //true
更重要的是,每一個實例也有一個constructor屬性,默認調用prototype對象的constructor屬性。
alert(cat1.constructor == Cat.prototype.constructor); // true
因此,在運行"Cat.prototype = new Animal();"這一行之后,cat1.constructor也指向Animal!
alert(cat1.constructor == Animal); // true
這顯然會導致繼承鏈的紊亂(cat1明明是用構造函數Cat生成的),因此我們必須手動糾正,將Cat.prototype對象的constructor值改為Cat。這就是第二行的意思。
這是很重要的一點,編程時務必要遵守。下文都遵循這一點,即如果替換了prototype對象,
那么,下一步必然是為新的prototype對象加上constructor屬性,並將這個屬性指回原來的構造函數。
o.prototype.constructor = o;
這里還有另外一種方法,就是直接繼承prototype,這種方法是對第一種方法的改進。由於Animal對象中,不變的屬性都可以直接寫入Animal.prototype。所以,我們也可以讓Cat()跳過 Animal(),直接繼承Animal.prototype。
現在,我們先將Animal對象改寫:
function Animal(){ }
Animal.prototype.species = "動物";
然后,將Cat的prototype對象,然后指向Animal的prototype對象,這樣就完成了繼承。
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
與前一種方法相比,這樣做的優點是效率比較高(不用執行和建立Animal的實例了),比較省內存。缺點是 Cat.prototype和Animal.prototype現在指向了同一個對象,那么任何對Cat.prototype的修改,都會反映到Animal.prototype。
所以,上面這一段代碼其實是有問題的。請看第二行
Cat.prototype.constructor = Cat;
這一句實際上把Animal.prototype對象的constructor屬性也改掉了!
alert(Animal.prototype.constructor); // Cat
三、上下文(this的理解)。
JavaScript 有一套完全不同於其它語言的對 this 的處理機制。在五種不同的情況下 ,this 指向的各不相同。
1、全局范圍內this;
當在全部范圍內使用 this,它將會指向全局對象。瀏覽器中運行的 JavaScript 腳本,這個全局對象是 window。
2、函數調用
foo();
這里 this 也會指向全局對象。
ES5 注意: 在嚴格模式下(strict mode),不存在全局變量。 這種情況下 this 將會是 undefined。
3、方法調用
test.foo();
這個例子中,this 指向 test 對象。
4、調用構造函數
new foo();
如果函數傾向於和 new 關鍵詞一塊使用,則我們稱這個函數是 構造函數。在函數內部,this 指向新創建的對象。
5、顯式的設置 this
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); // 數組將會被擴展,如下所示
foo.call(bar, 1, 2, 3); // 傳遞到foo的參數是:a = 1, b = 2, c = 3
當使用 Function.prototype 上的 call 或者 apply 方法時,函數內的 this 將會被 顯式設置為函數調用的第一個參數。
因此函數調用的規則在上例中已經不適用了,在foo 函數內 this 被設置成了 bar。
注意: 在對象的字面聲明語法中,this 不能用來指向對象本身。 因此 var obj = {me: this} 中的 me 不會指向 obj,因為 this 只可能出現在上述的五種情況中。 譯者注:這個例子中,如果是在瀏覽器中運行,obj.me 等於 window 對象。
四、閉包
“官方”的解釋是:閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。
相信很少有人能直接看懂這句話,因為他描述的太學術。其實這句話通俗的來說就是:JavaScript中所有的function都是一個閉包。不過一般來說,嵌套的function所產生的閉包更為強大,也是大部分時候我們所謂的“閉包”。看下面這段代碼:
function a() {
var i = 0;
function b() {
alert(++i);
}
return b;
}
var c = a();
c();
這段代碼有兩個特點:
函數b嵌套在函數a內部;
函數a返回函數b。
引用關系如圖:
這樣在執行完var c=a()后,變量c實際上是指向了函數b,b中用到了變量i,再執行c()后就會彈出一個窗口顯示i的值(第一次為1)。這段代碼其實就創建了一個閉包,為什么?因為函數a外的變量c引用了函數a內的函數b,就是說:
當函數a的內部函數b被函數a外的一個變量引用的時候,就創建了一個我們通常所謂的“閉包”。
讓我們說的更透徹一些。所謂“閉包”,就是在構造函數體內定義另外的函數作為目標對象的方法函數,而這個對象的方法函數反過來引用外層外層函數體中 的臨時變量。這使得只要目標 對象在生存期內始終能保持其方法,就能間接保持原構造函數體當時用到的臨時變量值。盡管最開始的構造函數調用已經結束,臨時變量的名稱也都消失了,但在目 標對象的方法內卻始終能引用到該變量的值,而且該值只能通這種方法來訪問。即使再次調用相同的構造函數,但只會生成新對象和方法,新的臨時變量只是對應新 的值,和上次那次調用的是各自獨立的。
簡而言之,閉包的作用就是在a執行完並返回后,閉包使得Javascript的垃圾回收機制GC不會收回a所占用的資源,因為a的內部函數b的執行需要依賴a中的變量。、
閉包的應用場景
保護函數內的變量安全。以最開始的例子為例,函數a中i只有函數b才能訪問,而無法通過其他途徑訪問到,因此保護了i的安全性。
在內存中維持一個變量。依然如前例,由於閉包,函數a中i的一直存在於內存中,因此每次執行c(),都會給i自加1。
通過保護變量的安全實現JS私有屬性和私有方法(不能被外部訪問)推薦閱讀:http://javascript.crockford.com/private.html
私有屬性和方法在Constructor外是無法被訪問的
function Constructor(...) {
var that = this;
var membername = value;
function membername(...) {...}
}
以上3點是閉包最基本的應用場景,很多經典案例都源於此。
五、Javascript模塊化開發的實現
var module1 = (function(){
var _count = 0;
var m1 = function(){
//...
};
var m2 = function(){
//...
};
return {
m1 : m1,
m2 : m2
};
})();
暫時先寫到這里了,這也是我在前端技術水平方向上幾個比較關注的點,如果是你的話會從哪些方面去考察呢,或者是你在面試的過程中又遇到那些印象比較深刻的題目呢,有興趣的話大家一起討論討論。。。