let a = 100; typeof a;//number a = undefined; typeof a;//undefined a = null; typeof a;//object a instanceof Object;//false. instanceof 運算符檢測 Object.prototype 是否存在於 a 的原型鏈上
5 == true;//false。true會先轉換爲1,false轉換爲0 '12' + 1;//'123'。數字先轉換成字串 '12' - 1;//11。字串先轉換成數字 [] == false;//true。數組轉換爲其餘類型時,取決於另外一個比較者的類型 [] == 0;//true [2] == '2';//true [2] == 2;//true [] - 2;//-2 12 + {a:1};//"12[object Object]"。這裏對象先被轉換成了字串 null == false;//false null == 0;//false null - 1;//-1
> a = {"name":"cenze","age":28} > JSON.stringify(a,null,2) { "name": "cenze", "age": 28 }
let str = '23abGdH4d4Kd'; str = str.replace(/([a-z]*)([A-Z]*)/g, function(match, $1 , $2){return $1.toUpperCase() + $2.toLowerCase()});//"23ABgDh4D4kD" str = 'web-application-development'; str = str.replace(/-([a-z])/g, (replace)=>replace[1].toUpperCase());//"webApplicationDevelopment"(駝峯轉換)
str = Math.random().toString(36).substring(2)
3.2 / 1.7 | 0 = 1; ~~(3.2 / 1.7) = 1;//~~可還原原數 3.2 / 1.7 >> 0 = 1;
3.2 / 1.7 ^ 0 = 1;
-3.2 / 1.7 & 1 = 1;//& 1操做沒法保持符號不變 if (!~str.indexOf(substr)) {//~按位取反,等價於符號取反而後減一 //do some things }
let a = 2, b = 2.3; //方法一:Math.round() Math.round(a) === a;//true Math.round(b) === b;//false //方法二:模1運算 typeof a == "number" && (a % 1) === 0;//true typeof b == "number" && (b % 1) === 0;//false //方法三:ES6 Number.isInteger() Number.isInteger(a);//true Number.isInteger(b);//false //方法四:parseInt() parseInt(a, 10) === a;//true parseInt(b, 10) === b;//false //方法五:異或0運算 a ^ 0 === a;//true b ^ 0 === b;//false
true && 0;//0
false && 2;//false
1 && 2;//2 1 || 2;//1 0 || 1;//1
![] || !{} || 3;//3
[] == ![];//true
!NaN && !undefined && !null && !0 && !'' && 6;//6
//方法一:經過中間數組完成交換 let a = 1, b = 2; a = [b, b = a][0]; //方法二:經過加減運算完成交換 let a = 1, b = 2; a = a + b; b = a - b; a = a - b; //方法三:經過加減運算完成交換 let a = 1, b = 2; a = a - b; b = a + b; a = b - a; //方法四:經過兩次異或還原完成交換。另外,這裏不會產生溢出 let a = 1, b = 2; a = a ^ b; b = a ^ b; a = a ^ b; //方法五:經過乘法運算完成交換 let a = 1, b = 2; a = b + (b = a) * 0; //方法六:經過ES6解構賦值 let a = 1, b = 2; [a, b] = [b, a];
/、%、**運算符:html
5 / 3;//1.6666666666666667 4.53 % 2;//0.5300000000000002 5 % 3;//2 2 ** 3;//8,相比於Math.pow(2,3)要簡潔多了
compareFunction
可選,用來指定按某種順序進行排列的函數。若是省略,元素按照轉換爲的字符串的諸個字符的Unicode位點進行排序。好比:
[1, 10, 21, 2].sort();//[1, 10, 2, 21] [1, 10, 21, 2].sort((a, b) => a - b);//[1, 2, 10, 21]
// 須要被排序的數組 var list = ['Delta', 'alpha', 'CHARLIE', 'bravo']; // 對須要排序的數字和位置的臨時存儲 var mapped = list.map(function(el, i) { return { index: i, value: el.toLowerCase() }; }) // 按照多個值排序數組 mapped.sort(function(a, b) { return +(a.value > b.value) || +(a.value === b.value) - 1; }); // 根據索引獲得排序的結果 var result = mapped.map(function(el){ return list[el.index]; });
//方法一:先排序後去重 Array.prototype.unique = function(){ this.sort(); //先排序 let res = [this[0]]; for(let i = 1; i < this.length; i++){ if(this[i] !== res[res.length - 1]){ res.push(this[i]); } } return res; } //方法二:利用對象屬性惟一性避免重複元素 Array.prototype.unique = function(){ let res = [], deduplication = {}; for(let i = 0; i < this.length; i++){ if(!deduplication[this[i]]){ res.push(this[i]); deduplication[this[i]] = 1; } } return res; } //方法三:利用過濾函數遞代去重處理,該方法可保持數組原序不變 Array.prototype.unique = function () { let self = this, res = [], target; while (self.length) { target = self.shift(); res.push(target); self = self.filter(item => target !== item); } return res; } //方法四:利用ES6新增長的Set數據類型作中間轉換可去重 Array.prototype.unique = function () { return [...new Set(this)]; }
Math.max.apply(null, [2,5,30,1,20]);//30 Math.min.apply(null, [2,5,30,1,20]);//1
Math.min(...[2,5,30,1,20]);//1.ES6
1. encodeURI()對 URI 進行完整的編碼,但不會對「;/?:@&=+$,#」這些在 URI 中具備特殊含義的 ASCII 標點符號進行編碼,同時也不編碼「- _ . ! ~ * ' ( )」這些ASCII字符。但decodeURI("%27") == unescape("%27") == "'"; 2. encodeURIComponent()可把字符串做爲 URI 組件進行編碼,會對「;/?:@&=+$,#」這些在 URI 中具備特殊含義的 ASCII 標點符號進行編碼,但不編碼「- _ . ! ~ * ' ( )」這些ASCII字符。
setTimeout
函數會在一個時間段過去後在隊列中添加一個消息,這個時間段做爲函數的第二個參數被傳入。若是隊列中沒有其它消息,消息會被立刻處理;若是有其它消息,setTimeout
消息必須等待其它消息處理完。所以第二個參數僅僅表示最少的時間而非確切的時間。
瀏覽器的內核是多線程的,它們在內核控制下相互配合以保持同步,一個瀏覽器至少實現三個常駐線程:JavaScript引擎線程、GUI渲染線程、事件觸發線程
JavaScript引擎線程:一直等待着任務隊列中任務的到來,而後按優先級加以處理。瀏覽器不管何時都只有一個JavaScript線程在運行JavaScript程序。 GUI渲染線程:負責渲染瀏覽器界面,當界面須要重繪(Repaint)或因爲某種操做引起迴流(Reflow)時,該線程就會執行。但須要注意,GUI渲染線程與JavaScript引擎是互斥的,當JavaScript引擎執行時GUI線程會被掛起,GUI更新會被保存在一個隊列中等到JavaScript引擎空閒時當即被執行。 事件觸發線程:在一個事件被觸發時該線程會把事件添加到待處理隊列的隊尾,等待JavaScript引擎的處理。這些事件可來自JavaScript引擎當前執行的代碼塊如setTimeout、也可來自瀏覽器內核的其餘線程如鼠標點擊、Ajax異步請求等,但因爲JavaScript的單線程關係全部這些事件都得排隊等待JavaScript引擎處理(當線程中沒有執行任何同步代碼的前提下才會執行異步代碼)。
1. setTimeout()和setInterval()共用一個編號池,clearTimeout()和 clearInterval() 在技術上能夠互換;可是爲了不混淆,不要混用取消定時函數 2. 須要注意的是setTimeout()和setInterval()延遲執行的函數或代碼中的this會指向一個錯誤的值,由於那些函數或代碼是在一個分離的環境空間裏被執行的,這些代碼中包含的 this 關鍵字在非嚴格模式會指向 window (或全局)對象,嚴格模式下爲 undefined
3. IE < 9環境下setTimeout() 或者 setInterval()都不支持傳遞額外的參數,能夠考慮自行從新定義兼容代碼同名取代這兩個函數
4. setTimeout()的最小延遲時間與瀏覽器及操做系統有關,但不是0。在John Resig的《Javascript忍者的祕密》中:Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.另外Firefox中定義的最小時間間隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定義的最小時間間隔是4毫秒
5. 延遲只是代表可能的最小延遲時間,而不保證確切延遲時間。若是任務隊列很長,耗時不少,最後延遲時間可能遠遠超出參數中指定的值
function ClassA(sColor) { this.color = sColor; } ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB(sColor, sName) { ClassA.call(this, sColor); this.name = sName; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function () { alert(this.name); }; var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //輸出 "blue" objB.sayColor(); //輸出 "red" objB.sayName(); //輸出 "John"
var obj = { foo: 1, get bar() { return 2; } }; var copy = Object.assign({}, obj); // { foo: 1, bar: 2 } // copy.bar的值來自obj.bar的getter函數的返回值
call、apply和bind:call和apply的用途都是用來調用當前函數,且用法類似,它們的第一個參數都用做 this 對象,但其餘參數call要分開列舉,而apply要以一個數組形式傳遞;bind給當前函數定義預設參數後返回這個新的函數(初始化參數改造後的原函數拷貝),其中預設參數的第一個參數是this指定(當使用new
操做符調用新函數時,該this指定無效),新函數調用時傳遞的參數將位於預設參數以後與預設參數一塊兒構成其所有參數,bind
最簡單的用法是讓一個函數不論怎麼調用都有一樣的 this
值。下邊的list()也稱偏函數(Partial Functions):node
function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] // Create a function with a preset leading argument var leadingThirtysevenList = list.bind(undefined, 37); var list2 = leadingThirtysevenList(); // [37] var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
Memoization技術: 替代函數中太多的遞歸調用,是一種能夠緩存以前運算結果的技術,這樣咱們就不須要從新計算那些已經計算過的結果。In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. Although related to caching, memoization refers to a specific case of this optimization, distinguishing it from forms of caching such as buffering or page replacement. In the context of some logic programming languages, memoization is also known as tabling.web
function memoizer(fundamental, cache) { let cache = cache || {}, shell = function(arg) { if (! (arg in cache)) { cache[arg] = fundamental(shell, arg); } return cache[arg]; }; return shell; }
let singleton = function(){ let obj; return function(){ return obj || (obj = new Object()); } }();
let variables = { key1: 'value1', key2: 'value2' }, fnBody = 'return this.key1 + ":" + this.key2', fn = new Function(fnBody); console.log(fn.apply(variables));
var currying = function (fn) { var _args = []; return function () { if (arguments.length === 0) { return fn.apply(this, _args); } Array.prototype.push.apply(_args, [].slice.call(arguments)); return arguments.callee; } }; var add=function () { var total = 0; for (var i = 0, c; c = arguments[i++];) { total += c; } return total; }; var sum = currying(add); sum(100,200)(300);//ƒ () { ... } sum(400);//ƒ () { ... } sum();//1000
Function.prototype.uncurrying = function (){ var _this = this; return function (){ return Function.prototype.call.apply(_this,arguments); } }; function fn(){ console.log(`Hello ${this.value},${[].slice.call(arguments)}`); } var uncurryfn = fn.uncurrying(); uncurryfn({value:'World'},'Javascript'); //Hello World,Javascript
DocumentFragment: Roughly speaking, a DocumentFragment is a lightweight container that can hold DOM nodes. 在維護頁面DOM樹時,使用文檔片斷document fragments 一般會起到優化性能的做用shell
let ul = document.getElementsByTagName("ul")[0], docfrag = document.createDocumentFragment(); const browserList = [ "Internet Explorer", "Mozilla Firefox", "Safari", "Chrome", "Opera" ]; browserList.forEach((e) => { let li = document.createElement("li"); li.textContent = e; docfrag.appendChild(li); }); ul.appendChild(docfrag);
forEach()遍歷:另外,適當時候也能夠考慮使用for 或 for ... in 或 for ... of 語句結構後端
1. 數組實例遍歷: arr.forEach(function(item, key){ //do some things }) 2. 非數組實例遍歷: Array.prototype.forEach.call(obj, function(item, key){ //do some things }) 3. 非數組實例遍歷: Array.from(document.body.childNodes[0].attributes).forEach(function(item, key){ //do some things. Array.from()是ES6新增長的 })
DOM事件流:Graphical representation of an event dispatched in a DOM tree using the DOM event flow.If the bubbles
attribute is set to false, the bubble phase will be skipped, and if stopPropagation()
has been called prior to the dispatch, all phases will be skipped.數組
(1) 事件捕捉(Capturing Phase):event經過target的祖先從window傳播到目標的父節點。IE不支持Capturing
(2) 目標階段(Target Phase):event到達event的target。若是事件類型指示事件不冒泡,則event在該階段完成後將中止
(3) 事件冒泡(Bubbling Phase):event以相反的順序在目標祖先中傳播,從target的父節點開始,到window結束瀏覽器
事件綁定:緩存
1. Tag事件屬性綁定:<button onclick="do some things"></button> 2. 元素Node方法屬性綁定:btnNode.onclick = function(){//do some things} 3. 註冊EventListener:btnNode.addEventListener('click', eventHandler, bubble);另外,IE下實現與W3C有點不一樣,btnNode.attachEvent(‘onclick’, eventHandler)
遞歸與棧溢出(Stack Overflow):遞歸很是耗費內存,由於須要同時保存成千上百個調用幀,很容易發生「棧溢出」錯誤;而尾遞歸優化後,函數的調用棧會改寫,只保留一個調用記錄,但這兩個變量(func.arguments、func.caller,嚴格模式「use strict」會禁用這兩個變量,因此尾調用模式僅在嚴格模式下生效)就會失真。在正常模式下或者那些不支持該功能的環境中,採用「循環」替換「遞歸」,減小調用棧,就不會溢出多線程
function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10); // 89 Fibonacci(50);// 20365011074,耗時10分鐘左右才能出結果 Fibonacci(100);// 這裏就一直出不告終果,也沒有錯誤提示 function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100) // 573147844013817200000 Fibonacci2(1000) // 7.0330367711422765e+208 Fibonacci2(10000) // Infinity. "Uncaught RangeError:Maximum call stack size exceeded"
可見,經尾遞歸優化以後,性能明顯提高。若是不能使用尾遞歸優化,可以使用蹦牀函數(trampoline)將遞歸轉換爲循環:蹦牀函數中循環的做用是申請第三者函數來繼續執行未完成的任務,而保證本身函數能夠順利退出。另外,這裏的第三者和本身多是同一函數定義閉包
function trampoline(f) { while (f && f instanceof Function) { f = f(); } return f; } //改造Fibonacci2()的定義 function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2.bind(undefined, n - 1, ac2, ac1 + ac2); } trampoline(Fibonacci2(100));//573147844013817200000
預取與延遲加載:預取如提早加載圖片,當用戶須要查看時可直接從本地渲染,而不須要去服務端下載,它保證了圖片快速、無縫地發佈,讓用戶在瀏覽時得到更好的用戶體驗;延遲加載如先加載小圖或示例圖做爲佔位圖,只有進入瀏覽器可視區域內時,才加載完整圖片並顯示出來,在頁面圖片數量多且比較大的時候很是有用
mousemove
事件,執行屢次監聽函數;但若是對監聽函數用 100ms 的Debounce去彈跳後,那麼瀏覽器只會在第 3.1s 的時候執行這個監聽函數一次。下邊是Debounce的實現:
/** * * @param fn {Function} 該函數定義了事件觸發後的業務處理邏輯 * @param delay {Number} 延遲時間,單位是毫秒(ms) * * @return {Function} 目標事件將要觸發執行的監聽函數 */ function debounce(fn, delay) { // 定時器,用來 setTimeout let timer // 返回一個「去彈跳」了的監聽函數 return function () { // 保存函數調用時的上下文和參數,傳遞給 fn let context = this, args = arguments; // 先清除定時器,保證 delay 毫秒內 fn 不執行屢次 clearTimeout(timer) // 用戶中止當前連續操做後的第 delay 毫秒執行 fn timer = setTimeout(function () { fn.apply(context, args) }, delay) } }
/** * * @param fn {Function} 該函數定義了事件觸發後的業務處理邏輯 * @param delay {Number} 執行間隔,單位是毫秒(ms) * * @return {Function} 目標事件將要觸發執行的監聽函數 */ function throttle(fn, threshhold) { // 記錄上次執行的時間 let last, // 定時器 timer; // 默認間隔爲 250ms threshhold || (threshhold = 250);
// 返回一個「經節流」了的監聽函數 return function () { // 保存函數調用時的上下文和參數,傳遞給 fn let context = this, args = arguments, now = +new Date(); // 若是距離上次執行 fn 函數的時間小於 threshhold,那麼就放棄 // 執行 fn,並從新計時 if (last && now < last + threshhold) { clearTimeout(timer) timer = setTimeout(function () { last = now fn.apply(context, args) }, threshhold) } else {// 在連續動做剛開始或超過threshhold間隔時當即執行 fn last = now fn.apply(context, args) } } }