前端工做面試問題(下)

 

 

 續 「前端工做面試問題(上)javascript

 

JS相關問題:css

  • 解釋下事件代理。

   在傳統的事件處理中,你按照須要爲每個元素添加或者是刪除事件處理器。然而,事件處理器將有可能致使內存泄露或者是性能降低——你用得越多這種風險就越大。JavaScript事件代理則是一種簡單的技巧,經過它你能夠把事件處理器添加到一個父級元素上,這樣就避免了把事件處理器添加到多個子級元素上html

  事件代理用到了兩個在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,你再也不須要在元素被載入或者卸載的時候來添加或者刪除事件處理器了。 java

 

  • 解釋下 JavaScript 中 this 是如何工做的。

  this 永遠指向函數運行時所在的對象,而不是函數被建立時所在的對象。匿名函數或不處於任何對象中的函數指向 window 。函數中的this的值取決於函數調用的模式node

方法調用模式jquery

當函數被保存爲對象的一個屬性時,成該函數爲該對象的方法。函數中this的值爲該對象。git

複製代碼
var foo = {
    name: 'fooname',
    getName: function (){
        return this.name  
    }
}
foo.getName();  // this => foo
複製代碼

函數調用模式
當函數並非對象的屬性。函數中this的值爲全局對象
note:某個方法中的內部函數中的this的值也是全局對象,而非外部函數的thisgithub

function foo(){
    this.name = 'fooname';  
}
foo();  // this => window

構造器調用模式
即便用new調用的函數,則其中this將會被綁定到那個新構造的對象。構造器調用將一個全新的對象做爲this變量的值,並隱式返回這個新對象做爲調用結果。web

複製代碼
function Foo(){
    this.name = 'fooname';
}
var foo = new Foo();  // this => foo

若是你不是使用new來調用構造器,那其實你就是在使用一個實函數。所以this就不會是你預期的值。在Sloppy模式中,this 指向的就是window 而你將會建立全局變量。不過若是使用的是strict模式,那你仍是會獲得警告(this===undefined)。
複製代碼

使用apply或call調用模式
該模式調用時,函數中this被綁定到apply或call方法調用時接受的第一個參數。

function getName(name){
    this.name = name;
}
var foo = {};
getName.call(foo, name);  // this =>foo

在方法中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();

一、定義一個函數爲Person()  
二、定義一個匿名函數指向person  
三、實例化一個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內容
  記住,在載入頁面後,瀏覽器輸出流自動關閉。在此以後,任何一個對當前頁面進行操做的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 
}

複製代碼
var sUserAgent = navigator.userAgent;
//檢測Opera、KHTML
var isOpera = sUserAgent.indexOf(「Opera」) > -1;
var isKHTML = sUserAgent.indexOf(「KHTML」) > -1 || sUserAgent.indexOf(「Konqueror」) > -1 || sUserAgent.indexOf(「AppleWebKit」) > -1;
//檢測IE、Mozilla
var isIE = sUserAgent.indexOf(「compatible」) > -1 && sUserAgent.indexOf(「MSIE」) > -1 && !isOpera;
var isMoz = sUserAgent.indexOf(「Gecko」) > -1 && !isKHTML;
//檢測操做系統
var isWin = (navigator.platform == 「Win32″) || (navigator.platform == 「Windows」);
var isMac = (navigator.platform == 「Mac68K」) || (navigator.platform == 「MacPPC」) || (navigator.platform == 「Macintosh」);
var isUnix = (navigator.platform == 「X11″) && !isWin && !isMac;
複製代碼

檢測瀏覽器的特性(特性檢測)

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() { // 函數聲明 函數名爲bar
        alert("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 內置對象不是好的作法?
由於你不知道哪一天瀏覽器或javascript自己就會實現這個方法,並且和你擴展的實現有不一致的表現。到時候你的javascript代碼可能已經在無數個頁面中執行了數年,而瀏覽器的實現致使全部使用擴展原型的代碼都崩潰了。
  須要給Array原型添加一個distinct的方法,最好檢查是否存在同名的方法,避免自定義方法覆蓋原生方法:
  Arrray.prototype.distinct = Arrray.prototype.distinct || function(){/*.....*/}
 
  • 請指出 document load 和 document ready 兩個事件的區別。
document.ready和onload的區別——JavaScript文檔加載完成事件。頁面加載完成有兩種事件,一是ready,表示文檔結構已經加載完成(不包含圖片等非文字媒體文件),二是onload,指示頁面包含圖片等文件在內的全部元素都加載完成。

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() 的區別。
  eq返回的是一個jquery對象   get返回的是一個html 對象數組。
  進一步說,返回的是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]

相關文章
相關標籤/搜索