標籤:string 箭頭函數 項目 寫入 nes 注意 script att red
原文: https://www.cnblogs.com/ys-ys/p/5158510.html
--------------------------------------------------------------------------------
感謝好友破狼提供的這篇好文章,也感謝寫這些知識點的作者們和將他們整理到一起的作者。這是github上的一篇文章,在這裡本獸也就只做翻譯,由於本獸英語水平和編程能力都不咋地,如有不好的地方也請多理解體諒。戳這裡:原文
能夠為大家提供這些簡短而實用的JavaScript技巧來提高大家編程能力,這對於我來說是件很開心的事。每天僅花上不到2分鐘的時間中,你將可以讀遍JavaScript這門可怕的語言所呈現給我們的特性:performance(效能), conventions(協議), hacks(代碼hack), interview questions(面試問題)及所有其他的東西。
第二篇地址:( 譯、持續更新 ) JavaScript 上分小技巧(二)
第三篇地址:( 譯、持續更新 ) JavaScript 上分小技巧(三)
第四篇地址:( 譯、持續更新 ) JavaScript 上分小技巧(四)
#15 - 使用更簡單的類似indexOf的包含判斷方式
原生的JavaScript沒有contains方法。對檢查字串或字串數組項中是否存在某值,你可以這樣做:
var someText = ‘JavaScript rules‘;if (someText.indexOf(‘JavaScript‘) !== -1) {}// 或者if (someText.indexOf(‘JavaScript‘) >= 0) {}
但是我們再看看這些ExpressJs程式碼片段。
// examples/mvc/lib/boot.jsfor (var key in obj) { // "reserved" exports if (~[‘name‘, ‘prefix‘, ‘engine‘, ‘before‘].indexOf(key)) continue;// examples/lib/utils.jsexports.normalizeType = function(type){ return ~type.indexOf(‘/‘) ? acceptParams(type) : { value: mime.lookup(type), params: {} };};// examples/web-service/index.js// key is invalidif (!~apiKeys.indexOf(key)) return next(error(401, ‘invalid api key‘));
問題是~位元運算符。"運算子執行操作這樣的二進位運算式,但他們返回標準的JavaScript的數值."
他們將-1轉換為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" - trueString.prototype.includes()
在ES6(ES 2015)中介紹了includes()方法可以用來確定是否一個字串包含另一個字串:
‘something‘.includes(‘thing‘); // true
在ECMAScript 2016 (ES7)中,甚至數組都可以這樣操作,如indexOf:
!!~[1, 2, 3].indexOf(1); // true[1, 2, 3].includes(1); // true
不幸的是,這隻是在Chrome,Firefox,Safari 9或以上的瀏覽器中被支援。
#14 - arrow 函數(ES6)
介紹下ES6裡的新功能,arrow函數可能會是個很方便的工具,用更少行數寫更多代碼。他的名字來源於他的文法,=>和小箭頭->比就像一個“胖胖的箭頭”。可能有些人知道,這種函數類型和其他靜態語言如lambda運算式的匿名函數。它被稱為匿名,因為這些箭頭函數沒有一個描述性的函數名。
那麼這樣有什麼好處呢?
文法:更少的LOC,不用一次次的鍵入函數關鍵字。
語義:從上下文中捕捉關鍵字this。
簡單文法案例:
看看下面的兩段程式碼片段,他們做的是一樣的工作。你能很快的理解arrow函數的功能。
// arrow函數的日常文法param => expression// 可能也會寫在括弧中// 括弧是多參數要求(param1 [, param2]) => expression// 使用日常函數var arr = [5,3,2,9,1];var arrFunc = arr.map(function(x) { return x * x;});console.log(arr)// 使用arrow函數var arr = [5,3,2,9,1];var arrFunc = arr.map((x) => x*x);console.log(arr)
正如你所看到的,這個例子中的arrow函數可以節省你輸入括弧內參數和返回關鍵字的時間。建議把圓括弧內的參數輸入,如 (x,y) => x+y 。在不同的使用方式下,它只是
用來應對遺忘的一種方式。但是上面的代碼也會這樣執行:x => x*x.目前看來,這些僅僅是導致更少的LOC和更好的可讀性的句法改進。
this 綁定
還有一個更好的理由使用arrow函數。那就是在會出現this問題的背景下。使用arrow函數,你就不用擔心.bind(this)和 that=this 了。因為arrow函數會從上下文中找到this。
看下面的例子:
// 全域定義this.ithis.i = 100;var counterA = new CounterA();var counterB = new CounterB();var counterC = new CounterC();var counterD = new CounterD();// 不好的樣本function CounterA() { // CounterA‘s `this` 執行個體 (!! 忽略這裡) this.i = 0; setInterval(function () { // `this` 指全域對象,而不是 CounterA‘s `this` // 因此,開始計數與100,而不是0 (本地的 this.i) this.i++; document.getElementById("counterA").innerHTML = this.i; }, 500);}// 手動綁定 that = thisfunction 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函數function CounterD() { this.i = 0; setInterval(() => { this.i++; document.getElementById("counterD").innerHTML = this.i; }, 500);}
關於arrow函數的進一步資訊可以看這裡 。查看不同的文法選請訪問該網站。
#13 - 測量一個JavaScript代碼塊效能的技巧
快速測量一個JavaScript塊的效能,我們可以使用控制台的功能像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 (在瀏覽器控制台輸出)
#12 - ES6中參數處理
在許多程式設計語言中,函數的參數是預設的,而開發人員必須顯式定義一個參數是可選的。在JavaScript中的每個參數是可選的,但我們可以這一行為而不讓一個函數利用ES6的預設值作為參數。
const _err = function( message ){ throw new Error( message );}const getSum = (a = _err(‘a is not defined‘), b = _err(‘b is not defined‘)) => a + bgetSum( 10 ) // throws Error, b is not definedgetSum( undefined, 10 ) // throws Error, a is not defined
_err是立即拋出一個錯誤的函數。如果沒有一個參數作為值,預設值是會被使用,_err將被調用,將拋出錯誤。你可以在Mozilla開發人員網路看到的更多預設參數的例子。
#11 - 提升
理解提升將協助你組織你的function。只需要記住,變數聲明和定義函數會被提升到頂部。變數的定義是不會的,即使你在同一行中聲明和定義一個變數。此外,變數聲明讓系統知道變數存在,而定義是將其賦值給它。
function doTheThing() { // 錯誤: notDeclared is not defined console.log(notDeclared); // 輸出: undefined console.log(definedLater); var definedLater; definedLater = ‘I am defined!‘ // 輸出: ‘I am defined!‘ console.log(definedLater) // 輸出: 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!‘); } // 錯誤: undefined is not a function functionVar(); var functionVar = function(){ console.log(‘I did it!‘); }}
為了使事情更容易閱讀,在函數範圍內提升變數的聲明將會讓你明確該變數的聲明是來自哪個範圍。在你需要使用變數之前定義它們。在範圍底部定義函數,確保代碼清晰規範。
#10 - 檢查一個對象是否有屬性
當你要檢查一個對象是否存在某個屬性時,你可能會這樣做 :
var myObject = { name: ‘@tips_js‘};if (myObject.name) { ... }
這是可以的,但你必須知道這個還有兩原生的方式,in operator 和 object.hasownproperty,每個對象是對象,既可用方法。每個object都繼承自Object,這兩個方法都可用。
兩個方法的一些不同點:
var myObject = { name: ‘@tips_js‘};myObject.hasOwnProperty(‘name‘); // true‘name‘ in myObject; // truemyObject.hasOwnProperty(‘valueOf‘); // false, valueOf 是從原型鏈繼承的‘valueOf‘ in myObject; // true
他們之間的不同在於檢查的性質,換句話說,當該對象本身有尋找的屬性時hasOwnProperty返回true,然而,in operator不區分屬性建立的對象和屬性繼承的原型鏈。
這裡有另外一個例子:
var myFunc = function() { this.name = ‘@tips_js‘;};myFunc.prototype.age = ‘10 days‘;var user = new myFunc();user.hasOwnProperty(‘name‘); // trueuser.hasOwnProperty(‘age‘); // false, 因為age是原型鏈上的
點擊看例子。同時,建議在檢查對象的屬性存在時,閱讀這些有關的常見錯誤。
#09 - 模板字串
截至ES6,JS已經有模板字串作為替代經典的字串引用。
案例:一般字元串
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或者簡單邏輯來實現多行字串。
您還可以使用函數來修改模板字串的輸出,它們被稱為模板字串的標記。你可能還想讀到更多的理解模板字串相關資訊。
#08 - 將節點列錶轉換為數組
querySelectorAll 方法返回一個和數組類似的節點列表對象。這些資料結構類似數組,因為經常以數組形式出現,但是又不能用數組的方法,比如map和foreach。這裡有一個快速、安全、可重用的方式將一個節點列表到一個DOM元素數組:
const nodelist = document.querySelectorAll(‘div‘);const nodelistToArray = Array.apply(null, nodelist);//later on ..nodelistToArray.forEach(...);nodelistToArray.map(...);nodelistToArray.slice(...);//etc...
apply方法是將一系列數組格式的參數傳遞給一個給定this的函數。MDN指出,apply將會調用類似數組的對象,而這正是querySelectorAll所返回的。因為我們不需要在函數的上下文中指定this,所以我們傳入null或0。返回的結果是一組能使用數組方法的DOM元素數組。
如果你使用的是es2015可以利用...(spread operator)
const nodelist = [...document.querySelectorAll(‘div‘)]; // 返回的是個真實的數組//later on ..nodelist.forEach(...);nodelist.map(...);nodelist.slice(...);//etc...
#07 - "use strict" 和"偷懶"
strict 模式讓開發人員更加安全的編寫JavaScript。
預設情況下,JavaScript允許開發人員偷懶,例如,我們在第一次聲明變數的時候可以不用var,雖然這可能看起來像一個沒有經驗的開發人員,同時這也是很多錯誤的根源,變數名拼字錯誤或意外地將它提到了外部範圍。
程式員喜歡讓電腦為我們做些無聊的事,如檢查一些我們工作的錯誤。"use strict"指令會協助我們做這些錯誤檢查,將我們的錯誤轉換成JavaScript錯誤。
我們把這個指令可以通過添加在一個js檔案的頂部:
// 整個script檔案都將是strict 模式文法"use strict";var v = "Hi! I‘m a strict mode script!";或者在函數內:function f(){// 函數範圍內的strict 模式文法 ‘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."; }
在包含這個指令的JavaScript檔案或者函數內,我們將一些較大的JavaScript項目中的不良行為直接在JavaScript引擎執行中禁止了。在其他情況中,strict 模式改變以下的行為:
· 變數只有在前面 var 聲明了才能用
· 試圖寫入唯讀屬性產生的誤差
· 必須用 new 關鍵字調用建構函式
· this 不會預設指向全域對象
· 非常有限的使用eval()
· 保護保留字元或未來保留字元不被作為變數名使用
strict 模式在新項目中是很有好處的,但是在大多數地方沒使用到它的老項目裡使用它是非常具有挑戰性的。當你把多個檔案合并到一個檔案時,它也是個問題,就像可能導致整個檔案都在strict 模式下執行。
它不是一個聲明,只是一個字面量,早期版本的瀏覽器會忽略它。strict 模式支援:
· IE 10+
· FF 4+
· Chrome 13+
· Safari 5.1+
· Opera 12+
參閱MDN對於strict 模式的描述。
#06 - 處理一個數組或單個元素作為參數的方法
相比於寫個單獨的方法去分別操作一個數組和一個元素作為參數的函數,更好的是寫一個通用的函數,這樣就都可以操作。這類似於一些jQuery的方法(css匹配將修改所有的選取器)。
你僅需要先將一切放進數組,Array.concat會接收數組或單一的對象:
function printUpperCase(words) { var elements = [].concat(words); for (var i = 0; i < elements.length; i++) { console.log(elements[i].toUpperCase()); }}
printUpperCase現在可以接收無論單一的元素作為參數還是一個數組:
printUpperCase("cactus");// => CACTUSprintUpperCase(["cactus", "bear", "potato"]);// => CACTUS// BEAR// POTATO
#05 - undefined 和 null 的不同
· undefined指的是一個變數未被聲明,或者一個變數被聲明但未賦值
· null是指一個特定的值,即"沒有值"
. JavaScript給未賦值的變數預設定義為undefined
· JavaScript不會給未賦值的變數設定null值,它被程式員用來表示一個無價值的值
· undefined在json格式資料中是無效的,而null有效
· undefined 類型是 undefined
· null類似是object.為什麼呢?
· 兩者都是原始值
· 兩者都被認為false(Boolean(undefined) // false, Boolean(null) // false)。
· 辨認變數是不是undefined
typeof variable === "undefined"
· 檢查變數是不是null
variable === "null"
從值考慮他們是相等的,但是從類型和值共同考慮他們是不相等的
null == undefined // truenull === undefined // false
#04 - 以非ASCII字元形式來排序字串
JavaScript有個原生的方法對字串格式的數組進行排序,做一個簡單的array.sort()將會把字串們按首字母的數序排列。當然,也可以提供自訂排序功能。
[‘Shanghai‘, ‘New York‘, ‘Mumbai‘, ‘Buenos Aires‘].sort();// ["Buenos Aires", "Mumbai", "New York", "Shanghai"]
當你試圖用非ASCII字元,如 [‘é‘, ‘a‘, ‘ú‘, ‘c‘]這樣的進行排序,你會得到一個奇怪的結果[‘c‘, ‘e‘, ‘á‘, ‘ú‘],因為只有用英語的語言才能排序,所以發生這種情況。
看一個簡單的例子:
// Spanish[‘único‘,‘árbol‘, ‘cosas‘, ‘fútbol‘].sort();// ["cosas", "fútbol", "árbol", "único"] // 錯誤的排序// German[‘Woche‘, ‘w?chentlich‘, ‘w?re‘, ‘Wann‘].sort();// ["Wann", "Woche", "w?re", "w?chentlich"] // 錯誤的排序
幸運的是,有兩種方法來避免這種行為,國際化的ECMAScript API提供了localecompare和and Intl.Collator。
這兩種方法都有自己的自訂參數,以便將其配置來充分的完成功能。
使用 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"]
· 每個方法都可以自訂位置
· 在FF瀏覽器,intl.collator()會更快,當比較的是較大的數值或字串
因此,當你在用英語以外的語言來賦值給字串數組時,記得要使用此方法來避免意外的排序。
#03 - 改善嵌套條件
我們怎樣才能改善並且使JavaScript中的if語句做出更有效嵌套。
if (color) { if (color === ‘black‘) { printBlackBackground(); } else if (color === ‘red‘) { printRedBackground(); } else if (color === ‘blue‘) { printBlueBackground(); } else if (color === ‘green‘) { printGreenBackground(); } else { printYellowBackground(); }}
一個改善的辦法是用switch語句代替嵌套的if語句。雖然代碼是更加簡潔有序,但不推薦這樣做,因為很難debug。這裡指出原因。
switch(color) { case ‘black‘: printBlackBackground(); break; case ‘red‘: printRedBackground(); break; case ‘blue‘: printBlueBackground(); break; case ‘green‘: printGreenBackground(); break; default: printYellowBackground();}
但是,當我們有多個判斷條件的情況下呢?在這種情況下,如果我們想讓代碼更加簡潔有序,我們可以使用switch。如果我們將true的作為一個參數傳遞給該switch語句,它可以讓我們在每一個情況下放置一個條件。
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;}
但我們必須避免在每一個條件下進行多次檢查,盡量避免使用switch。我們也必須考慮到最有效方法是通過一個object。
var colorObj = { ‘black‘: printBlackBackground, ‘red‘: printRedBackground, ‘blue‘: printBlueBackground, ‘green‘: printGreenBackground, ‘yellow‘: printYellowBackground};if (color in colorObj) { colorObj[color]();}
這裡有更多相關的資訊。
#02 - ReactJs 子級構造的keys是很重要的
keys是代表你需要傳遞給動態數組的所有組件的一個屬性。這是一個獨特的和指定的ID,react用它來標識每個DOM組件以用來知道這是個不同的組件或者是同一個組件。使用keys來確保子組件是可儲存的並且不是再次創造的,並且防止怪異事情的產生。
· 使用已存在的一個獨立的對象值
· 定義父組件中的鍵,而不是子組件
//不好的...render() { <div key={{item.key}}>{{item.name}}</div>}...//好的<MyComponent key={{item.key}}/>
· 使用數組不是個好習慣
· random()從不會執行
//不好的<MyComponent key={{Math.random()}}/>
· 你可以建立你的唯一id,請確保該方法是快速的並已經附加到對象上的
· 當子級的數量是龐大的或包含複雜的組件,使用keys來提高效能
· 你必須為所有的子級ReactCSSTransitionGroup提供key屬性
#01 - AngularJs: $digest vs $apply
AngularJs最讓人欣賞的特點是雙向資料繫結。為了是它工作,AngularJs評估模型的變化和視圖的迴圈($digest)。你需要瞭解這個概念,以便瞭解架構是如何在引擎中工作的。
Angular評估每時的每個事件的變化。這就是$digest迴圈。有時你必須手動觸發一次新的迴圈,你必須有正確的選擇,因為這個階段是效能方面表現出最具影響力的。
$apply
這個核心方法讓你來啟動一次$digest迴圈。這意味著所有的watch列表中的對象都將被檢查,整個應用程式啟動了$digest迴圈。在內部,執行可選的函數參數之後,調用$rootScope.$digest();
$digest
在這種情況下,$digest方法在當前範圍和它的子範圍執行,你應該注意到,父級的範圍將不被檢查,並沒有受到影響。
建議:
· 只在瀏覽器DOM事件在Angular之外被觸發的時候使用$apply或者$digest
· 給$apply傳遞函數運算式,這存在一個錯誤處理機制:允許在digest周期中整合變化。
$scope.$apply(() => { $scope.tip = ‘Javascript Tip‘;});
· 如果你僅僅想更新當前範圍或者他的子範圍,用$digest,並且防止整個應用程式的$digest。效能不言而喻咯。
· 當$apply有很多東西綁定時,這對機器來說是個艱難的過程,可能會導致效能問題。
· 如果你使用的是Angular 1.2.x以上的,使用$evalAsync。這是一個在當前迴圈或下一次迴圈的期間或對錶達式做出評估的核心方法,這可以提高你的應用程式的效能。
#00 - 在數組插入一個項
將一個元素插入到現有數組中,是一個常見的任務,你可以使用push在數組的末尾添加元素,使用unshift在開始的位置,或者使用splice。
這些都是已知的方法,但這並不意味著沒有更高效能的實現方式。
在數組的末尾添加一個元素很容易與push(),但還有一個更高效能的途徑。
var arr = [1,2,3,4,5];arr.push(6); //在Chrome (window 7)上提高了50%左右的速度arr[arr.length] = 6; //在Chrome 47.0.2526.106 (Mac OS X 10.11.1)上提高了 43% 的速度
這兩種方法都修改了數組,不相信?看這個jsperf
現在,如果我們正在嘗試將一個項目添加到數組的開頭:
var arr = [1,2,3,4,5];arr.unshift(0); //在Chrome (window 7)上提高了15%左右的速度[0].concat(arr); //在Chrome 47.0.2526.106 (Mac OS X 10.11.1)上提高了 98% 的速度
這裡更詳細一點:unshift編輯原有的數組,concat返回一個新數組。jsperf
添加在陣列中的物品很容易使用splice,它是做它的最高效的方式。
var items = [‘one‘, ‘two‘, ‘three‘, ‘four‘];items.splice(items.length / 2, 0, ‘hello‘);
### 2016-01-25 更新 ###
未完待續...(該文章知識點如在github有更新,這邊每隔一小段時間也會做相對應的更新,原諒本獸時間不是那麼充足)
本篇文章也會在號"shuang_lang_shuo"發表,同時大家也多支援破狼的處女作新書《AngularJS深度剖析與最佳實務》。
【轉】javascript 中的很多有用的東西