前端工作面试问题(下)
?
?續 “前端工作面試問題(上)”
?
JS相關問題:
- 解釋下事件代理。
? 在傳統的事件處理中,你按照需要為每一個元素添加或者是刪除事件處理器。然而,事件處理器將有可能導致內存泄露或者是性能下降——你用得越多這種風險就越大。JavaScript事件代理則是一種簡單的技巧,通過它你可以把事件處理器添加到一個父級元素上,這樣就避免了把事件處理器添加到多個子級元素上。
事件代理用到了兩個在JavaSciprt事件中常被忽略的特性:事件冒泡以及目標元素。當一個元素上的事件被觸發的時候,比如說鼠標點擊了一個按鈕,同樣的事件將會在那個元素的所有祖先元素中被觸發。這一過程被稱為事件冒泡;這個事件從原始元素開始一直冒泡到DOM樹的最上層。任何一個事件的目標元素都是最開始的那個元素,在我們的這個例子中也就是按鈕,并且它在我們的元素對象中以屬性的形式出現。使用事件代理,我們可以把事件處理器添加到一個元素上,等待一個事件從它的子級元素里冒泡上來,并且可以得知這個事件是從哪個元素開始的。
1 // 獲取父節點,并為它添加一個click事件 2 document.getElementById("parent-list").addEventListener("click",function(e) { 3 // 檢查事件源e.targe是否為Li 4 if(e.target && e.target.nodeName.toUpperCase == "LI") { 5 // 真正的處理過程在這里 6 console.log("List item ",e.target.id.replace("post-")," was clicked!"); 7 } 8 });這樣做的好處:那些需要創建的以及駐留在內存中的事件處理器少了,這樣我們就提高了性能,并降低了崩潰的風險。在DOM更新后無須重新綁定事件處理器了。如果你的頁面是動態生成的,比如說通過Ajax,你不再需要在元素被載入或者卸載的時候來添加或者刪除事件處理器了。?
?
- 解釋下 JavaScript 中?this?是如何工作的。
? this 永遠指向函數運行時所在的對象,而不是函數被創建時所在的對象。匿名函數或不處于任何對象中的函數指向 window 。函數中的this的值取決于函數調用的模式:
方法調用模式
當函數被保存為對象的一個屬性時,成該函數為該對象的方法。函數中this的值為該對象。
var foo = {name: 'fooname',getName: function (){return this.name } } foo.getName(); // this => foo函數調用模式
當函數并不是對象的屬性。函數中this的值為全局對象
note:某個方法中的內部函數中的this的值也是全局對象,而非外部函數的this
構造器調用模式
即使用new調用的函數,則其中this將會被綁定到那個新構造的對象。構造器調用將一個全新的對象作為this變量的值,并隱式返回這個新對象作為調用結果。
如果你不是使用new來調用構造器,那其實你就是在使用一個實函數。因此this就不會是你預期的值。在Sloppy模式中,this?指向的就是window?而你將會創建全局變量。不過如果使用的是strict模式,那你還是會得到警告(this===undefined)。
使用apply或call調用模式
該模式調用時,函數中this被綁定到apply或call方法調用時接受的第一個參數。
在方法中this?的用法更傾向于傳統的面向對象語言:this?指向的接收方,也就是包含有這個方法的對象。
?
?箭頭函數就是沒有自己的this?的函數。在這樣的函數中你可以隨便使用this,也不用擔心有沒有隱式的存在。下面提供了三種思路來解決這個問題:
1)that=this,將this?賦值到一個變量上,這樣就把this?顯性地表現出來了(除了that,self?也是個很常見的用于存放this的變量名),之后就使用那個變量。
2)bind()。使用bind()來創建一個函數,這個函數的this?總是存有你想要傳遞的值(下面這個例子中,方法的this):
this.friends.forEach(function (friend) {console.log(this.name+' knows '+friend);}.bind(this));3)用forEach的第二個參數。forEach的第二個參數會被傳入回調函數中,作為回調函數的this?來使用。
this.friends.forEach(function (friend) {console.log(this.name+' knows '+friend);}, this);?
- 解釋下原型繼承的原理。
? 在javascript中,類(定義類是模塊開發和重用代碼的有效方式之一)的實現是基于其原型繼承機制的。如果兩個實例都從一個原型對象上繼承了屬性,我們說它們是同一個類的實例。如果兩個對象繼承自同一個原型,往往意味著(但不是絕對)它們是由同一個構造函數創建并初始化的。
1)類和對象
在javascript中,類的所有實例對象都從一個類型對象上繼承屬性。因此,原型對象是類的核心。
2)類和構造函數
構造函數是用來初始化和創建對象的。使用new關鍵字來調用構造函數,創建一個新對象。調用構造函數的一個重要特征是,構造函數的prototype屬性被用做新對象的原型(var ?object.prototype = new Object())。這意味著通過同一個構造函數創建的對象都是繼承自一個相同的對象,因此它們都是一個類的成員。
javascript中的類牽扯三種不同的對象,三種對象的屬性的行為和下面三種類成員非常相似:
? ??構造函數對象
? ? ? ? ?Js所有的函數都有一個prototype屬性,這個屬性引用了一個對象,即原型對象,也簡稱原型。這個函數包括構造函數和普通函數,判斷一個函數F是否是Object對象的實例:F.prototype instanceof Object //->true
? ??原型對象屬性被類的所有實例所繼承,如果原型對象的屬性值是函數的話,這個函數就作為類的實例方法來調用
? ??實例對象,類的每個實例對象都是一個獨立的對象,直接 給這個實例定義的屬性是不會為所有實例對象鎖共享的。定義在實例上的非函數屬性,實際上是實例的字段。
在javascript中定義類的步奏可以縮減為一個分三步的算法。第一步,先定義一個構造函數,并設置初始化新對象的實例屬性。第二步,給構造函數的prototype對象定義實例的方法。第三步:給構造函數定義類字段和類屬性。
? (面試官可能會問到“javascript繼承和其他語言繼承的區別”,可以從基于對象和訪問修飾符分析)
javascript中基于原型的繼承機制是動態的:對象從其原型繼承屬性,如果創建對象之后原型的屬性發生改變,也會影響到繼承這個原型的所有實例對象。這意味著我們可以通過給原型對象添加新的方法來擴充javascript類。
?
- 你是如何測試 JavaScript 代碼的?
? 1)使用瀏覽器自帶的控制臺調試,詳細可參照“使用console進行 性能測試 和 計算代碼運行時間”
2)chrome的單步調試功能
JavaScript?斷點設置和調試功能和java編輯工具的調試方法類似
Sources Panel 的左邊是內容源,包括頁面中的各種資源;中間主區域用于展示左邊資源文件的內容;右邊是調試功能區,最上面的一排按鈕分別是暫停/繼續、單步執行、單步跳入、單步跳出、禁用/啟用所有斷點。鼠標點擊文件的行號就可以設置和刪除斷點。添加的每個斷點都會出現在右側調試區的 Breakpoints 列表中,點擊列表中斷點就會定位到內容區的斷點上。Call Stack 列表的下方是 Scope Variables 列表,在這里可以查看此時局部變量和全局變量的值。
- *AMD vs. CommonJS?
CommonJs?是服務器端模塊的規范,Node.js采用了這個規范。根據CommonJS規范,一個單獨的文件就是一個模塊。加載模塊使用require方法,該方法讀取一個文件并執行,最后返回文件內部的exports對象。
//require方法默認讀取js文件,所以可以省略js后綴 var test = require('./boobar').foobar;//test為boobar文件中的foobar對象的實例 test.bar(); //調用方法bar()CommonJS 加載模塊是同步的,所以只有加載完成才能執行后面的操作。像Node.js主要用于服務器的編程,加載的模塊文件一般都已經存在本地硬盤,所以加載起來比較快,不用考慮異步加載的方式,所以CommonJS規范比較適用。?
AMD ?CMD?采用異步模式,方便瀏覽器環境要從服務器加載模塊。AMD 是 RequireJS 在推廣過程中對模塊定義的規范化產出。AMD異步加載模塊。它的模塊支持對象、函數、構造器、字符串、JSON等各種類型的模塊。適用AMD規范適用define方法定義模塊。
//通過數組引入依賴 ,回調函數通過形參傳入依賴 define(['someModule1', ‘someModule2’], function (someModule1, someModule2) {function foo () {/// something someModule1.test();}return {foo: foo} });本題內容整理自:http://my.oschina.net/felumanman/blog/263330?p=1
?
- 什么是哈希表?
? 類比數組,數組是編程上的哈希表,哈希表是一種數據結構,關鍵點就是用一個key值來取對應數據,就像數組的下標。
? https://github.com/floraLam/dailyLearn/blob/master/dataStructure/31哈希表.html
?
- 解釋下為什么接下來這段代碼不是 IIFE(立即調用的函數表達式):function foo(){ }();.
? 而函數定義(語句以function關鍵字開始)是不能被立即執行的,這無疑會導致語法的錯誤(SyntaxError)。當函數定義代碼段包裹在括號內,使解析器可以將之識別為函數表達式,然后調用。IIFE:(function foo(){})()
之前面試,遇到問題,區分(function(){})();和(function(){}());其實兩者實現效果一樣。函數字面量:首先聲明一個函數對象,然后執行它。(function () { alert(1); })(); 優先表達式:由于Javascript執行表達式是從圓括號里面到外面,所以可以用圓括號強制執行聲明的函數。(function () { alert(2); }());
?
- 描述以下變量的區別:null,undefined?或?undeclared?
? ?'undefined'是未定義,在變量沒有賦值的時候的值即為undefined。"缺少值",就是此處應該有一個值,但是還沒有定義。
'underclared'即為被污染的命名,訪問沒有被聲明的變量,會拋出異常,終止執行。
'null'是一個空的對象引用。"沒有對象",即該處不應該有值
undefined和null在if語句中,都會被自動轉為false,相等運算符甚至直接報告兩者相等。typeof undefined會返回undefined ,而typeof null?總返回?object(typeof有六種可能:"number"、"string"、"boolean"、"object"、"function"、"undefined")
false == undefined;//false false == null;//false null == undefined;//true?
- 什么是閉包,如何使用它,為什么要使用它?
當某個函數調用時會創建一個執行環境以及作用域鏈,然后根據arguments和其它命名參數初始化形成活動對象。在外部函數調用結束后,其執行環境與作用域鏈被銷毀,但是其活動對象保存在了閉包之中,最后在閉包函數調用結束后才銷毀。簡單的說,閉包就是能夠讀取其他函數內部變量的函數。
由于在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成“定義在一個函數內部的函數”。
注意:
1)由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。
2)閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變量的值始終保持在內存中。
用法
//第1種寫法 :這種寫法沒什么特別的,只是給函數添加一些屬性。 function Circle(r) { this.r = r; } Circle.PI = 3.14159; Circle.prototype.area = function() { return Circle.PI * this.r * this.r; } var c = new Circle(1.0); alert(c.area()); //第2種寫法 :這種寫法是聲明一個變量,將一個函數當作值賦給變量。 var Circle = function() { var obj = new Object(); obj.PI = 3.14159; obj.area = function( r ) { return this.PI * r * r; } return obj; } var c = new Circle(); alert( c.area( 1.0 ) ); //第3種寫法 :這種方法最好理解,就是new 一個對象,然后給對象添加屬性和方法。 var Circle = new Object(); Circle.PI = 3.14159; Circle.Area = function( r ) { return this.PI * r * r; } alert( Circle.Area( 1.0 ) ); //第4種寫法 :這種方法使用較多,也最為方便。var obj = {}就是聲明一個空的對象。 var Circle={ "PI":3.14159, "area":function(r){ return this.PI * r * r; } }; alert( Circle.area(1.0) );閉包的用途????
事實上,通過使用閉包,我們可以做很多事情。比如模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提升代碼的執行效率。
1)匿名自執行函數
全局對象過于龐大,影響訪問速度(因為變量的取值是需要從原型鏈上遍歷的)。
除了每次使用變量都是用var關鍵字外,我們在實際情況下經常遇到這樣一種情況,即有的函數只需要執行一次,其內部變量無需維護,我們可以使用閉包。
我們創建了一個匿名的函數,并立即執行它,由于外部無法引用它內部的變量,因此在函數執行完后會立刻釋放資源,關鍵是不污染全局對象。
2)結果緩存
我們開發中會碰到很多情況,設想我們有一個處理過程很耗時的函數對象,每次調用都會花費很長時間,那么我們就需要將計算出來的值存儲起來,當調用這個函數的時候,首先在緩存中查找,如果找不到,則進行計算,然后更新緩存并返回值,如果找到了,直接返回查找到的值即可。閉包正是可以做到這一點,因為它不會釋放外部的引用,從而函數內部的值可以得以保留。
3)封裝
4)實現類和繼承
- 你喜歡的使用閉包的模式是什么?兩種模式用在不同場合。參見jQuery源碼,立即調用模式,把$的jQuery源碼放在了全局作用域下。返回函數類型的,制作一個隨時可以使用的函數。
?
- 請舉出一個匿名函數的典型用例?
? $.("input").each(function(e){this.val('OK')});
- ------------------------------解釋 “JavaScript 模塊模式” 以及你在何時使用它。
? 我們在做radf庫的時候,把所有的函數寫在var function = ?radf(){}里,為的是在全局作用域下,只有一個radf對象,所有的屬性和方法全在radf命名空間下面。這樣就是一個無污染的環境。
- 如果有提到無污染的命名空間,可以考慮加分。
- 如果你的模塊沒有自己的命名空間會怎么樣?
- 與其它庫或內容造成沖突。
- 如果有提到無污染的命名空間,可以考慮加分。
- 如果你的模塊沒有自己的命名空間會怎么樣?
?
- 你是如何組織自己的代碼?是使用模塊模式,還是使用經典繼承的方法?
? 在模塊模式中使用繼承。例如我們的庫中有pannel布局型組件和data型組件,其余都依照這兩個基本組件繼承而來。
具體可以參考 ExtJS4 便捷三層開發模式?
?
- 請指出 JavaScript 宿主對象和原生對象的區別?
原生對象,獨立于宿主環境的 ECMAScript 實現提供的對象。為array obj regexp date function等可以new實例化的對象。
內置對象為gload Math 等,開發者不必明確實例化內置對象,它已被實例化了。類似于isNaN()、parseInt()和parseFloat()方法等,看起來都是函數,而實際上,它們都是Global對象的方法。具體可以參考?JavaScript 全局對象
宿主對象。即由 ECMAScript 實現的宿主環境(操作系統和瀏覽器)提供的對象。所有的BOM和DOM對象都是宿主對象。因為其對于不同的“宿主”環境所展示的內容不同(這就是兼容性和特性檢測的緣由)。ECMAScript官方未定義的對象都屬于宿主對象。
?
- 指出下列代碼的區別:
function Person(){}
var person = Person();
var person = new Person();
1、定義一個函數為Person()2、定義一個匿名函數指向person
3、實例化一個person、原型來自于函數Person
- .call?和?.apply?的區別是什么??
call和apply都是調用一個對象的一個方法,以另一個對象替換當前對象。它們都屬于Function.prototype的一個方法,所以每個function實例都有call和apply屬性。這兩個方法可以用來代替另一個對象調用一個方法,可將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。
區別在于,兩者傳遞的參數不同,雖然函數第一個參數都是要傳入給當前對象的對象,但是,apply的第二個參數是一個參數數組,將多個參數組合成為一個數組傳入;而call第二個參數則是直接的參數列表。
?
- 請解釋?Function.prototype.bind?
?Function.prototype.bind()其實就是函數綁定。函數的接收者取決于他是如何被調用,可以通過調用.bind()給函數綁定作用域上下文,即函數的接收者。
var foo = { x: 3} var bar = function(){console.log(this.x);}bar(); // undefined var boundFunc = bar.bind(foo);//隱式看作是在foo作用域里調用bar方法 boundFunc(); // 3我們創建了一個新的函數,當它被執行的時候,它的 this 會被設置成 foo —— 而不是像我們調用 bar() 時的全局作用域。
對于改變上下文作用域(具體可以查看上題“解釋下 JavaScript 中?this?是如何工作的”),可以將this設置到一個變量上,這樣改變了上下文之后繼續引用到它。同時可以選擇 self, _this 或者 context 作為變量名稱(也有人使用 that)。
.bind()創建了一個函數,當這個函數在被調用的時候,它的 this 關鍵詞會被設置成被傳入的值(這里指調用bind()時傳入的參數)也就是我們傳入想要的上下文。
簡單的用法:
關于 Function.prototype.bind() 內部,這里有個非常簡單的例子:
Function.prototype.bind = function (scope) {var fn = this;return function () {return fn.apply(scope);//使用call效果一樣}; }?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
- 你何時優化自己的代碼?
優化代碼是在不改變程序行為的基礎上進行小的改動,是代碼逐漸改善的過程。移除長期累積下來的爛碼,以得到更清晰和更容易維護,除錯以及添加新功能的代碼,這做法不能單純只出現在編碼的后期,甚至是你意識到你的代碼已經無從再下手非重寫不可的時候,而是從開始開發起,逐漸積累,逐漸修改。以前因為日常編碼的隨意性,導致問題日益積累,逐步擴散,最后只能推倒重來。如果時間經受不起推倒重來,你別無選擇,唯一實現的選擇就是重構。整體的優化設計雖然惹人注目令人難忘,但沒有平日的積累,何以收獲龐大的成就?你的目標應該是讓代碼每天都有新變化。堅持幾個月,我相信我們都能擁有驕傲地,清晰代碼。
?
- 在什么時候你會使用?document.write()?
記住,在載入頁面后,瀏覽器輸出流自動關閉。在此之后,任何一個對當前頁面進行操作的document.write()方法將打開—個新的輸出流,它將清除當前頁面內容(包括源文檔的任何變量或值)。因此,假如希望用腳本生成的HTML替換當前頁面,就必須把HTML內容連接起來賦給一個變量,使用一個document.write()方法完成寫操作。不必清除文檔并打開一個新數據流,一個document.write()調用就可完成所有的操作。
關于document.write()方法還有一點要說明的是它的相關方法document.close()。腳本向窗口(不管是本窗口或其他窗口)寫完內容后,必須關閉輸出流。在延時腳本的最后一個document.write()方法后面,必須確保含有document.close()方法,不這樣做就不能顯示圖像和表單。并且,任何后面調用的document.write()方法只會把內容追加到頁面后,而不會清除現有內容來寫入新值。為了演示document.write()方法,我們提供了同一個應用程序的兩個版本。
- 大多數生成的廣告代碼依舊使用?document.write(),雖然這種用法會讓人很不爽。
?
- 請指出瀏覽器特性檢測,特性推斷和瀏覽器 UA 字符串嗅探的區別?
檢測瀏覽器的特殊名稱和版本(用戶代理檢測)即瀏覽器UA字符串嗅探。瀏覽器嗅探技術可以快捷的將代碼進行分支,以便針對不同的瀏覽器應用不同的指令;針對特定瀏覽器的特定版本,超出范圍之外都是不可靠的
if (navigator.userAgent.indexOf("MSIE 7") > -1){
//do something
}
檢測瀏覽器的特性(特性檢測)
if(document.all){?
//do something?
}?
另外IE獨有children,parentElement,innerText,outerText,outerHTML,FF沒有;
直接進行特性檢測是個很好的方法,并且大部分情況下能滿足需求。一般只要在檢測前知道這個特性是否被實現即可,而不會去考慮它們之間的關系。?
另外,針對CSS3中新特性@font-face、border-radius、 border-image、box-shadow、rgba() 等,HTML5的特性——比如audio、video、本地儲存、和新的 <input>標簽的類型和屬性等,必要時要進行優雅降級。
?
?
?
- 請盡可能詳盡的解釋 AJAX 的工作原理。
? 非ajax是把要提交的內容放在submit里面,瀏覽器刷新提交數據。ajax即異步數據刷新,將要提交的數據與服務器接口交換數據,將得到的數據返回用于重組dom元素,以及改變一些頁面效果。
Ajax的原理簡單來說通過XmlHttpRequest對象來向服務器發異步請求,從服務器獲得數據,然后用javascript來操作DOM而更新頁面。這其中最關鍵的一步就是從服務器獲得請求數據。
?XMLHttpRequest是ajax的核心機制,它是在IE5中首先引入的,是一種支持異步請求的技術。簡單的說,也就是javascript可以及時向服務器提出請求和處理響應,而不阻塞用戶。達到無刷新的效果。
?? 簡單地說,我們可以把服務器端看成一個數據接口,它返回的是一個純文本流,當然,這個文本流可以是XML格式,可以是Html,可以是Javascript代碼,也可以只是一個字符串。這時候,XMLHttpRequest向服務器端請求這個頁面,服務器端將文本的結果寫入頁面,這和普通的web開發流程是一樣的,不同的是,客戶端在異步獲取這個結果后,不是直接顯示在頁面,而是先由javascript來處理,然后再顯示在頁面。
?
- 請解釋 JSONP 的工作原理,以及它為什么不是真正的 AJAX。
JSONP動態創建script標簽,回調函數。Ajax是頁面無刷新請求數據操作
動態添加一個<script>標簽,而script標簽的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關了。當GET請求從被調用頁面返回時,可以返回一段JavaScript代碼,這段代碼會自動調用主頁面中的一個callback函數。
Jsonp優點不受同源策略的影響,它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;并且在請求完畢后可以通過調用callback的方式回傳結果
Jsonp缺點,它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題。
?
- 你使用過 JavaScript 模板系統嗎?
?沒用過,但是知道:在生成各種頁面內容結合javascript模板技術,能讓邏輯和數據之間更加清晰。邏輯是寫在"<%"與"%>"之間,如果是注釋,則用"<%#"與"%>",后臺傳過來的變量使用@來標記。
- 如有使用過,請談談你都使用過哪些庫,比如 Mustache.js,Handlebars 等等。
?
- 請解釋變量聲明提升。
? javascript不支持塊級作用域,即變量定義的作用域并不是離其最近的封閉語句或代碼塊,而是包含它的函數:
var foo = 1; function bar() {if (!foo) {var foo = 10;}alert(foo);//10 } bar();?
var a = 1; function b() {a = 10;return;function a() {}} b();alert(a);//1對于被函數作用域包圍的變量的作用域為函數,函數內部訪問變量,將會返回函數體內最近的變量值;函數外部訪問變量將會函數體外所聲明的變量值。
也但是,對于被if語句包裹的代碼段,不能看作是另外一個獨立的作用域,也就是說,對于被非函數的{}所包圍的代碼段內所定義的變量,變量的聲明將會提升到{}所在的作用域。
function f(){{var x =0;}}等同于function f(){var x ;{x =0;}}
function foo() {bar();var x = 1;}會被解釋為function foo() {var x;bar();x = 1;}
變量賦值并沒有被提升,只是聲明被提升了。但是,函數的聲明有點不一樣,函數體也會一同被提升。但是請注意,函數的聲明有兩種方式:
function test() {foo(); // TypeError "foo is not a function"bar(); // "this will run!"var foo = function () { // 變量指向函數表達式alert("this won't run!");}function bar() { // 函數聲明 函數名為baralert("this will run!");} } test();對于var?a=1; ?function?a(){ } ?alert(a);function?a(){ ?} var?a=1; ?alert(a);都是會打印出1 ?
對于全局作用于范圍的變量,var與不var是有區別的.?沒有var的寫法,其變量不會被提升。比如下面的程序會報錯:alert(a);a=1;
eval中創建的局部變量是不會被提升var a = 1;function t(){console.info(a);eval('var a = 2');console.info(a);}t();console.info(a);結果按照順序為1,2,1
?
- 請描述下事件冒泡機制。
? 從目標元素開始,往頂層元素傳播。途中如果有節點綁定了相應的事件處理函數,這些函數都會被一次觸發。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)來組織事件的冒泡傳播。
?
- "attribute" 和 "property" 的區別是什么?
DOM元素的attribute和property兩者是不同的東西。attribute翻譯為“特性”,property翻譯為“屬性”。
attribute是一個特性節點,每個DOM元素都有一個對應的attributes屬性來存放所有的attribute節點,attributes是一個類數組的容器,說得準確點就是NameNodeMap,不繼承于Array.prototype,不能直接調用Array的方法。attributes的每個數字索引以名值對(name=”value”)的形式存放了一個attribute節點。<div class="box" id="box" gameid="880">hello</div>
property就是一個屬性,如果把DOM元素看成是一個普通的Object對象,那么property就是一個以名值對(name=”value”)的形式存放在Object中的屬性。要添加和刪除property和普通的對象類似。
很多attribute節點還有一個相對應的property屬性,比如上面的div元素的id和class既是attribute,也有對應的property,不管使用哪種方法都可以訪問和修改。
總之,attribute節點都是在HTML代碼中可見的,而property只是一個普通的名值對屬性。
?
- 為什么擴展 JavaScript 內置對象不是好的做法?
需要給Array原型添加一個distinct的方法,最好檢查是否存在同名的方法,避免自定義方法覆蓋原生方法:
Arrray.prototype.distinct = Arrray.prototype.distinct || function(){/*.....*/} ?
- 請指出 document load 和 document ready 兩個事件的區別。
jQuery中$(function(){/* do something*/});他的作用或者意義就是:在DOM加載完成后就可以可以對DOM進行操作。一般情況先一個頁面響應加載的順序是,域名解析-加載html-加載js和css-加載圖片等其他信息。
- ==?和?===?有什么不同?
注意:
1)如果兩個值的類型不同,它們就不相同。
2)如果兩個值是數字,而且值相同,那么除非其中一個或兩個都是NaN(這種情況它們不是等同的),否則它們是等同的。值NaN永遠不會與其他任何值等同,包括它自身(奇怪的家伙),要檢測一個值是否是NaN,可以使用全局函數isNaN()。
3)如果兩個值都是字符串,而且在串中同一位置上的字符完全相同,那么它們就完全等同。如果字符串的長度或內容不同,它們就不是等同的。
4)如果兩個值都是布爾型true,或者兩個值都是布爾型false,那么它們等同。
5)如果兩個值引用的是同一個對象、數組或函數,那么它們完全等同。如果它們引用的是不同的對象(數組或函數),它們就不完全等同,即使這兩個對象具有完全相同的屬性,或兩個數組具有完全相同的元素。
6)如果兩個值都是null或都是undefined,“==”返回true,“===”返回false。
- 請解釋一下 JavaScript 的同源策略。
? 同源策略是客戶端腳本(尤其是Javascript)的重要的安全度量標準。所謂同源是指,域名,協議,端口相同。如果我們又想利用XMLHTTP的無刷新異步交互能力,又不愿意公然突破Javascript的安全策略,可以選擇的方案就是給XMLHTTP加上嚴格的同源限制。
同源策略阻止從一個源加載的文檔或腳本獲取或設置另一個源加載的文檔的屬性。
處理跨域方法:
? 1)document.domain+iframe的設置
2)動態創建script
3)利用iframe和location.hash
4)window.name實現的跨域數據傳輸
5)使用HTML5 postMessage
?
- 如何實現下列代碼:
[1,2,3,4,5].duplicator(); // [1,2,3,4,5,1,2,3,4,5]
Array.prototype.duplicator = function(){var l = this.length,i;
for(i=0;i<l;i++){
this.push(this[i])
}
}
- 什么是三元表達式?“三元” 表示什么意思?
三元運算符需要三個操作數。
語法是?條件 ? 結果1 : 結果2;. 這里你把條件寫在問號(?)的前面后面跟著用冒號(:)分隔的結果1和結果2。滿足條件時結果1否則結果2。
- 什么是?"use strict";?? 使用它的好處和壞處分別是什么?
在所有的函數 (或者所有最外層函數) 的開始處加入 "use strict"; 指令啟動嚴格模式。
"嚴格模式"有兩種調用方法
?
1)將"use strict"放在腳本文件的第一行,則整個腳本都將以"嚴格模式"運行。如果這行語句不在第一行,則無效,整個腳本以"正常模式"運行。如果不同模式的代碼文件合并成一個文件,這一點需要特別注意。
?
2)將整個腳本文件放在一個立即執行的匿名函數之中。
?
? 好處
- 消除Javascript語法的一些不合理、不嚴謹之處,減少一些怪異行為;
- 消除代碼運行的一些不安全之處,保證代碼運行的安全;
- 提高編譯器效率,增加運行速度;
- 為未來新版本的Javascript做好鋪墊。
壞處
同樣的代碼,在"嚴格模式"中,可能會有不一樣的運行結果;一些在"正常模式"下可以運行的語句,在"嚴格模式"下將不能運行
?
jQuery?相關問題:
?
- 解釋"chaining"。
Chaining 允許我們在一條語句中允許多個 jQuery 方法(在相同的元素上)。這樣的話,瀏覽器就不必多次查找相同的元素。鏈式調用,這是因為jQuery內部在方法調用之后,都返回本身(在無狀態的方法中返回新對象來支持方法鏈,有狀態的方法中返回this來支持方法鏈)。雖然如此,但是如果直接把全部代碼都寫在一行,可讀性會變差,不利于維護,因此要加上必要的縮進和換行。
$("#p1").css("color","red").slideUp(2000).slideDown(2000);- 解釋"deferreds"。
deferred對象就是jQuery的回調函數解決方案。deferred對象的含義就是"延遲"到未來某個點再執行。
對于那些某些耗時很長的javascript操作比如異步的操作(比如ajax讀取服務器數據),和同步的操作(比如遍歷一個大型數組),并不是馬上能夠得到結果,因此,為它們指定回調函數(callback)。
? $.ajax("test.html").done(function(){ alert("成功!"); }).fail(function(){ alert("出錯!"); });同時回調函數可以添加任意多個,它們按照添加順序執行。
?
- 你知道哪些針對 jQuery 的優化方法。
1)總是從ID選擇器開始繼承
在jQuery中最快的選擇器是ID選擇器,因為它直接來自于JavaScript的getElementById()方法。當然 這只是對于單一的元素來講。如果你需要選擇多個元素,這必然會涉及到 DOM遍歷和循環,為了提高性能,建議從最近的ID開始繼承。如下所示:var traffic_lights = $(“#traffic_light input”)
可以使用console測試程序性能,比較id選擇器和class選擇器的效率。
2)在class前使用tag(標簽名)
在jQuery中第二快的選擇器是tag(標簽)選擇器( 比如:$(“head”) ),因為它來自原生的getElementsByTagName() 方法。
在使用tag來修飾class的時候,我們需要注意以下幾點:
(1) 不要使用tag來修飾ID,如下所示:var content = $(“div#content”);這樣一來,選擇器會先遍歷所有的div元素,然后匹配#content。
(2)不要使用ID來修飾ID,如下所示:var traffic_light = $(“#content #traffic_light”);
3)將jQuery對象緩存起來
把jQuery對象緩存起來,不要讓相同的選擇器在你的代碼里出現多次。
注意:(1)為了區分普通的JavaScript對象和jQuery對象,可以在變量首字母前加上 $ 符號。
??(2)代碼可以使用jQuery的鏈式操作加以改善。
4)對直接的DOM操作進行限制
這里的基本思想是在內存中建立你確實想要的東西,然后更新DOM ,因為直接的DOM操作速度很慢。例如,動態的創建一組列表元素,盡量不要在循環中,調用append:? ??
for?(var?i=0,?l=top_100_list.length;?i<l;?i++){?$mylist.append("<li>"?+?top_100_list[i]?+?"</li>");}
而應該將整套元素字符串創建完畢后,再在插入進dom中
for?(var?i=0,?l=top_100_list.length;?i<l;?i++){top_100_li?+=?"<li>"?+?top_100_list[i]?+?"</li>";}? ?$mylist.html(top_100_li);
5)冒泡
除非在特殊情況下, 否則每一個js事件(例如:click, mouseover等.)都會冒泡到父級節點。當我們需要給多個元素調用同個函數時這點會很有用。代替這種效率很差的多元素事件監聽的方法就是, 你只需向它們的父節點綁定一次。
6)推遲到 $(window).load
$(document).rady 確實很有用, 它可以在頁面渲染時,其它元素還沒下載完成就執行。其實可以通過將jQuery函數綁定到$(window).load 事件的方法來減少頁面載入時的cpu使用率。它會在所有的html(包括iframe)被下載完成后執行。一些特效的功能,例如拖放, 視覺特效和動畫, 預載入隱藏圖像等等,都是適合這種技術的場合。
7)壓縮JavaScript
壓縮之前,請保證你的代碼的規范性(語句執行結束后添加分號),否則可能失敗,導致Js錯誤。
8)給選擇器一個上下文
jQuery選擇器中有一個這樣的選擇器,它能指定上下文。jQuery( expression, context );
通過它,能縮小選擇器在DOM中搜索的范圍,達到節省時間,提高效率。
普通方式:$(‘.myDiv’)改進方式:$(‘.myDiv’ , $(“#listItem”) )
?
- 請解釋?.end()?的用途。
? end() 方法結束當前鏈條中的最近的篩選操作,并將匹配元素集還原為之前的狀態。
大多數 jQuery 的遍歷方法會操作一個 jQuery 對象實例,并生成一個匹配不同 DOM 元素集的新對象。當發生這種情況時,應該會把新的元素集推入維持在對象中的堆棧內。每次成功的篩選方法調用都會把新元素推入堆棧中。如果我們需要老的元素集,可以使用 end() 從堆棧中彈出新集合。但由于進行了額外的調用,會有一點點性能損失。
?
- 你如何給一個事件處理函數命名空間,為什么要這樣做?
? 用 .bind('click.myCustomRoutine',function(){...}); 把匿名函數綁定到 click 事件(使用命名空間多次綁定不同的行為方法);使用.unbind('click.myCustomRoutine') 即可 釋放所有綁定到 .myCustomRoutine 命名空間的 click 事件,而不會解除其他通過 .bind('click') 或另外的命名 空間所綁定的事件行為。?
但是,考慮一種情況就是:需要在運行時根據用戶交互的結果進行不同click事件處理邏輯的綁定,因而理論 上會無數次對某一個事件進行 bind / unbind 操作。但又希望 unbind 的時候只把自己綁上去的處理邏輯給釋放掉而不是所有其他地方有 可能的額外的同一事件綁定邏輯。?
這時候如果直接用 .click() / .bind('click') 加 上 .unbind('click') 來進行重復綁定的話,被unbind 掉的將是所有綁定在元素上的 click 處理邏輯,潛在會影響到該元素 其他第三方的行為。
對于這種問題,jQuery的解決方案是使用事件綁定的命名空間。即在事件名稱后添加.something 來區分自己這部分行為邏輯范圍。??
- 請說出你可以傳遞給 jQuery 方法的四種不同值。
選擇器(字符串),HTML(字符串),回調函數,HTML元素,對象,數組,元素數組,jQuery對象等。
?
- 什么是效果隊列?
? jQuery中有個動畫隊列的機制。當對一個對象添加多次動畫效果時后添加的動作就會被放入這個動畫隊列中,等前面的動畫完成后再開始執行。可是用戶的操作往往都比動畫快,如果用戶對一個對象頻繁操作時不處理動畫隊列就會造成隊列堆積,影響到效果。
jQuery中有stop這個方法可以停止當前執行的動畫,并且它有兩個布爾參數,默認值都為false。第一個參數為true時會清空動畫隊列,第二個參數為true時會瞬間完成掉當前動畫。;第二個參數為true,把當前在執行的動畫跳轉到完成狀態。這時第一個參數如果也為true,后面的隊列就會被清空。
?
- 請指出?.get(),[],eq()?的區別。
進一步說,返回的是jQuery對象,就可以繼續調用其他方法,返回的是html數組就不能調用jQuery的其他方法,例如:
$("ul li").eq(1).css("color", "red"); //這個是正確的 $("ul li").get(1).css("color", "red"); //這個是錯誤的
當$()所獲取的對象不存在,即為[]時,get()返回undefined,而eq()返回m.fn.init[0],jQuery文檔對象。
- 請指出?.bind(),.live()?和?.delegate()?的區別。
? 對于bind():$('a').bind('click',function(){alert('That tickles!');})jQuery掃描文檔找出所有的$(‘a')元素,并把alert函數綁定到每個元素的click事件上。?
對于live():$('a').live('click',function(){alert('That tickles!')})jQuery把alert函數綁定到$(document)元素上,并使用'click'和'a'作為參數。任何時候只要有事件冒泡到document節點上,它就查看該事件是否是一個click事件,以及該事件的目標元素與'a'這一CSS選擇器是否匹配,如果都是的話,則執行函數。?
對于.delegate() :$('#container').delegate('a','click',function(){alert('That tickles!')})jQuery掃描文檔查找$('#container'),并使用click事件和'a'這一CSS選擇器作為參數把alert函數綁定到$('#container')上。任何時候只要有事件冒泡到$('#container')上,它就查看該事件是否是click事件,以及該事件的目標元素是否與CSS選擇器相匹配。如果兩種檢查的結果都為真的話,它就執行函數。?
bind看上去更加明確直接,但是delegate和live執行的效率會更高。
bind首先要掃描整個的文檔查找所有的$(‘a')元素,把它們存成jQuery對象。盡管live函數僅需要把'a'作為串參數傳遞以用做之后的判斷,但是$()函數并未“知道”被鏈接的方法將會是.live()。
delegate方法僅需要查找并存儲$(document)元素。?一種尋求避開這一問題的方法是調用在$(document).ready()之外綁定的live,這樣它就會立即執行。在這種方式下,其會在DOM獲得填充之前運行,因此就不會查找元素或是創建jQuery對象了。?
?
- 請指出?$?和?$.fn?的區別,或者說出?$.fn?的用途。
jQuery對方法的拓展,從調用聲明創建方法的方式來看,可以歸結為兩類:一類直接由$符調用;另一類由$("")來調用。$拓展的方法是靜態方法,可以使用$直接調用,其拓展的方式一般使用$.extend({});;而$.fn拓展的方法是實例方法,必須由“對象”$("")來調用,一般使用$.fn.extend({?})。
$.fn是指jquery的命名空間,加上fn上的方法及屬性,會對jquery實例每一個有效。?如擴展$.fn.abc() ;使用:$("#div").abc(); 。
?
- 請優化下列選擇器:
$(".foo div#bar:eq(0)")->$(".foo #bar:first-child")
?
代碼相關的問題:
modulo(12, 5) // 2
問題:實現滿足上述結果的modulo函數
1 (function module(a,b){ 2 var r; 3 if( !isNaN(a) && !isNaN(b)){ 4 (a>b)?(r= a%b):(r= b%a); 5 return r; 6 }else{ 7 throw new Error("arguments are not numbers"); 8 } 9 })(12,5);實際上就是求模運算。注意:檢查參數的合理性(數字且長度為2)否則拋出異常
?
"i'm a lasagna hog".split("").reverse().join("");
問題:上面的語句的返回值是什么??答案:"goh angasal a m'i"
這道題目提醒了一點:"i'm a lasagna hog"不繼承于Array.prototype不能直接調用Array的reverse()方法。因此先要使用split("")將字符串轉變為數組,最后使用join("")將已經逆序的結果轉換為字符串。
博主想補充一點是:通過dom操作(ducument.getElementByTayName和document.getByClassName)獲取的NodeList和函數中的arguments對象都是偽數組,不能不繼承Array.prototype,不能直接調用Array的方法。可以使用遍歷,將偽數組中的元素push到新數組中或使用[].slice.call(arguments)將偽數組轉換為數組。
?
( window.foo || ( window.foo = "bar" ) );
問題:window.foo 的值是什么??答案:"bar"?只有 window.foo 為假(定義的值類型轉換為false或者沒定義,即為undefined)時的才是上面答案,否則就是它本身的值(提前聲明定義的值)。
?
var foo = "Hello"; (function() { var bar = " World"; alert(foo + bar); })(); alert(foo + bar);
問題:上面兩個 alert 的結果是什么?答案: "Hello World"?和?ReferenceError: bar is not defined
var foo = "Hello"; (function() { var bar = " World"; console.info(foo + bar); //代碼段包裹在“立即調用函數”中,獲取全局的foo和函數內的bar })(); console.info(foo + bar);//“立即調用函數”執行完畢后,外部無法訪問其內部的局部變量,因此,在此作用域內的bar未定義?
var foo = [];
foo.push(1);
foo.push(2);
問題:foo.length 的值是什么??答案:2
個人感覺這道題目考得太簡單了,最后,大家思考一下這道題目:
var array1 = [1,2]; var array2 = array1; array1[0] = array2[1]; array2.push(3); console.log(array1); console.log(array2);array2 = array1將array2和array1引用同一片存儲區域,對其中一個變量進行賦值和修改操作都會影響到另外一個變量的值。?
因此答案是:Array1的值為[2,2,3];Array2的值為[2,2,3]
轉載于:https://www.cnblogs.com/0603ljx/p/4334228.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的前端工作面试问题(下)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2018.08.09洛谷P3959 宝藏
- 下一篇: 作业三--阅读《构建之法》1-5章