前端知識雜燴(Javascript篇)

1. JavaScript是一門什麼樣的語言,它有什麼特色?
2.JavaScript的數據類型都有什麼?
3.請描述一下 cookies,sessionStorage 和 localStorage 的區別?
4.webSocket如何兼容低瀏覽器?(阿里)
5.this和它聲明環境無關,而徹底取決於他的執行環境
6.JavaScript異步編程經常使用的四種方法
七、在嚴格模式('use strict')下進行 JavaScript 開發有神馬好處?
八、神馬是 NaN,它的類型是神馬?怎麼測試一個值是否等於 NaN?
九、解釋一下下面代碼的輸出
十、實現函數 isInteger(x) 來判斷 x 是不是整數
11.前端模塊化-AMD(異步模塊定義)規範與CMD(通用模塊定義)規範(期待ES6模塊一統天下)
12.JS跨域彙總
13.兩張圖讓你看懂「==」與if()
14.JS中建立對象的幾種方式(此處只列舉,詳情見紅寶書《JS高級程序設計》)
15.JS中實現繼承的幾種方式(此處只列舉,詳情見紅寶書《JS高級程序設計》)
16.JS中函數的幾種建立形式。
17.call與apply的異同?
18.JavaScript中常見的內存泄漏及解決方案
19.原生的ajax請求處理流程
20.閉包的應用場景(草稿-非正式)
21.使用JS事件委託的優勢和缺點
22.前端模塊化開發做用及基本原理(部分可參考第11題)
23.Js中訪問對象屬性用點和用中括號有什麼不一樣
24.Javascript垃圾回收方法
25.說說你對閉包的理解
26.DOM操做——怎樣添加、移除、移動、複製、建立和查找節點。

1. JavaScript是一門什麼樣的語言,它有什麼特色?

JavaScript 是一種腳本語言,官方名稱爲 ECMAScript(因定義語言的標準爲 ECMA-262)。JS 的主要特色:1. 語法相似於常見的高級語言,如 C 和 Java;2. 腳本語言,不須要編譯就能夠由解釋器直接運行;3. 變量鬆散定義,屬於弱類型語言;4. 面向對象的。 JS 最初是爲網頁設計而開發的,如今也是 Web 開發的重要語言。它支持對瀏覽器(瀏覽器對象模型,BOM)和 HTML 文檔(文檔對象模型,DOM)進行操做,而使網頁呈現動態的交互特性。 嚴格的說,JS 只是 ECMAScript 的一種實現,是 ECMAScript 和 BOM、DOM 組成的一種 Web 開發技術。javascript

2.JavaScript的數據類型都有什麼?

  基本數據類型:String,Boolean,Number,Undefined, Null
  引用數據類型:Object(Array,Date,RegExp,Function)
  那麼問題來了,如何判斷某變量是否爲數組數據類型?php

  • 方法一.判斷其是否具備「數組性質」,如slice()方法。可本身給該變量定義slice方法,故有時會失效
  • 方法二.obj instanceof Array 在某些IE版本中不正確
  • 方法三.方法一二皆有漏洞,在ECMA Script5中定義了新方法Array.isArray(), 保證其兼容性,最好的方法以下:
 
 
 
 
function isArray(value){return Object.prototype.toString.call(value) == "[object Array]";}

3.請描述一下 cookies,sessionStorage 和 localStorage 的區別?

cookie是網站爲了標示用戶身份而儲存在用戶本地終端(Client Side)上的數據(一般通過加密)。cookie數據始終在同源的http請求中攜帶(即便不須要),記會在瀏覽器和服務器間來回傳遞。sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存。html

  • 存儲大小:
    (1) cookie數據大小不能超過4k。
    (2)sessionStorage和localStorage 雖然也有存儲大小的限制,但比cookie大得多,能夠達到5M或更大。
  • 有期時間:
    (1) localStorage存儲持久數據,瀏覽器關閉後數據不丟失除非主動刪除數據;
    (2)sessionStorage 數據在當前瀏覽器窗口關閉後自動刪除。
    (3) cookie設置的cookie過時時間以前一直有效,即便窗口或瀏覽器關閉

4.webSocket如何兼容低瀏覽器?(阿里)

  • Adobe Flash Socket 、
  • ActiveX HTMLFile (IE) 、
  • 基於 multipart 編碼發送 XHR 、
  • 基於長輪詢的 XHR

5.this和它聲明環境無關,而徹底取決於他的執行環境

 
 
 
 
var name = ‘羅恩’; var aaa = { name: ‘哈利’, say: function () { console.log(this.name); } } var bbb = { name: ‘赫敏’, say: aaa.say } var ccc = aaa.say; aaa.say(); //哈利 bbb.say(); //赫敏 ccc(); //羅恩

6.JavaScript異步編程經常使用的四種方法

  • 1.回調函數
    f1(f2);
    回調函數是異步編程的基本方法。其優勢是易編寫、易理解和易部署;缺點是不利於代碼的閱讀和維護,各個部分之間高度耦合 (Coupling),流程比較混亂,並且每一個任務只能指定一個回調函數。
  • 2.事件監聽
    f1.on('done',f2);
    事件監聽即採用事件驅動模式,任務的執行不取決於代碼的順序,而取決於某個事件是否發生。其優勢是易理解,能夠綁定多個事件,每一個事件能夠指定多個回調函數,能夠去耦合, 有利於實現模塊化;缺點是整個程序都要變成事件驅動型,運行流程會變得不清晰。
  • 3.發佈/訂閱
    f1: jQuery.publish("done");
    f2: jQuery.subscribe("done", f2);
    假定存在一個"信號中心",某個任務執行完成,就向信號中心"發佈"(publish)一個信號,其餘任務能夠向信號中心"訂閱"(subscribe)這個信號,從而知道何時本身能夠開始執行,這就叫作 "發佈/訂閱模式" (publish-subscribe pattern),又稱 "觀察者模式" (observer pattern)。該 方法的性質與"事件監聽"相似,但其優點在於能夠 經過查看"消息中心",瞭解存在多少信號、每一個信號有多少訂閱者,從而監控程序的運行。
  • 4.promise對象
    f1().then(f2);
    Promises對象是CommonJS工做組提出的一種規範,目的是爲異步編程提供 統一接口 ;思想是, 每個異步任務返回一個Promise對象,該對象有一個then方法,容許指定回調函數。其優勢是回調函數是鏈式寫法,程序的流程很是清晰,並且有一整套的配套方法, 能夠實現許多強大的功能,如指定多個回調函數、指定發生錯誤時的回調函數, 若是一個任務已經完成,再添加回調函數,該回調函數會當即執行,因此不用擔憂是否錯過了某個事件或信號;缺點就是編寫和理解相對比較難。

七、在嚴格模式('use strict')下進行 JavaScript 開發有神馬好處?

  • 消除Javascript語法的一些不合理、不嚴謹之處,減小一些怪異行爲;
  • 消除代碼運行的一些不安全之處,保證代碼運行的安全;
  • 提升編譯器效率,增長運行速度;
  • 爲將來新版本的Javascript作好鋪墊。

八、神馬是 NaN,它的類型是神馬?怎麼測試一個值是否等於 NaN?

NaN 是 Not a Number 的縮寫,JavaScript 的一種特殊數值,其類型是 Number,能夠經過 isNaN(param) 來判斷一個值是不是 NaN前端

 
 
 
 
console.log(isNaN(NaN)); //trueconsole.log(isNaN(23)); //falseconsole.log(isNaN('ds')); //trueconsole.log(isNaN('32131sdasd')); //trueconsole.log(NaN === NaN); //falseconsole.log(NaN === undefined); //falseconsole.log(typeof NaN); //numberconsole.log(Object.prototype.toString.call(NaN)); //[object Number]

ES6 中,isNaN() 成爲了 Number 的靜態方法:Number.isNaN()html5


九、解釋一下下面代碼的輸出

 
 
 
 
console.log(0.1 + 0.2); //0.30000000000000004console.log(0.1 + 0.2 == 0.3); //false

JavaScript 中的 number 類型就是浮點型,JavaScript 中的浮點數採用IEEE-754 格式的規定,這是一種二進制表示法,能夠精確地表示分數,好比1/2,1/8,1/1024,每一個浮點數佔64位。可是,二進制浮點數表示法並不能精確的表示相似0.1這樣 的簡單的數字,會有舍入偏差。
因爲採用二進制,JavaScript 也不能有限表示 1/十、1/2 等這樣的分數。在二進制中,1/10(0.1)被表示爲0.00110011001100110011…… 注意 0011 是無限重複的,這是舍入偏差形成的,因此對於 0.1 + 0.2 這樣的運算,操做數會先被轉成二進制,而後再計算:java

 
 
 
 
0.1 => 0.0001 1001 1001 1001…(無限循環)0.2 => 0.0011 0011 0011 0011…(無限循環)

雙精度浮點數的小數部分最多支持 52 位,因此二者相加以後獲得這麼一串 0.0100110011001100110011001100110011001100…因浮點數小數位的限制而截斷的二進制數字,這時候,再把它轉換爲十進制,就成了 0.30000000000000004。
對於保證浮點數計算的正確性,有兩種常見方式。node

  • 一是先升冪再降冪:
 
 
 
 
function add(num1, num2){ let r1, r2, m; r1 = (''+num1).split('.')[1].length; r2 = (''+num2).split('.')[1].length; m = Math.pow(10,Math.max(r1,r2)); return (num1 * m + num2 * m) / m;}console.log(add(0.1,0.2)); //0.3console.log(add(0.15,0.2256)); //0.3756
  • 二是是使用內置的 toPrecision()toFixed() 方法,**注意,方法的返回值字符串。
 
 
 
 
function add(x, y) { return x.toPrecision() + y.toPrecision()}console.log(add(0.1,0.2)); //"0.10.2"

十、實現函數 isInteger(x) 來判斷 x 是不是整數

能夠將 x 轉換成10進制,判斷和自己是否是相等便可:git

 
 
 
 
function isInteger(x) { return parseInt(x, 10) === x; }

ES6 對數值進行了擴展,提供了靜態方法 isInteger() 來判斷參數是不是整數:github

 
 
 
 
Number.isInteger(25) // trueNumber.isInteger(25.0) // trueNumber.isInteger(25.1) // falseNumber.isInteger("15") // falseNumber.isInteger(true) // false

JavaScript可以準確表示的整數範圍在 -2^532^53 之間(不含兩個端點),超過這個範圍,沒法精確表示這個值。ES6 引入了Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER這兩個常量,用來表示這個範圍的上下限,並提供了 Number.isSafeInteger() 來判斷整數是不是安全型整數。web


11.前端模塊化-AMD(異步模塊定義)規範與CMD(通用模塊定義)規範(期待ES6模塊一統天下)

AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出。CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。主要區別是

  • 1. 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改爲能夠延遲執行(根據寫法不一樣,處理方式不一樣)。CMD 推崇 as lazy as possible.

    AMD和CMD最大的區別是對依賴模塊的執行時機處理不一樣,注意不是加載的時機或者方式不一樣
    不少人說requireJS是異步加載模塊,SeaJS是同步加載模塊,這麼理解其實是不許確的,其實加載模塊都是異步的,只不過AMD依賴前置,js能夠方便知道依賴模塊是誰,當即加載,而CMD就近依賴,須要使用把模塊變爲字符串解析一遍才知道依賴了那些模塊,這也是不少人詬病CMD的一點,犧牲性能來帶來開發的便利性,實際上解析模塊用的時間短到能夠忽略
    爲何咱們說兩個的區別是依賴模塊執行時機不一樣,爲何不少人認爲AMD是異步的,CMD是同步的(除了名字的緣由。。。)
    一樣都是異步加載模塊,AMD在加載模塊完成後就會執行改模塊,全部模塊都加載執行完後會進入require的回調函數,執行主邏輯,這樣的效果就是依賴模塊的執行順序和書寫順序不必定一致,看網絡速度,哪一個先下載下來,哪一個先執行,可是主邏輯必定在全部依賴加載完成後才執行
    CMD加載完某個依賴模塊後並不執行,只是下載而已,在全部依賴模塊加載完成後進入主邏輯,遇到require語句的時候才執行對應的模塊,這樣模塊的執行順序和書寫順序是徹底一致的
    這也是不少人說AMD用戶體驗好,由於沒有延遲,依賴模塊提早執行了,CMD性能好,由於只有用戶須要的時候才執行的緣由

  • 2. CMD 推崇依賴就近,只有在用到某個模塊的時候再去require;AMD 推崇依賴前置在定義模塊的時候就要聲明其依賴的模塊。看代碼:
  • 3.AMD推薦的風格經過返回一個對象作爲模塊對象,CommonJS的風格經過對module.exportsexports的屬性賦值來達到暴露模塊對象的目的。

    順便提一下:CommonJS是適用於服務器端的規範,NodeJS便是它的一種實現,CommonJS定義的模塊分爲:{模塊引用(require)} {模塊定義(exports)} {模塊標識(module)}。 require()用來引入外部模塊;exports對象用於導出當前模塊的方法或變量,惟一的導出口;module對象就表明模塊自己。

 
 
 
 
//CommonJS規範寫法//sum.js exports.sum = function(){//作加操做}; //calculate.js var math = require('sum'); exports.add = function(n){ return math.sum(val,n); };
 
 
 
 
// CMDdefine(function(require, exports, module) {var a = require('./a')a.doSomething()// 此處略去 100 行var b = require('./b') // 依賴能夠就近書寫b.doSomething();//本模塊的導出接口 exports.each = function (arr) { // 實現代碼 }; exports.log = function (str) { // 實現代碼 };})
 
 
 
 
// AMD就只有一個接口:define(id?,dependencies?,factory);define(['./a', './b'], function(a, b) { // 依賴必須一開始就寫好a.doSomething()// 此處略去 100 行b.doSomething();//返回本模塊的導出接口 var myModule = { doStuff:function(){ console.log('Yay! Stuff'); } } return myModule;...}) //AMD還有一個require方法主要用來在頂層 JavaScript 文件中或需要動態讀取依賴時加載代碼require(['foo', 'bar'], function ( foo, bar ) { // 這裏寫其他的代碼 foo.doSomething(); //返回本模塊的導出接口 var myModule = { doStuff:function(){ console.log('Yay! Stuff'); } } return myModule;});

12.JS跨域彙總

1.經過jsonp跨域

  • 只能使用 GET 方法發起請求,這是因爲 script 標籤自身的限制決定的。
  • 不能很好的發現錯誤,並進行處理。與 Ajax 對比,因爲不是經過 XmlHttpRequest 進行傳輸,因此不能註冊 success、 error 等事件監聽函數。

2.經過修改document.domain來跨子域(iframe)
3.隱藏的iframe+window.name跨域
4.iframe+跨文檔消息傳遞(XDM)
5.跨域資源共享 CORS

  • CORS 除了 GET 方法外,也支持其它的 HTTP 請求方法如 POST、 PUT 等。
  • CORS 可使用 XmlHttpRequest 進行傳輸,因此它的錯誤處理方式比 JSONP 好。
  • JSONP 能夠在不支持 CORS 的老舊瀏覽器上運做。

6.Web Sockets

跨域請求並不是是瀏覽器限制了發起跨站請求,而是請求能夠正常發起,到達服務器端,可是服務器返回的結果會被瀏覽器攔截。


13.兩張圖讓你看懂「==」與if()


14.JS中建立對象的幾種方式(此處只列舉,詳情見紅寶書《JS高級程序設計》)

1.對象字面量
2.Object構造函數

mygirl=new Object();
ES5新方法構建對象,再添加屬性

3.工廠模式

工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。

4.構造函數模式

使用構造函數的主要問題,就是每一個方法都要在每一個實例上從新建立一遍。

5.原型模式

function Girl(){
}
在Girl.prototype上添加屬性和方法
var mygirl=new Girl();
特色:原型中全部屬性是被全部實例共享的,這種共享對於函數很是合適,可是對於基本屬性就顯得不是很合適,尤爲是對於組合使用原型模式和構造函數建立對象包含引用類型值的屬性來講,問題就比較突出了。

6.組合使用原型模式和構造函數建立對象(推薦)

建立自定義類型的最多見方式,就是組合使用構造函數模式與原型模式。構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。

7.動態原型模式

相對於組合模式,就是把原型上添加方法的步驟放在構造函數中,而後根據構造函數中是否已經存在該方法來決定添不添加

8.寄生構造函數模式

相對於工廠模式就是把函數當作構造函數調用

9.穩妥構造函數模式


15.JS中實現繼承的幾種方式(此處只列舉,詳情見紅寶書《JS高級程序設計》)

1.藉助原型鏈

 
 
 
 
function SuperType(){ this.colors = ["red", "blue", "green"]; }function SubType(){}//繼承了 SuperTypeSubType.prototype = new SuperType();

使用原型鏈會出現兩個問題,一是若是原型中包含引用類型值,子類全部實例會共享這個引用類型;二是沒有辦法在不影響全部對象實例的狀況下,給超類型的構造函數傳遞參數。

2.藉助構造函數

 
 
 
 
function SuperType(name){this.name = name;}function SubType(){//繼承了 SuperType,同時還傳遞了參數SuperType.call(this, "Nicholas");//實例屬性this.age = 29;}

可以解決第一種方法原型中包含引用類型值所帶來問題,也能向超類型構造函數傳遞參數。問題是若是僅僅是借用構造函數,那麼也將沒法避免構造函數模式存在的問題——方法都在構造函數中定義,所以函數複用就無從談起了。

3.組合繼承(推薦)
組合繼承( combination inheritance),有時候也叫作僞經典繼承,指的是將原型鏈和借用構造函數的技術組合到一塊,從而發揮兩者之長的一種繼承模式。其背後的思路是使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。這樣,既經過在原型上定義方法實現了函數複用,又可以保證每一個實例都有它本身的屬性。下面來看一個例子。

 
 
 
 
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){ alert(this.name);};function SubType(name, age){ //繼承屬性 SuperType.call(this, name); this.age = age;}//繼承方法SubType.prototype = new SuperType();SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function(){ alert(this.age);};var instance1 = new SubType("Nicholas", 29);instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"instance1.sayName(); //"Nicholas";instance1.sayAge(); //29var instance2 = new SubType("Greg", 27);alert(instance2.colors); //"red,blue,green"instance2.sayName(); //"Greg";instance2.sayAge(); //27

4.原型式繼承

 
 
 
 
function object(o){ function F(){} F.prototype = o; return new F();}

在 object() 函數內部,先建立了一個臨時性的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回了這個臨時類型的一個新實例。ECMAScript 5 經過新增 Object.create() 方法規範化了原型式繼承。

 
 
 
 
var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]};var anotherPerson = Object.create(person);anotherPerson.name = "Greg";anotherPerson.friends.push("Rob");alert(person.friends); //"Shelby,Court,Van,Rob"

在沒有必要興師動衆地建立構造函數,而只想讓一個對象與另外一個對象保持相似的狀況下,原型式繼承是徹底能夠勝任的。不過別忘了,包含引用類型值的屬性始終都會共享相應的值,就像使用原型模式同樣。

5.寄生式繼承

 
 
 
 
function createAnother(original){var clone = object(original); //經過調用函數建立一個新對象,也可使用其餘相似的方法clone.sayHi = function(){ //以某種方式來加強這個對象alert("hi");};return clone; //返回這個對象}

6.寄生組合式繼承

 
 
 
 
function inheritPrototype(subType, superType){var prototype = object(superType.prototype); //建立對象prototype.constructor = subType; //加強對象subType.prototype = prototype; //指定對象}

16.JS中函數的幾種建立形式。

一、聲明函數
最普通最標準的聲明函數方法,包括函數名及函數體。

 
 
 
 
function fn1(){}

二、建立匿名函數表達式
建立一個變量,這個變量的內容爲一個函數

 
 
 
 
var fn1=function (){}

注意採用這種方法建立的函數爲匿名函數,即沒有函數name
三、建立具名函數表達式
建立一個變量,內容爲一個帶有名稱的函數
var fn1=function xxcanghai(){};
注意:具名函數表達式的函數名只能在建立函數內部使用
四、Function構造函數
能夠給 Function 構造函數傳一個函數字符串,返回包含這個字符串命令的函數,此種方法建立的是匿名函數。

五、自執行函數
(function(){alert(1);})();
(function fn1(){alert(1);})();
自執行函數屬於上述的「函數表達式」,規則相同
參考:http://www.cnblogs.com/xxcanghai/p/4991870.html


17.call與apply的異同?

call方法與apply方法的做用是同樣的,都是爲了改變函數內部的this指向。區別僅在於傳入的參數形式的不一樣。
apply函數接受兩個參數,第一個參數指定了函數體內this對象的指向,第二個參數爲一個能夠下標訪問的集合,這個集合可使數組,也能夠是類數組,apply方法把這個集合中的元素做爲參數傳遞給被調用的函數。
call方法傳入的參數數量是不固定的,跟apply相同的是,第一個參數也是表明函數體內this對象的指向,從第二個參數開始日後,是一組參數序列,每一個參數被依次傳入函數。
Notes

  • call方法不是說不能接受數組作參數,而是將數組參數當作一個總體,做爲參數序列的一部分,而apply方法是將數組中的元素當作參數
  • call和apply第一個參數爲null時,函數體內的this指向宿主對象,瀏覽器中則爲window,在嚴格模式下,仍爲null.

18.JavaScript中常見的內存泄漏及解決方案

現代的瀏覽器大多采用標記清除的方法來進行垃圾回收,其基本步驟以下:

  1. 垃圾回收器建立了一個「roots」列表。Roots 一般是代碼中全局變量的引用。JavaScript 中,「window」 對象是一個全局變量,被看成 root 。window 對象老是存在,所以垃圾回收器能夠檢查它和它的全部子對象是否存在(即不是垃圾);
  2. 全部的 roots 被檢查和標記爲激活(即不是垃圾)。全部的子對象也被遞歸地檢查。從 root 開始的全部對象若是是可達的,它就不被看成垃圾。
  3. 全部未被標記的內存會被當作垃圾,收集器如今能夠釋放內存,歸還給操做系統了。
    內存泄漏主因是不須要的引用未被及時清除。下列列舉幾種常見的內存泄漏及其解決方案
  • 意外的全局變量
    尤爲當全局變量用於臨時存儲和處理大量信息時,須要多加當心。若是必須使用全局變量存儲大量數據時,確保用完之後把它設置爲 null 或者從新定義。與全局變量相關的增長內存消耗的一個主因是緩存。緩存數據是爲了重用,緩存必須有一個大小上限纔有用。高內存消耗致使緩存突破上限,由於緩存內容沒法被回收。另外啓用嚴格模式解析JavaScript,也能夠避免意外的全局變量。
  • 循環引用
    循環引用很常見且大部分狀況下是無害的,但當參與循環引用的對象中有DOM對象或者ActiveX對象時,循環引用將致使內存泄露。老版本的 IE 是沒法檢測 DOM 節點與 JavaScript 代碼之間的循環引用,會致使內存泄漏,。現在,現代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進的垃圾回收算法,已經能夠正確檢測和處理循環引用了。
  • 循環引用和閉包
 
 
 
 
function bindEvent(){ var obj=document.createElement("XXX"); obj.onclick=function(){ //Even if it's a empty function }}

函數將間接引用全部它能訪問的對象。obj.onclick這個函數中 能夠訪問外部的變量obj 因此他引用了obj,而obj又引用了它,所以這個事件綁定將會形成內存泄露.解決辦法是能夠把函數卸載外面。

 
 
 
 
function bindEvent(){ var obj=document.createElement("XXX"); obj.onclick=onclickHandler;}function onclickHandler(){ //do something}

另外對於事件應該在unload中解除循環引用的屬性置爲null

  • 某些DOM操做
    從外到內執行appendChild。這時即便調用removeChild也沒法釋放。範例:
 
 
 
 
var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); document.body.appendChild(parentDiv); parentDiv.appendChild(childDiv);

解決方法:
從內到外執行appendChild:

 
 
 
 
var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); parentDiv.appendChild(childDiv); document.body.appendChild(parentDiv);
  • 被遺忘的計時器或回調函數
    在 JavaScript 中使用 setInterval 很是日常。一段常見的代碼:
 
 
 
 
var someResource = getData();setInterval(function() { var node = document.getElementById('Node'); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); }}, 1000);

此例說明了什麼:與節點或數據關聯的計時器再也不須要,node 對象能夠刪除,整個回調函數也不須要了。但是,計時器回調函數仍然沒被回收(計時器中止纔會被回收)。同時,someResource 若是存儲了大量的數據,也是沒法被回收的。
參考:
Javascript內存泄漏
4類 JavaScript 內存泄漏及如何避免
如何檢測瀏覽器內存泄漏
Chrome 提供了一套很棒的檢測 JavaScript內存佔用的工具。與內存相關的兩個重要的工具:timelineprofiles。具體參考Chrome開發者工具之JavaScript內存分析


19.原生的ajax請求處理流程

Ajax 的全稱是Asynchronous JavaScript and XML,其中,Asynchronous 是異步的意思,它有別於傳統web開發中採用的同步的方式。

Ajax的原理簡單來講經過XmlHttpRequest對象來向服務器發異步請求,從服務器得到數據,而後用javascript來操做DOM而更新頁面。

XMLHttpRequest是ajax的核心機制,它是在IE5中首先引入的,是一種支持異步請求的技術。簡單的說,也就是javascript能夠及時向服務器提出請求和處理響應,而不阻塞用戶。達到無刷新的效果。

XMLHttpRequest這個對象的屬性有:

  • onreadystatechang 每次狀態改變所觸發事件的事件處理程序。
  • responseText 從服務器進程返回數據的字符串形式。
  • responseXML 從服務器進程返回的DOM兼容的文檔數據對象。
  • status 從服務器返回的數字代碼,好比常見的404(未找到)和200(已就緒)
  • status Text 伴隨狀態碼的字符串信息
  • readyState 對象狀態值
    • 0 (未初始化) 對象已創建,可是還沒有初始化(還沒有調用open方法)
    • 1 (初始化) 對象已創建,還沒有調用send方法
    • 2 (發送數據) send方法已調用,可是當前的狀態及http頭未知
    • 3 (數據傳送中) 已接收部分數據,由於響應及http頭不全,這時經過responseBody和responseText獲取部分數據會出現錯誤,
    • 4 (完成) 數據接收完畢,此時能夠經過經過responseXml和responseText獲取完整的迴應數據
 
 
 
 
function createXHR() { if (XMLHttpRequest) { return new XMLHttpRequest(); }else if (ActiveObject) { var versions=["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"]; for (var i = 0,len=versions.length; i < len; i++) { try{ var xhr=new ActiveObject(versions[i]); if (xhr) { return xhr; } }catch(e){ return false; } } }else{ throw new Error("No XHR object available."); }}var xhr=createXHR();xhr.onreadystatechange=function(){ if (xhr.readyState===4) { if ((xhr.status>=200 && xhr.status<300)||xhr.status===304) { console.log(xhr.responseText); }else{ console.log("Request was unsuccessful:"+xhr.status); } }};/*Get請求數據鍵值須要使用encodeURIComponent編碼*/xhr.open("get","example.txt?name1=value1&name2=value2",true);//true表示異步xhr.setRequestHeader("Myheader","Myvaule");//在open方法以後,send方法以前設置請求頭xhr.send(null);/*POST請求數據鍵值須要使用encodeURIComponent編碼*/xhr.open("post","example.php",true);//true表示異步xhr.setRequestHeader("Myheader","Myvaule");//在open方法以後,send方法以前設置請求頭/*send方法的參數爲要發送的數據,格式爲name1=value1&name2=value2;若是想模擬表單,能夠添加請求頭Content-type:application/x-www-form-urlencoded*/xhr.send(data);

20.閉包的應用場景(草稿-非正式)

1.使用閉包代替小範圍使用全局變量
2.函數外或在其餘函數中訪問某一函數內部的參數
3.在函數執行以前爲要執行的函數提供具體參數
如:setTimeOut
setInterval
xhr.addEventListener("load",functionName, false);
若是functionName須要參數 怎麼辦呢

 
 
 
 
function functionNameFnc(a){ return function(){//作functionName該作的事情 已經能夠用參數了 a } } xhr.addEventListener("load",functionNameFnc(a), false);

4.爲節點循環綁定click事件,在事件函數中使用當次循環的值或節點,而不是最後一次循環的值或節點
4.封裝私有變量
紅寶書中提供:
1.使用閉包能夠在JavaScript中模擬塊級做用域
2.閉包能夠用於在對象中建立私有變量;
函數綁定、函數柯里化


21.使用JS事件委託的優勢和缺點

什麼???不知道事件委託,呵呵!!自行百度或者查閱紅寶書
優勢

  • 1.管理的函數變少了。不須要爲每一個元素都添加監聽函數。對於同一個父節點下面相似的子元素,能夠經過委託給父元素的監聽函數來處理事件。
  • 2.能夠方便地動態添加和修改元素,不須要由於元素的改動而修改事件綁定。
  • 3.JavaScript和DOM節點之間的關聯變少了,這樣也就減小了因循環引用而帶來的內存泄漏發生的機率。

缺點

  • 1.事件管理代碼有成爲性能瓶頸的風險,因此儘可能使它可以短小精悍;
  • 2.不是全部的事件都能冒泡的。blur、focus、load和unload不能像其它事件同樣冒泡。事實上blur和focus能夠用事件捕獲而非事件冒泡的方法得到(在IE以外的其它瀏覽器中);
  • 3.在管理鼠標事件的時候有些須要注意,若是處理mousemove這樣的事件的話趕上性能瓶頸的風險就很大,由於mousemove事件觸發很是頻繁,並且mouseout則由於其怪異的表現而變得很難用事件代理來管理。
  • 4.若是把全部事件都用代理就可能會出現事件誤判,即本不應綁定事件的元素被綁上了事件。

22.前端模塊化開發做用及基本原理(部分可參考第11題)

前端模塊化開發的做用

  • 提升可維護性。模塊化可讓每一個文件的職責單一,很是有利於代碼的維護
  • 解決變量污染、命名空間問題
    之前通常定義一個全局的對象來包裹全部的變量和方法var Util = {};
  • 解決文件依賴問題
    好比下面的一個例子,在dialog.js裏須要用到util.js裏的函數就必須在dialog.js以前引入util.js,使用模塊化加載能夠在dialog.js模塊裏引入util.js
 
 
 
 
<script src="util.js"></script><script src="dialog.js"></script>

使用requireJS加載模塊的簡要流程(網上找的,敘述的不是很好,大概那個意思)
1.咱們在使用requireJS時,都會把全部的js交給requireJS來管理,也就是咱們的頁面上只引入一個require.js,把data-main指向咱們的main.js。
2.經過咱們在main.js裏面定義的require方法或者define方法,requireJS會把這些依賴和回調方法都用一個數據結構保存起來。
3.當頁面加載時,requireJS會根據這些依賴預先把須要的js經過document.createElement的方法引入到dom中,這樣,被引入dom中的script便會運行。
4.因爲咱們依賴的js也是要按照requireJS的規範來寫的,因此他們也會有define或者require方法,一樣相似第二步這樣循環向上查找依賴,一樣會把他們村起來。
5.當咱們的js裏須要用到依賴所返回的結果時(一般是一個key value類型的object),requireJS便會把以前那個保存回調方法的數據結構裏面的方法拿出來而且運行,而後把結果給須要依賴的方法。

模塊加載基本原理

  • 1.路徑分析:id即路徑原則。
    一般咱們的入口是這樣的: require( [ 'a', 'b' ], callback ) 。這裏的 'a'、'b' 都是 ModuleId。經過 id 和路徑的對應原則,加載器才能知道須要加載的 js 的路徑。在這個例子裏,就是 baseUrl + 'a.js' 和 baseUrl + 'b.js'。但 id 和 path 的對應關係並非永遠那麼簡單,好比在 AMD 規範裏就能夠經過配置 Paths 來給特定的 id 指配 path。
  • 2.加載腳本內容:createElement('script') & appendChild
    知道路徑以後,就須要去請求。通常是經過 createElement('script') & appendChild 去請求。這個你們都知道,很少說。對於同源的文件模塊,有的加載器也會經過 AJAX 去請求腳本內容。
    通常來講,須要給 <script> 設置一個屬性用來標識模塊 id, 做用後面會提到。
  • 3.獲取當前文件路徑:document.currentScript
    獲取到正確的文件路徑,才能正確判斷依賴文件路徑
    a.js 裏多是 define( id, factory ) 或者是 define( factory ),後者被稱爲匿名模塊。那麼當 define(factory) 被執行的時候,咱們怎麼知道當前被定義的是哪一個模塊呢,具體地說,這個匿名模塊的實際模塊 id 是什麼? 答案是經過 document.currentScript 獲取當前執行的<script>,而後經過上面給 script 設置的屬性來獲得模塊 id。須要注意的是,低級瀏覽器是不支持 currentScript 的,這裏須要進行瀏覽器兼容。還能夠經過 script.onload 將script.src帶過去來處理這個事情。

  • 4.依賴分析
    在繼續講以前,須要先簡單介紹下模塊的生命週期。模塊在被 Define 以後並非立刻能夠用了,在你執行它的 factory 方法來生產出最終的 export 以前,你須要保證它的依賴是可用的。那麼首先就要先把依賴分析出來。簡單來講,就是經過 toString 這個方法獲得 factory 的內容,而後用正則去匹配其中的 require( 'moduleId' )。固然也能夠不用正則。

  • 5.遞歸加載
    在分析出模塊的依賴以後,咱們須要遞歸去加載依賴模塊。用僞代碼來表達大概是這樣的:

 
 
 
 
Module.prototype.load = function () { var deps = this.getDeps(); for (var i = 0; i < deps.length; i++) { var m = deps[i]; if (m.state < STATUS.LOADED) { m.load(); } } this.state = STATUS.LOADED;}

參考https://www.zhihu.com/question/21157540/answer/33583597


23.Js中訪問對象屬性用點和用中括號有什麼不一樣

  • 中括號運算符老是能代替點運算符。但點運算符卻不必定能所有代替中括號運算符。
  • 中括號運算符能夠用字符串變量的內容做爲屬性名。點運算符不能。
  • 中括號運算符能夠用純數字爲屬性名。點運算符不能。
  • 中括號運算符能夠用js的關鍵字和保留字做爲屬性名。點運算符不能
 
 
 
 
var foo = {name: 'kitten'}foo.name; // kittenfoo['name']; // kittenvar get = 'name';foo[get]; // kittenfoo.1234; // SyntaxErrorfoo['1234']; // works

24.Javascript垃圾回收方法

  • 標記清除(mark and sweep)
    這是JavaScript最多見的垃圾回收方式,當變量進入執行環境的時候,好比函數中聲明一個變量,垃圾回收器將其標記爲「進入環境」,當變量離開環境的時候(函數執行結束)將其標記爲「離開環境」。
    垃圾回收器會在運行的時候給存儲在內存中的全部變量加上標記,而後去掉環境中的變量以及被環境中變量所引用的變量(閉包),在這些完成以後仍存在標記的就是要刪除的變量了
  • 引用計數(reference counting)
    在低版本IE中常常會出現內存泄露,不少時候就是由於其採用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每一個值被使用的次數,當聲明瞭一個 變量並將一個引用類型賦值給該變量的時候這個值的引用次數就加1,若是該變量的值變成了另一個,則這個值得引用次數減1,當這個值的引用次數變爲0的時 候,說明沒有變量在使用,這個值無法被訪問了,所以能夠將其佔用的空間回收,這樣垃圾回收器會在運行的時候清理掉引用次數爲0的值佔用的空間。
    在IE中雖然JavaScript對象經過標記清除的方式進行垃圾回收,但BOM與DOM對象倒是經過引用計數回收垃圾的, 也就是說只要涉及BOM及DOM就會出現循環引用問題。

25.說說你對閉包的理解

使用閉包主要是爲了設計私有的方法和變量。閉包的優勢是能夠避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。在js中,函數即閉包,只有函數纔會產生做用域的概念
閉包有三個特性:

1.函數嵌套函數
2.函數內部能夠引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收


26.DOM操做——怎樣添加、移除、移動、複製、建立和查找節點。

  • 建立新節點
 
 
 
 
createDocumentFragment() //建立一個DOM片斷 createElement() //建立一個具體的元素 createTextNode() //建立一個文本節點
  • 添加、移除、替換、插入

    操做的都是子節點,調用時要先取父節點(parentNode)

 
 
 
 
appendChild() removeChild() replaceChild() insertBefore() //並無insertAfter()

能夠本身編寫一個insertAfter函數

 
 
 
 
function insertAfter(newElement,targetElement){ var parent=targetElemnt.parentNode; if(parent.lastChild==targetElement){ parent.appendChild(newElement) }else{ parent.insertBefore(newElement,targetElement.nextSlibing) }}

其餘方法

 
 
 
 
cloneNode()//一個參數,爲true時,深賦值,複製節點及其整個子節點樹,爲false時只複製節點自己normalize()//刪除空文本節點或者合併相鄰文本節點
  • 查找
 
 
 
 
getElementsByTagName() //經過標籤名稱 getElementsByName() //經過元素的Name屬性的值(IE容錯能力較強, 會獲得一個數組,其中包括id等於name值的) getElementById() //經過元素Id,惟一性 getElementByClassName()//html5 querySelector() querySelectorAll()



相關文章
相關標籤/搜索