JavaScript小技巧整理篇(非常全)
能夠?yàn)榇蠹姨峁┻@些簡短而實(shí)用的JavaScript技巧來提高大家編程能力,這對(duì)于我來說是件很開心的事。每天僅花上不到2分鐘的時(shí)間中,你將可以讀遍JavaScript這門可怕的語言所呈現(xiàn)給我們的特性:performance(性能), conventions(協(xié)議), hacks(代碼hack), interview questions(面試問題)及所有其他的項(xiàng)。
#24 - 使用 === 代替 ==
==(或者!=)做對(duì)比的時(shí)候會(huì)將進(jìn)行對(duì)比的兩者轉(zhuǎn)換到同一類型再比較。===(或者!==)則不會(huì),他會(huì)將進(jìn)行對(duì)比的兩者做類型對(duì)比和值對(duì)比,相對(duì)于 == ,=== 的對(duì)比會(huì)更加嚴(yán)謹(jǐn)。
[10] == 10 // true [10] === 10 // false "10" == 10 // true "10" === 10 // false [] == 0 // true [] === 0 // false "" == false // true 但是 true == "a" 是false "" === false // false
#23 - 轉(zhuǎn)換數(shù)值的更加的方法
將字符串轉(zhuǎn)換為數(shù)字是非常常見的。最簡單和最快的(jspref)的方式來實(shí)現(xiàn),將使用+(加)算法。
var one = '1'; var numberOne = +one; // Number 1
你也可以使用-(減號(hào))算法的轉(zhuǎn)換類型并且變成負(fù)數(shù)值。
var one = '1'; var negativeNumberOne = -one; // Number -1
#22 - 清空一個(gè)數(shù)組
你定義一個(gè)數(shù)組,并希望清空它的內(nèi)容。通常,你會(huì)這樣做:
var list = [1, 2, 3, 4]; function empty() { //清空數(shù)組 list = []; } empty();
但是還有一種更高性能的方法。
你可以使用這些代碼:
var list = [1, 2, 3, 4]; function empty() { //清空數(shù)組 list.length = 0; } empty();
· list =[] 將一個(gè)變量指定個(gè)引用到那個(gè)數(shù)組,而其他引用都不受影響。這意味著,對(duì)于先前數(shù)組的內(nèi)容的引用仍然保留在內(nèi)存中,從而導(dǎo)致內(nèi)存泄漏。
· list.length = 0 刪除數(shù)組內(nèi)的所有東西,這不需要引用任何其他的東西
然而,如果你有一個(gè)copy的數(shù)組(A和copy-A),如果你使用list.length = 0 刪除其內(nèi)容,副本也會(huì)失去它的內(nèi)容。
var foo = [1,2,3]; var bar = [1,2,3]; var foo2 = foo; var bar2 = bar; foo = []; bar.length = 0; console.log(foo, bar, foo2, bar2); //[] [] [1, 2, 3] []
StackOverflow上的更多詳情:difference-between-array-length-0-and-array
#21 - 對(duì)數(shù)組排序進(jìn)行"洗牌"(隨機(jī)排序)
這段代碼在這里使用Fisher Yates洗牌算法給一個(gè)指定的數(shù)組進(jìn)行洗牌(隨機(jī)排序)。
function shuffle(arr) { var i, j, temp; for (i = arr.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } return arr; };
案例:
var a = [1, 2, 3, 4, 5, 6, 7, 8]; var b = shuffle(a); console.log(b); // [2, 7, 8, 6, 5, 3, 1, 4]
#20 - 返回對(duì)象的函數(shù)能夠用于鏈?zhǔn)讲僮?/span>
當(dāng)創(chuàng)建面向?qū)ο蟮腏avaScript對(duì)象的function時(shí),函數(shù)返回一個(gè)對(duì)象將能夠讓函數(shù)可鏈?zhǔn)降膶懺谝黄饋韴?zhí)行。
function Person(name) { this.name = name; this.sayName = function() { console.log("Hello my name is: ", this.name); return this; }; this.changeName = function(name) { this.name = name; return this; }; } var person = new Person("John"); person.sayName().changeName("Timmy").sayName(); //Hello my name is: John //Hello my name is: Timmy
#19 - 字符串安全連接
假設(shè)你有一些類型未知的變量,你想將它們連接起來。可以肯定的是,算法操作不會(huì)在級(jí)聯(lián)時(shí)應(yīng)用:
var one = 1; var two = 2; var three = '3'; var result = ''.concat(one, two, three); //"123"
這樣的連接不正是你所期望的。相反,一些串聯(lián)和相加可能會(huì)導(dǎo)致意想不到的結(jié)果:
var one = 1; var two = 2; var three = '3'; var result = one + two + three; //"33" 而不是 "123"
談到性能,對(duì)join和concat進(jìn)行比較,他們的執(zhí)行速度是幾乎一樣的。你可以在MDN了解更多與concat相關(guān)的知識(shí)
#18 - 更快的四舍五入
今天的技巧是關(guān)于性能。見到過雙波浪線"~~"操作符嗎?它有時(shí)也被稱為double NOT運(yùn)算符。你可以更快的使用它來作為Math.floor()替代品。為什么呢?
單位移~將32位轉(zhuǎn)換輸入-(輸入+1),因此雙位移將輸入轉(zhuǎn)換為-(-(輸入+1)),這是個(gè)趨于0的偉大的工具。對(duì)于輸入的數(shù)字,它將模仿Math.ceil()取負(fù)值和Math.floor()取正值。如果執(zhí)行失敗,則返回0,這可能在用來代替Math.floor()失敗時(shí)返回一個(gè)NaN的時(shí)候發(fā)揮作用。
// 單位移 console.log(~1337) // -1338 // 雙位移 console.log(~~47.11) // -> 47 console.log(~~-12.88) // -> -12 console.log(~~1.9999) // -> 1 console.log(~~3) // -> 3 //失敗的情況 console.log(~~[]) // -> 0 console.log(~~NaN) // -> 0 console.log(~~null) // -> 0 //大于32位整數(shù)則失敗 console.log(~~(2147483647 + 1) === (2147483647 + 1)) // -> 0
雖然~~可能有更好的表現(xiàn),為了可讀性,請(qǐng)使用Math.floor()。
#17 - Node.js:讓module在沒被require的時(shí)候運(yùn)行
在node里,你可以根據(jù)代是運(yùn)行了require('./something.js')還是node something.js,來告訴你的程序去做兩件不同的事情。如果你想與你的一個(gè)獨(dú)立的模塊進(jìn)行交互,這是很有用的。
if (!module.parent) { // 運(yùn)行 `node something.js` app.listen(8088, function() { console.log('app listening on port 8088'); }) } else { // 使用 `require('/.something.js')` module.exports = app; }
更多信息,請(qǐng)看the documentation for modules
#16 - 給回調(diào)函數(shù)傳遞參數(shù)
在默認(rèn)情況下,你無法將參數(shù)傳給回調(diào)函數(shù),如下:
function callback() { console.log('Hi human'); } document.getElementById('someelem').addEventListener('click', callback);
你可以采取JavaScript閉包的優(yōu)點(diǎn)來給回調(diào)函數(shù)傳參,案例如下:
function callback(a, b) { return function() { console.log('sum = ', (a+b)); } } var x = 1, y = 2; document.getElementById('someelem').addEventListener('click', callback(x, y));
什么是閉包呢?閉包是指一個(gè)針對(duì)獨(dú)立的(自由)變量的函數(shù)。換句話說,閉包中定義的函數(shù)會(huì)記住它被創(chuàng)建的環(huán)境。了解更多請(qǐng)參閱MDN所以這種方式當(dāng)被調(diào)用的時(shí)候,參數(shù)X/Y存在于回調(diào)函數(shù)的作用域內(nèi)。
另一種方法是使用綁定方法。例如:
var alertText = function(text) { alert(text); }; document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello'));
兩種方法在性能上有一些略微區(qū)別,詳情參閱jsperf
#15 - 使用更簡單的類似indexOf的包含判斷方式
原生的JavaScript沒有contains方法。對(duì)檢查字符串或字符串?dāng)?shù)組項(xiàng)中是否存在某值,你可以這樣做:
var someText = 'JavaScript rules'; if (someText.indexOf('JavaScript') !== -1) { } // 或者 if (someText.indexOf('JavaScript') >= 0) { }
但是我們?cè)倏纯催@些ExpressJs代碼片段。
// examples/mvc/lib/boot.js for (var key in obj) { // "reserved" exports if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue; // examples/lib/utils.js exports.normalizeType = function(type){ return ~type.indexOf('/') ? acceptParams(type) : { value: mime.lookup(type), params: {} }; }; // examples/web-service/index.js // key is invalid if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));
問題是~位運(yùn)算符。"運(yùn)算符執(zhí)行操作這樣的二進(jìn)制表達(dá)式,但他們返回標(biāo)準(zhǔn)的JavaScript的數(shù)值."
他們將-1轉(zhuǎn)換為0,而0在JavaScript中又是false。
var someText = 'text'; !!~someText.indexOf('tex'); // someText 包含 "tex" - true !~someText.indexOf('tex'); // someText 不包含 "tex" - false ~someText.indexOf('asd'); // someText 不包含 "asd" - false ~someText.indexOf('ext'); // someText 包含 "ext" - true String.prototype.includes()
在ES6(ES 2015)中介紹了includes()方法可以用來確定是否一個(gè)字符串包含另一個(gè)字符串:
'something'.includes('thing'); // true
在ECMAScript 2016 (ES7)中,甚至數(shù)組都可以這樣操作,如indexOf:
!!~[1, 2, 3].indexOf(1); // true
[1, 2, 3].includes(1); // true
不幸的是,這只是在Chrome,F(xiàn)irefox,Safari 9或以上的瀏覽器中被支持。
#14 - arrow 函數(shù)(ES6)
介紹下ES6里的新功能,arrow函數(shù)可能會(huì)是個(gè)很方便的工具,用更少行數(shù)寫更多代碼。他的名字來源于他的語法,=>和小箭頭->比就像一個(gè)“胖胖的箭頭”。可能有些人知道,這種函數(shù)類型和其他靜態(tài)語言如lambda表達(dá)式的匿名函數(shù)。它被稱為匿名,因?yàn)檫@些箭頭函數(shù)沒有一個(gè)描述性的函數(shù)名。
那么這樣有什么好處呢?
語法:更少的LOC,不用一次次的鍵入函數(shù)關(guān)鍵字。
語義:從上下文中捕捉關(guān)鍵字this。
簡單語法案例:
看看下面的兩段代碼片段,他們做的是一樣的工作。你能很快的理解arrow函數(shù)的功能。
// arrow函數(shù)的日常語法 param => expression // 可能也會(huì)寫在括號(hào)中 // 括號(hào)是多參數(shù)要求 (param1 [, param2]) => expression // 使用日常函數(shù) var arr = [5,3,2,9,1]; var arrFunc = arr.map(function(x) { return x * x; }); console.log(arr) // 使用arrow函數(shù) var arr = [5,3,2,9,1]; var arrFunc = arr.map((x) => x*x); console.log(arr)
正如你所看到的,這個(gè)例子中的arrow函數(shù)可以節(jié)省你輸入括號(hào)內(nèi)參數(shù)和返回關(guān)鍵字的時(shí)間。建議把圓括號(hào)內(nèi)的參數(shù)輸入,如 (x,y) => x+y 。在不同的使用情況下,它只是
用來應(yīng)對(duì)遺忘的一種方式。但是上面的代碼也會(huì)這樣執(zhí)行:x => x*x.目前看來,這些僅僅是導(dǎo)致更少的LOC和更好的可讀性的句法改進(jìn)。
this 綁定
還有一個(gè)更好的理由使用arrow函數(shù)。那就是在會(huì)出現(xiàn)this問題的背景下。使用arrow函數(shù),你就不用擔(dān)心.bind(this)和 that=this 了。因?yàn)閍rrow函數(shù)會(huì)從上下文中找到this。
看下面的例子:
// 全局定義this.i this.i = 100; var counterA = new CounterA(); var counterB = new CounterB(); var counterC = new CounterC(); var counterD = new CounterD(); // 不好的示例 function CounterA() { // CounterA's `this` 實(shí)例 (!! 忽略這里) this.i = 0; setInterval(function () { // `this` 指全局對(duì)象,而不是 CounterA's `this` // 因此,開始計(jì)數(shù)與100,而不是0 (本地的 this.i) this.i++; document.getElementById("counterA").innerHTML = this.i; }, 500); } // 手動(dòng)綁定 that = this function CounterB() { this.i = 0; var that = this; setInterval(function() { that.i++; document.getElementById("counterB").innerHTML = that.i; }, 500); } // 使用 .bind(this) function CounterC() { this.i = 0; setInterval(function() { this.i++; document.getElementById("counterC").innerHTML = this.i; }.bind(this), 500); } // 使用 arrow函數(shù) function CounterD() { this.i = 0; setInterval(() => { this.i++; document.getElementById("counterD").innerHTML = this.i; }, 500); }
關(guān)于arrow函數(shù)的進(jìn)一步信息可以看這里 。查看不同的語法選請(qǐng)?jiān)L問該站點(diǎn)。
#13 - 測量一個(gè)JavaScript代碼塊性能的技巧
快速測量一個(gè)JavaScript塊的性能,我們可以使用控制臺(tái)的功能像console.time(label)和console.timeEnd(label)
console.time("Array initialize"); var arr = new Array(100), len = arr.length, i; for (i = 0; i < len; i++) { arr[i] = new Object(); }; console.timeEnd("Array initialize"); // 輸出: Array initialize: 0.711ms
更多信息Console object, JavaScript benchmarking
demo:jsfiddle-codepen (在瀏覽器控制臺(tái)輸出)
#12 - ES6中參數(shù)處理
在許多編程語言中,函數(shù)的參數(shù)是默認(rèn)的,而開發(fā)人員必須顯式定義一個(gè)參數(shù)是可選的。在JavaScript中的每個(gè)參數(shù)是可選的,但我們可以這一行為而不讓一個(gè)函數(shù)利用ES6的默認(rèn)值作為參數(shù)。
const _err = function( message ){ throw new Error( message ); } const getSum = (a = _err('a is not defined'), b = _err('b is not defined')) => a + b getSum( 10 ) // throws Error, b is not defined getSum( undefined, 10 ) // throws Error, a is not defined
_err是立即拋出一個(gè)錯(cuò)誤的函數(shù)。如果沒有一個(gè)參數(shù)作為值,默認(rèn)值是會(huì)被使用,_err將被調(diào)用,將拋出錯(cuò)誤。你可以在Mozilla開發(fā)者網(wǎng)絡(luò)看到的更多默認(rèn)參數(shù)的例子。
#11 - 提升
理解提升將幫助你組織你的function。只需要記住,變量聲明和定義函數(shù)會(huì)被提升到頂部。變量的定義是不會(huì)的,即使你在同一行中聲明和定義一個(gè)變量。此外,變量聲明讓系統(tǒng)知道變量存在,而定義是將其賦值給它。
function doTheThing() { // 錯(cuò)誤: notDeclared is not defined console.log(notDeclared); // 輸出: undefined console.log(definedLater); var definedLater; definedLater = 'I am defined!' // 輸出: 'I am defined!' console.log(definedLater) // Outputs: undefined console.log(definedSimulateneously); var definedSimulateneously = 'I am defined!' // 輸出: 'I am defined!' console.log(definedSimulateneously) // 輸出: 'I did it!' doSomethingElse(); function doSomethingElse(){ console.log('I did it!'); } // 錯(cuò)誤: undefined is not a function functionVar(); var functionVar = function(){ console.log('I did it!'); } }
為了使事情更容易閱讀,在函數(shù)作用域內(nèi)提升變量的聲明將會(huì)讓你明確該變量的聲明是來自哪個(gè)作用域。在你需要使用變量之前定義它們。在作用域底部定義函數(shù),確保代碼清晰規(guī)范。
#10 - 檢查一個(gè)對(duì)象是否有屬性
當(dāng)你要檢查一個(gè)對(duì)象是否存在某個(gè)屬性時(shí),你可能會(huì)這樣做 :
var myObject = { name: '@tips_js' }; if (myObject.name) { ... }
這是可以的,但你必須知道這個(gè)還有兩原生的方式,in operator 和 object.hasownproperty,每個(gè)對(duì)象是對(duì)象,既可用方法。每個(gè)object都繼承自O(shè)bject,這兩個(gè)方法都可用。
兩個(gè)方法的一些不同點(diǎn):
var myObject = { name: '@tips_js' }; myObject.hasOwnProperty('name'); // true 'name' in myObject; // true myObject.hasOwnProperty('valueOf'); // false, valueOf 是從原型鏈繼承的 'valueOf' in myObject; // true
他們之間的不同在于檢查的性質(zhì),換句話說,當(dāng)該對(duì)象本身有查找的屬性時(shí)hasOwnProperty返回yrue,然而,in operator不區(qū)分屬性創(chuàng)建的對(duì)象和屬性繼承的原型鏈。
這里有另外一個(gè)例子:
var myFunc = function() { this.name = '@tips_js'; }; myFunc.prototype.age = '10 days'; var user = new myFunc(); user.hasOwnProperty('name'); // true user.hasOwnProperty('age'); // false, 因?yàn)閍ge是原型鏈上的
點(diǎn)擊看例子。同時(shí),建議在檢查對(duì)象的屬性存在時(shí),閱讀這些有關(guān)的常見錯(cuò)誤。
#09 - 模板字符串
截至ES6,JS已經(jīng)有模板字符串作為替代經(jīng)典的結(jié)束引用的字符串。
案例:普通字符串
var firstName = 'Jake'; var lastName = 'Rawr'; console.log('My name is ' + firstName + ' ' + lastName); // My name is Jake Rawr 模板字符串: var firstName = 'Jake'; var lastName = 'Rawr'; console.log(`My name is ${firstName} ${lastName}`); // My name is Jake Rawr
在模板字符串中${}中,你可以寫不用寫/n或者簡單邏輯來實(shí)現(xiàn)多行字符串。
您還可以使用函數(shù)來修改模板字符串的輸出,它們被稱為模板字符串的標(biāo)記。你可能還想讀到更多的理解模板字符串相關(guān)信息。
#08 - 將節(jié)點(diǎn)列表轉(zhuǎn)換為數(shù)組
querySelectorAll 方法返回一個(gè)和數(shù)組類似的節(jié)點(diǎn)列表對(duì)象。這些數(shù)據(jù)結(jié)構(gòu)類似數(shù)組,因?yàn)榻?jīng)常以數(shù)組形式出現(xiàn),但是又不能用數(shù)組的方法,比如map和foreach。這里是一個(gè)快速、安全、可重用的方式將一個(gè)節(jié)點(diǎn)列表到一個(gè)DOM元素?cái)?shù)組:
const nodelist = document.querySelectorAll('div'); const nodelistToArray = Array.apply(null, nodelist); //later on .. nodelistToArray.forEach(...); nodelistToArray.map(...); nodelistToArray.slice(...); //etc...
apply方法是將一系列數(shù)組格式的參數(shù)傳遞給一個(gè)給定this的函數(shù)。MDN指出,apply將會(huì)調(diào)用類似數(shù)組的對(duì)象,而這正是querySelectorAll所返回的。因?yàn)槲覀儾恍枰诤瘮?shù)的上下文中指定this,所以我們傳入null或0。返回的結(jié)果是一組能使用數(shù)組方法的DOM元素?cái)?shù)組。
如果你使用的是es2015可以利用...(spread operator)
const nodelist = [...document.querySelectorAll('div')]; // 返回的是個(gè)真實(shí)的數(shù)組 //later on .. nodelist.forEach(...); nodelist.map(...); nodelist.slice(...); //etc...
#07 - "use strict" 和懶惰
嚴(yán)格模式的JavaScript讓開發(fā)人員更加安全的編寫JavaScript。
默認(rèn)情況下,JavaScript允許開發(fā)者懶惰,例如,我們?cè)诘谝淮温暶髯兞康臅r(shí)候可以不用var,雖然這可能看起來像一個(gè)沒有經(jīng)驗(yàn)的開發(fā)人員,同時(shí)這也是很多錯(cuò)誤的根源,變量名拼寫錯(cuò)誤或意外地將它提到了外部作用域。
程序員喜歡讓電腦為我們做些無聊的事,檢查一些我們工作的錯(cuò)誤。"use strict"指令我們做這些,將我們的錯(cuò)誤轉(zhuǎn)換成JavaScript的錯(cuò)誤。
我們把這個(gè)指令可以通過添加在一個(gè)js文件的頂部:
// 整個(gè)script文件都將是嚴(yán)格模式語法 "use strict"; var v = "Hi! I'm a strict mode script!"; 或者在函數(shù)內(nèi): function f() { // 函數(shù)范圍內(nèi)的嚴(yán)格模式語法 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function f2() { return "I'm not strict."; }
在包含這個(gè)指令的JavaScript文件或者函數(shù)內(nèi),我們將一些較大的JavaScript項(xiàng)目中的不良行為直接在JavaScript引擎執(zhí)行中禁止了。在其他情況中,嚴(yán)格模式改變以下的行為:
· 變量只有在前面 var 聲明了才能用
· 試圖寫入只讀屬性產(chǎn)生的誤差
· 必須用 new 關(guān)鍵字調(diào)用構(gòu)造函數(shù)
· this 不會(huì)默認(rèn)指向全局對(duì)象
· 非常有限的使用eval()
· 保護(hù)保留字符或未來保留字符不被作為變量名使用
嚴(yán)格模式在新項(xiàng)目中是很有好處的,但是在大多數(shù)地方?jīng)]使用到它的老項(xiàng)目里使用它是非常具有挑戰(zhàn)性的。當(dāng)你把多個(gè)文件合并到一個(gè)文件時(shí),它也是個(gè)問題,就像可能導(dǎo)致整個(gè)文件都在嚴(yán)格模式下執(zhí)行。
它不是一個(gè)聲明,只是一個(gè)字面量,早期版本的瀏覽器會(huì)忽略它。嚴(yán)格模式支持:
· IE 10+
· FF 4+
· Chrome 13+
· Safari 5.1+
· Opera 12+
參閱MDN對(duì)于嚴(yán)格模式的描述。
#06 - 處理一個(gè)數(shù)組或單個(gè)元素作為參數(shù)的方法
相比于寫個(gè)單獨(dú)的方法去分別操作一個(gè)數(shù)組和一個(gè)元素作為參數(shù)的函數(shù),更好的是寫一個(gè)通用的函數(shù),這樣就都可以操作。這類似于一些jQuery的方法(css匹配將修改所有的選擇器)。
你僅需要先將一切放進(jìn)數(shù)組,Array.concat會(huì)接收數(shù)組或單一的對(duì)象:
function printUpperCase(words) { var elements = [].concat(words); for (var i = 0; i < elements.length; i++) { console.log(elements[i].toUpperCase()); } }
printUpperCase現(xiàn)在可以接收無論單一的元素作為參數(shù)還是一個(gè)數(shù)組:
printUpperCase("cactus"); // => CACTUS printUpperCase(["cactus", "bear", "potato"]); // => CACTUS // BEAR // POTATO
#05 - undefined 和 null 的不同
· undefined指的是一個(gè)變量未被聲明,或者一個(gè)變量被聲明但未賦值
· null是指一個(gè)特定的值,即"沒有值"
. JavaScript給未賦值的變量默認(rèn)定義為undefined
· JavaScript不會(huì)給未賦值的變量設(shè)置null值,它被程序員用來表示一個(gè)無價(jià)值的值
· undefined在json格式數(shù)據(jù)中是無效的,而null有效
· undefined 類型是 undefined
· null類似是object.為什么呢?
· 兩者都是原始值
· 兩者都被認(rèn)為false(Boolean(undefined) // false, Boolean(null) // false)。
· 辨認(rèn)變量是不是undefined
typeof variable === "undefined"
· 檢查變量是不是null
variable === "null"
從值考慮他們是相等的,但是從類型和值共同考慮他們是不相等的
null == undefined // true
null === undefined // false
#04 - 以非ASCII字符形式來排序字符串
JavaScript有個(gè)原生的方法對(duì)字符串格式的數(shù)組進(jìn)行排序,做一個(gè)簡單的array.sort()將會(huì)把字符串們按首字母的數(shù)序排列。當(dāng)然,也可以提供自定義排序功能。
['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); // ["Buenos Aires", "Mumbai", "New York", "Shanghai"]
當(dāng)你試圖用非ASCII字符,如 ['é', 'a', 'ú', 'c']這樣的進(jìn)行排序,你會(huì)得到一個(gè)奇怪的結(jié)果['c', 'e', 'á', 'ú'],因?yàn)橹挥杏糜⒄Z的語言才能排序,所以發(fā)生這種情況。
看一個(gè)簡單的例子:
// Spanish ['único','árbol', 'cosas', 'fútbol'].sort(); // ["cosas", "fútbol", "árbol", "único"] // 錯(cuò)誤的排序 // German ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); // ["Wann", "Woche", "wäre", "wöchentlich"] // 錯(cuò)誤的排序
幸運(yùn)的是,有兩種方法來避免這種行為,ECMAScript國際化的API提供了localecompare和and Intl.Collator。
這兩種方法都有自己的自定義參數(shù),以便將其配置來充分的完成功能。
使用 localeCompare()
['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { return a.localeCompare(b); }); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { return a.localeCompare(b); }); // ["Wann", "wäre", "Woche", "wöchentlich"]
使用 intl.collator()
['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); // ["Wann", "wäre", "Woche", "wöchentlich"]
· 每個(gè)方法都可以自定義位置
· 在FF瀏覽器,intl.collator()會(huì)更快,當(dāng)比較的是較大的數(shù)值或字符串
因此,當(dāng)你在用英語以外的語言來賦值給字符串?dāng)?shù)組時(shí),記得要使用此方法來避免意外的排序。
#03 - 改善嵌套條件
我們?cè)鯓硬拍芨纳撇⑶沂笿avaScript中的if語句做出更有效的嵌套。
if (color) { if (color === 'black') { printBlackBackground(); } else if (color === 'red') { printRedBackground(); } else if (color === 'blue') { printBlueBackground(); } else if (color === 'green') { printGreenBackground(); } else { printYellowBackground(); } }
一個(gè)改善的辦法是用switch語句代替嵌套的if語句。雖然它是更加簡潔,更加有序,但不推薦這樣做,因?yàn)楹茈ydebug。這里指出原因。
switch(color) { case 'black': printBlackBackground(); break; case 'red': printRedBackground(); break; case 'blue': printBlueBackground(); break; case 'green': printGreenBackground(); break; default: printYellowBackground(); }
但是,當(dāng)我們有多個(gè)判斷條件的情況下呢?在這種情況下,如果我們想讓它更加簡潔,更加有序,我們可以使用switch。如果我們將true的作為一個(gè)參數(shù)傳遞給該switch語句,它可以讓我們?cè)诿恳粋€(gè)情況下放置一個(gè)條件。
switch(true) { case (typeof color === 'string' && color === 'black'): printBlackBackground(); break; case (typeof color === 'string' && color === 'red'): printRedBackground(); break; case (typeof color === 'string' && color === 'blue'): printBlueBackground(); break; case (typeof color === 'string' && color === 'green'): printGreenBackground(); break; case (typeof color === 'string' && color === 'yellow'): printYellowBackground(); break; }
但我們必須避免在每一個(gè)條件下進(jìn)行多次檢查,盡量避免使用switch。我們也必須考慮到最有效的方法是通過一個(gè)object。
var colorObj = { 'black': printBlackBackground, 'red': printRedBackground, 'blue': printBlueBackground, 'green': printGreenBackground, 'yellow': printYellowBackground }; if (color in colorObj) { colorObj[color](); }
這里有更多相關(guān)的信息。
#02 - ReactJs 子級(jí)構(gòu)造的keys是很重要的
keys是代表你需要傳遞給動(dòng)態(tài)數(shù)組的所有組件的一個(gè)屬性。這是一個(gè)獨(dú)特的和指定的ID,react用它來標(biāo)識(shí)每個(gè)DOM組件以用來知道這是個(gè)不同的組件或者是同一個(gè)組件。使用keys來確保子組件是可保存的并且不是再次創(chuàng)造的,并且防止怪異事情的產(chǎn)生。
· 使用已存在的一個(gè)獨(dú)立的對(duì)象值
· 定義父組件中的鍵,而不是子組件
//不好的 ... render() { <div key={{item.key}}>{{item.name}}</div> } ... //好的 <MyComponent key={{item.key}}/> · 使用數(shù)組不是個(gè)好習(xí)慣 · random()從不會(huì)執(zhí)行 //不好的 <MyComponent key={{Math.random()}}/>
· 你可以創(chuàng)建你的唯一id,請(qǐng)確保該方法是快速的并已經(jīng)附加到對(duì)象上的
· 當(dāng)子級(jí)的數(shù)量是龐大的或包含復(fù)雜的組件,使用keys來提高性能
· 你必須為所有的子級(jí)ReactCSSTransitionGroup提供key屬性
#01 - AngularJs: $digest vs $apply
AngularJs最讓人欣賞的特點(diǎn)是雙向數(shù)據(jù)綁定。為了是它工作,AngularJs評(píng)估模型的變化和視圖的循環(huán)($digest)。你需要了解這個(gè)概念,以便了解框架是如何在引擎中工作的。
Angular評(píng)估每時(shí)的每個(gè)事件的變化。這就是$digest循環(huán)。有時(shí)你必須手動(dòng)運(yùn)行一個(gè)新的循環(huán),你必須有正確的選擇,因?yàn)檫@個(gè)階段是性能方面表現(xiàn)出最具影響力的。
$apply
這個(gè)核心方法讓你來啟動(dòng)一個(gè)$digest循環(huán)。這意味著所以的watch列表中的對(duì)象都將被檢查,整個(gè)應(yīng)用程序啟動(dòng)了$digest循環(huán)。在內(nèi)部,執(zhí)行可選的函數(shù)參數(shù)之后,調(diào)用$rootScope.$digest();
$digest
在這種情況下,$digest方法在當(dāng)前作用域和它的子作用域執(zhí)行,你應(yīng)該注意到,父級(jí)的作用域?qū)⒉槐粰z查,并沒有受到影響。
建議:
· 只在瀏覽器DOM事件在Angular之外被觸發(fā)的時(shí)候使用$apply或者$digest
· 給$apply傳遞函數(shù)表達(dá)式,這有一個(gè)錯(cuò)誤處理機(jī)制,允許在消化周期中整合變化。
$scope.$apply(() => { $scope.tip = 'Javascript Tip'; });
· 如果你僅僅想更新當(dāng)前作用域或者他的子作用域,用$digest,并且防止整個(gè)應(yīng)用程序的$digest。性能不言而喻咯。
· 當(dāng)$apply有很多東西綁定時(shí),這對(duì)機(jī)器來說是個(gè)艱難的過程,可能會(huì)導(dǎo)致性能問題。
· 如果你使用的是Angular 1.2.x以上的,使用$evalAsync。這是一個(gè)在當(dāng)前循環(huán)或下一次循環(huán)的期間或?qū)Ρ磉_(dá)式做出評(píng)估的核心方法,這可以提高你的應(yīng)用程序的性能。
#00 - 在數(shù)組插入一個(gè)項(xiàng)
將一個(gè)項(xiàng)插入到現(xiàn)有數(shù)組中,是一個(gè)日常常見的任務(wù),你可以使用push在數(shù)組的末尾添加元素,使用unshift在開始的位置,或者在中間使用splice。
這些都是已知的方法,但這并不意味著沒有一個(gè)更高性能的途徑。我們來看一看。
在數(shù)組的末尾添加一個(gè)元素很容易與push(),但還有一個(gè)更高性能的途徑。
var arr = [1,2,3,4,5]; arr.push(6); arr[arr.length] = 6; //在Chrome 47.0.2526.106 (Mac OS X 10.11.1)上提高了 43% 的速度
這兩種方法都修改了數(shù)組,不相信我?看這個(gè)jsperf
現(xiàn)在,如果我們正在嘗試將一個(gè)項(xiàng)目添加到數(shù)組的開頭:
var arr = [1,2,3,4,5]; arr.unshift(0); [0].concat(arr); //在Chrome 47.0.2526.106 (Mac OS X 10.11.1)上提高了 98% 的速度
這里更詳細(xì)一點(diǎn):unshift編輯原有的數(shù)組,concat返回一個(gè)新數(shù)組。jsperf
添加在陣列中的物品很容易使用splice,它是做它的最高效的方式。
var items = ['one', 'two', 'three', 'four']; items.splice(items.length / 2, 0, 'hello');
我試著在不同的瀏覽器和操作系統(tǒng)中運(yùn)行這些測試,結(jié)果是相似的。我希望這些建議對(duì)你有用,鼓勵(lì)你自己做測試!
相關(guān)文章
js實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03JavaScript數(shù)組和對(duì)象的復(fù)制
本篇文章主要介紹了JavaScript數(shù)組和對(duì)象的復(fù)制的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-03-03JS中Math對(duì)象使用示例(秒懂如何使用Math對(duì)象)
這篇文章主要給大家介紹了關(guān)于JS中Math對(duì)象使用的相關(guān)資料,Math和其他的對(duì)象不同,它不是一個(gè)構(gòu)造函數(shù),它屬于一個(gè)工具類,不用創(chuàng)建對(duì)象,它里面封裝了數(shù)學(xué)運(yùn)算相關(guān)的屬性和方法,需要的朋友可以參考下2024-06-06微信小程序?qū)崿F(xiàn)購物車代碼實(shí)例詳解
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)購物車代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08js 創(chuàng)建一個(gè)浮動(dòng)div的代碼
js 創(chuàng)建一個(gè)浮動(dòng)div的代碼,一般用來指導(dǎo)用戶下面的操作與多條件選擇。點(diǎn)擊一下就可顯示,具體的大家可以自由發(fā)揮。2009-12-12JavaScript實(shí)現(xiàn)頁面跳轉(zhuǎn)的5種方法總結(jié)
在前臺(tái)開發(fā)中會(huì)涉及頁面跳轉(zhuǎn)的問題,下面這篇文章主要給大家總結(jié)介紹了關(guān)于JavaScript實(shí)現(xiàn)頁面跳轉(zhuǎn)的5種方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12原生js添加節(jié)點(diǎn)appendChild、insertBefore方式
這篇文章主要介紹了原生js添加節(jié)點(diǎn)appendChild、insertBefore方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10