JavaScript 是一種腳本語言,官方名稱爲 ECMAScript(因定義語言的標準爲 ECMA-262)。JS 的主要特色:1. 語法相似於常見的高級語言,如 C 和 Java;2. 腳本語言,不須要編譯就能夠由解釋器直接運行;3. 變量鬆散定義,屬於弱類型語言;4. 面向對象的。 JS 最初是爲網頁設計而開發的,如今也是 Web 開發的重要語言。它支持對瀏覽器(瀏覽器對象模型,BOM)和 HTML 文檔(文檔對象模型,DOM)進行操做,而使網頁呈現動態的交互特性。 嚴格的說,JS 只是 ECMAScript 的一種實現,是 ECMAScript 和 BOM、DOM 組成的一種 Web 開發技術。javascript
基本數據類型:String,Boolean,Number,Undefined, Null
引用數據類型:Object(Array,Date,RegExp,Function)
那麼問題來了,如何判斷某變量是否爲數組數據類型?php
function isArray(value){return Object.prototype.toString.call(value) == "[object Array]";}
cookie是網站爲了標示用戶身份而儲存在用戶本地終端(Client Side)上的數據(一般通過加密)。cookie數據始終在同源的http請求中攜帶(即便不須要),記會在瀏覽器和服務器間來回傳遞。sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存。html
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(); //羅恩
f1(f2);
f1.on('done',f2);
f1: jQuery.publish("done");
f2: jQuery.subscribe("done", f2);
f1().then(f2);
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^53
到 2^53
之間(不含兩個端點),超過這個範圍,沒法精確表示這個值。ES6 引入了Number.MAX_SAFE_INTEGER
和 Number.MIN_SAFE_INTEGER
這兩個常量,用來表示這個範圍的上下限,並提供了 Number.isSafeInteger()
來判斷整數是不是安全型整數。web
AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出。CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。主要區別是
AMD和CMD最大的區別是對依賴模塊的執行時機處理不一樣,注意不是加載的時機或者方式不一樣
不少人說requireJS是異步加載模塊,SeaJS是同步加載模塊,這麼理解其實是不許確的,其實加載模塊都是異步的,只不過AMD依賴前置,js能夠方便知道依賴模塊是誰,當即加載,而CMD就近依賴,須要使用把模塊變爲字符串解析一遍才知道依賴了那些模塊,這也是不少人詬病CMD的一點,犧牲性能來帶來開發的便利性,實際上解析模塊用的時間短到能夠忽略
爲何咱們說兩個的區別是依賴模塊執行時機不一樣,爲何不少人認爲AMD是異步的,CMD是同步的(除了名字的緣由。。。)
一樣都是異步加載模塊,AMD在加載模塊完成後就會執行改模塊,全部模塊都加載執行完後會進入require的回調函數,執行主邏輯,這樣的效果就是依賴模塊的執行順序和書寫順序不必定一致,看網絡速度,哪一個先下載下來,哪一個先執行,可是主邏輯必定在全部依賴加載完成後才執行
CMD加載完某個依賴模塊後並不執行,只是下載而已,在全部依賴模塊加載完成後進入主邏輯,遇到require語句的時候才執行對應的模塊,這樣模塊的執行順序和書寫順序是徹底一致的
這也是不少人說AMD用戶體驗好,由於沒有延遲,依賴模塊提早執行了,CMD性能好,由於只有用戶須要的時候才執行的緣由
AMD
推薦的風格經過返回一個對象作爲模塊對象,CommonJS
的風格經過對module.exports
或exports
的屬性賦值來達到暴露模塊對象的目的。
順便提一下: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;});
1.經過jsonp跨域
script
標籤自身的限制決定的。2.經過修改document.domain來跨子域(iframe)
3.隱藏的iframe+window.name跨域
4.iframe+跨文檔消息傳遞(XDM)
5.跨域資源共享 CORS
6.Web Sockets
跨域請求並不是是瀏覽器限制了發起跨站請求,而是請求能夠正常發起,到達服務器端,可是服務器返回的結果會被瀏覽器攔截。
1.對象字面量
2.Object構造函數
mygirl=new Object();
ES5新方法構建對象,再添加屬性
3.工廠模式
工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。
4.構造函數模式
使用構造函數的主要問題,就是每一個方法都要在每一個實例上從新建立一遍。
5.原型模式
function Girl(){
}
在Girl.prototype上添加屬性和方法
var mygirl=new Girl();
特色:原型中全部屬性是被全部實例共享的,這種共享對於函數很是合適,可是對於基本屬性就顯得不是很合適,尤爲是對於組合使用原型模式和構造函數建立對象包含引用類型值的屬性來講,問題就比較突出了。
6.組合使用原型模式和構造函數建立對象(推薦)
建立自定義類型的最多見方式,就是組合使用構造函數模式與原型模式。構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。
7.動態原型模式
相對於組合模式,就是把原型上添加方法的步驟放在構造函數中,而後根據構造函數中是否已經存在該方法來決定添不添加
8.寄生構造函數模式
相對於工廠模式就是把函數當作構造函數調用
9.穩妥構造函數模式
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; //指定對象}
一、聲明函數
最普通最標準的聲明函數方法,包括函數名及函數體。
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
call方法與apply方法的做用是同樣的,都是爲了改變函數內部的this指向。區別僅在於傳入的參數形式的不一樣。
apply函數接受兩個參數,第一個參數指定了函數體內this對象的指向,第二個參數爲一個能夠下標訪問的集合,這個集合可使數組,也能夠是類數組,apply方法把這個集合中的元素做爲參數傳遞給被調用的函數。
call方法傳入的參數數量是不固定的,跟apply相同的是,第一個參數也是表明函數體內this對象的指向,從第二個參數開始日後,是一組參數序列,每一個參數被依次傳入函數。
Notes
現代的瀏覽器大多采用標記清除的方法來進行垃圾回收,其基本步驟以下:
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
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);
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內存佔用的工具。與內存相關的兩個重要的工具:timeline
和 profiles
。具體參考Chrome開發者工具之JavaScript內存分析
Ajax 的全稱是Asynchronous JavaScript and XML,其中,Asynchronous 是異步的意思,它有別於傳統web開發中採用的同步的方式。
Ajax的原理簡單來講經過XmlHttpRequest對象來向服務器發異步請求,從服務器得到數據,而後用javascript來操做DOM而更新頁面。
XMLHttpRequest是ajax的核心機制,它是在IE5中首先引入的,是一種支持異步請求的技術。簡單的說,也就是javascript能夠及時向服務器提出請求和處理響應,而不阻塞用戶。達到無刷新的效果。
XMLHttpRequest這個對象的屬性有:
- 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);
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.閉包能夠用於在對象中建立私有變量;
函數綁定、函數柯里化
什麼???不知道事件委託,呵呵!!自行百度或者查閱紅寶書
優勢
缺點
前端模塊化開發的做用
<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便會把以前那個保存回調方法的數據結構裏面的方法拿出來而且運行,而後把結果給須要依賴的方法。
模塊加載基本原理
<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
var foo = {name: 'kitten'}foo.name; // kittenfoo['name']; // kittenvar get = 'name';foo[get]; // kittenfoo.1234; // SyntaxErrorfoo['1234']; // works
JavaScript
對象經過標記清除的方式進行垃圾回收,但BOM與DOM對象倒是經過引用計數回收垃圾的, 也就是說只要涉及BOM
及DOM就會出現循環引用問題。使用閉包主要是爲了設計私有的方法和變量。閉包的優勢是能夠避免全局變量的污染,缺點是閉包會常駐內存,會增大內存使用量,使用不當很容易形成內存泄露。在js中,函數即閉包,只有函數纔會產生做用域的概念
閉包有三個特性:
1.函數嵌套函數
2.函數內部能夠引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收
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()