1 給回調函數傳遞參數
在默認情況下,你無法將參數傳給回調函數颅崩,如下:
function callback() {
console.log('Hi human');
}
document.getElementById('someelem').addEventListener('click', callback);
你可以采取JavaScript閉包的優(yōu)點來給回調函數傳參绍刮,案例如下:
function callback(a, b) {
return function() {
console.log('sum = ', (a+b));
}
}
var x = 1, y = 2;
document.getElementById('someelem').addEventListener('click', callback(x, y));
什么是閉包呢?閉包是指一個針對獨立的(自由)變量的函數挨摸。換句話說孩革,閉包中定義的函數會記住它被創(chuàng)建的環(huán)境。了解更多請參閱MDN所以這種方式當被調用的時候得运,參數X/Y存在于回調函數的作用域內膝蜈。
另一種方法是使用綁定方法。例如:
var alertText = function(text) {
alert(text);
};
document.getElementById('someelem').addEventListener('click', alertText.bind(this, 'hello'));
兩種方法在性能上有一些略微區(qū)別熔掺,詳情參閱jsperf
2 使用更簡單的類似indexOf的包含判斷方式
原生的JavaScript沒有contains方法饱搏。對檢查字符串或字符串數組項中是否存在某值,你可以這樣做:
var someText = 'JavaScript rules';
if (someText.indexOf('JavaScript') !== -1) {
}
// 或者
if (someText.indexOf('JavaScript') >= 0) {
}
但是我們再看看這些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'));
問題是~位運算符推沸。"運算符執(zhí)行操作這樣的二進制表達式,但他們返回標準的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" - true
String.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或以上的瀏覽器中被支持课舍。
3 arrow 函數(ES6)
介紹下ES6里的新功能塌西,arrow函數可能會是個很方便的工具,用更少行數寫更多代碼筝尾。他的名字來源于他的語法捡需,=>和小箭頭->比就像一個“胖胖的箭頭”〕镆可能有些人知道栖忠,這種函數類型和其他靜態(tài)語言如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函數可以節(jié)省你輸入括號內參數和返回關鍵字的時間岩齿。建議把圓括號內的參數輸入,如 (x,y) => x+y 苞俘。在不同的使用情況下盹沈,它只是用來應對遺忘的一種方式。但是上面的代碼也會這樣執(zhí)行:x => x*x.目前看來吃谣,這些僅僅是導致更少的LOC和更好的可讀性的句法改進乞封。
this 綁定
還有一個更好的理由使用arrow函數。那就是在會出現this問題的背景下岗憋。使用arrow函數肃晚,你就不用擔心.bind(this)和 that=this 了。因為arrow函數會從上下文中找到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` 實例 (!! 忽略這里)
this.i = 0;
setInterval(function () {
// `this` 指全局對象关串,而不是 CounterA's `this`
// 因此拧廊,開始計數與100,而不是0 (本地的 this.i)
this.i++;
document.getElementById("counterA").innerHTML = this.i;
}, 500);
}
// 手動綁定 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函數
function CounterD() {
this.i = 0;
setInterval(() => {
this.i++;
document.getElementById("counterD").innerHTML = this.i;
}, 500);
}
4 測量一個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
5 ES6中參數處理
在許多編程語言中吧碾,函數的參數是默認的,而開發(fā)人員必須顯式定義一個參數是可選的飞蚓。在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 + b
getSum( 10 ) // throws Error, b is not defined
getSum( undefined, 10 ) // throws Error, a is not defined
_err是立即拋出一個錯誤的函數趴拧。如果沒有一個參數作為值,默認值是會被使用山叮,_err將被調用著榴,將拋出錯誤。你可以在Mozilla開發(fā)者網絡看到的更多默認參數的例子屁倔。
6 提升
理解提升將幫助你組織你的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)
// 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!');
}
// 錯誤: undefined is not a function
functionVar();
var functionVar = function(){
console.log('I did it!');
}
}
為了使事情更容易閱讀布轿,在函數作用域內提升變量的聲明將會讓你明確該變量的聲明是來自哪個作用域哮笆。在你需要使用變量之前定義它們。在作用域底部定義函數汰扭,確保代碼清晰規(guī)范稠肘。
7 檢查一個對象是否有屬性
當你要檢查一個對象是否存在某個屬性時,你可能會這樣做 :
name: '@tips_js'
};
if (myObject.name) { ... }
這是可以的萝毛,但你必須知道這個還有兩原生的方式项阴,in operator 和 object.hasownproperty,每個對象是對象笆包,既可用方法鲁冯。每個object都繼承自Object,這兩個方法都可用色查。
兩個方法的一些不同點:
var myObject = {
name: '@tips_js'
};
myObject.hasOwnProperty('name'); // true
'name' in myObject; // true
myObject.hasOwnProperty('valueOf'); // false, valueOf 是從原型鏈繼承的
'valueOf' in myObject; // true
他們之間的不同在于檢查的性質薯演,換句話說,當該對象本身有查找的屬性時hasOwnProperty返回yrue秧了,然而跨扮,in operator不區(qū)分屬性創(chuàng)建的對象和屬性繼承的原型鏈。
這里有另外一個例子:
var myFunc = function() {
this.name = '@tips_js';
};
myFunc.prototype.age = '10 days';
var user = new myFunc();
user.hasOwnProperty('name'); // true
user.hasOwnProperty('age'); // false, 因為age是原型鏈上的
8 模板字符串
截至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或者簡單邏輯來實現多行字符串。
您還可以使用函數來修改模板字符串的輸出璃氢,它們被稱為模板字符串的標記哟玷。你可能還想讀到更多的理解模板字符串相關信息。
9 將節(jié)點列表轉換為數組
querySelectorAll 方法返回一個和數組類似的節(jié)點列表對象一也。這些數據結構類似數組巢寡,因為經常以數組形式出現,但是又不能用數組的方法椰苟,比如map和foreach抑月。這里是一個快速、安全舆蝴、可重用的方式將一個節(jié)點列表到一個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...