寫作意圖
這篇文章用於總結一些javascript語言中常見的易混淆點。
call | apply | bind
在js中,最詭異莫測的莫過於this了,理解的不夠深入或是應用場景略微複雜,使用時就會出現各種意想不到的錯誤。所以,在很多時候,我們需要手動指定上下文環境,來修正this的指向。
最簡單判斷this所在環境的方法是,尋找this的實際呼叫者。
一個典型的錯誤
var a = {
name: 'ein',
sayName: function () {
console.log(this.name);
}
}
var b = a.sayName;
b(); //undefined
複製程式碼
我們本想將物件a的sayName賦值給變數b,通過呼叫來檢視a的name是什麼?結果卻輸出了undefined。發生這個異常的原因就是因為在呼叫函式b時,sayName中的this已經不再指向物件a而是指向了全域性物件window,由於window下並沒有name屬性,所以輸出undefined。
下面我們將通過以上三種方法來修正這個問題。
使用call
var b = a.sayName;
b.call(a); //ein
複製程式碼
使用call方法,第一個引數為函式呼叫時的上下文環境,將其設定為物件a,這樣this就會指向物件a,如此便可以取到物件a中的name屬性,輸出想要的值。
使用apply
var b = a.sayName;
b.apply(a); //ein
複製程式碼
使用apply方法,傳入上下文環境,可以實現同樣的效果。
call和apply的區別
那麼call和apply的區別是什麼呢?他們的區別在於後續的引數。讓我們改造一下上面的程式碼,來觀察效果。
var a = {
name: 'ein',
sayName: function (fistname, lastname) {
console.log(`${fistname} ${this.name} ${lastname}`);
}
}
var b = a.sayName;
b.call(a,'nick','snow'); //nick ein snow
b.apply(a,['nick','snow']); //nick ein snow
複製程式碼
call和apply的區別在於後續引數,call依次傳入後續引數,將被函式所使用。apply需要將後續引數以陣列的形式傳入。
使用bind
bind同樣可以用來修正this的指向,它與以上二者的區別在於,bind繫結之後並不會立即執行函式。
var b = a.sayName;
b.bind(a);
複製程式碼
在為b繫結a的上下文環境之後,b並不會立即執行。
b.bind(a,'nick','snow')(); //nick ein snow
var c = b.bind(a);
c('nick','snow'); //nick ein snow
複製程式碼
如此,便可以如願得到你想要的效果了。使用bind方法可以延緩函式的執行時間,在你想呼叫時再執行函式。
bind方法同樣可以傳入其它引數,和call方法的傳入方式相同。
splice | slice | split
看到這三個方法有沒有頭暈目眩的感覺?因為它們三個實在是太像三胞胎了,真的很難區分。首先,我們來學習或是回顧一下這三個單詞。
splice 拼接
slice 片,切片
split 分裂,裂開
複製程式碼
大多數api其實其名稱都與其用途有所關聯,這三個api便是很經典的案例。
- splice意為拼接,它的用途,便是將一個陣列分割開,並且可以再以指定的方式重新拼接在一起。
- slice意為切片,我們可以使用它來在一個陣列或是字串中切取我們想要的一段。
- split意為分裂,它可以將一個字串分裂成一個陣列。
使用splice
var a = [1,2,3,4,5];
a.splice(1,2,4,4);
console.log(a);
//[1,4,4,4,5]
複製程式碼
splice(starts, count, item1, ..., itemx)方法接受多個引數,第一個引數為刪除的起始元素,第二個引數為刪除數量,後續為插入的內容。
注意: splice方法會修改原始陣列,返回被刪除的內容陣列。
使用slice
var a = [1,2,3,4,5];
var b = '12345';
a.slice(1,3); //[2,3]
a.slice(1); //[2,3,4,5]
a.slice(2,-1) //[3,4]
b.slice(1,2); //'2'
console.log(a); //[1,2,3,4,5]
console.log(b); //'12345'
複製程式碼
- slice方法既可應用於陣列也可應用於字串。
- slice(stats, beforeEnds)方法接受最多兩個引數,第一個引數代表的序列號必須小於第二個引數,第二個引數為切片的終止位置,第二個引數省略時預設擷取到資料末尾,引數為負數時,將反向查詢匹配項。
注意: slice方法不會修改原始陣列,返回的是被切片節選的片段。
使用split
var a = '123456';
a.split(''); //['1','2','3','4','5','6']
a.split('',3); //['1','2','3']
console.log(a); //'123456'
複製程式碼
split(separator, count)方法可接受兩個引數,第一個引數為分割符,用於指定字串的分割規則,第二個引數為返回陣列的最大長度,返回的輸出長度不會大於這個引數。
注意: split不會修改原始字串,返回值為新陣列。
map | forEach | reduce | filter | every | some
這六個方法時常用的運算元組的api,均為Array.prototype的本地方法。所以一切陣列均可使用這些方法遍歷運算元組的每一項,下面將逐一介紹這些方法。
map
var arr = [
{'name': 'ein'},
{'name': 'zwei'},
{'name': 'drei'}
];
let newarr = arr.map((item, index) => {
return (
{
'name': item.name,
'order': index
}
)
});
console.log(arr);
// [{'name': 'ein'},{'name': 'zwei'},{'name': 'drei'}]
console.log(newarr);
// [{'name': 'ein','order': 0},{'name': 'zwei','order': 1},{'name': 'drei','order': 2}]
複製程式碼
map(fn(item, index))方法接收一個函式作為引數,這個函式接收兩個引數,第一個引數為每一項的陣列內容,第二個引數為陣列下標。
注意: map方法返回一個新的陣列,如果在操作中沒有return返回值,預設返回一個值為undefined的陣列。
預設返回:[undefined, undefined, undefined]
forEach
arr.forEach((item, index) => {
item.old = true;
delete item.name;
})
console.log(arr);
// [{'old': true},{'old': true},{'old': true}]
複製程式碼
forEach(fn(item, index))方法接收一個函式作為引數,這個函式接收兩個引數,第一個引數為每一項的陣列內容,第二個引數為陣列下標。
注意: forEach方法直接操作原始陣列,並且不返回任何內容。
reduce
簡單用例
var arr1 = [1,2,3,4,5];
var res = arr1.reduce((curr, next) => {
return curr + next
});
console.log(res); //15
console.log(arr1); //[1, 2, 3, 4, 5]
複製程式碼
複雜用例
var res1 = arr1.reduce((curr, next,index,arr) => {
console.log('content',curr,next,index,arr);
return curr + next
},10);
console.log(res1);
複製程式碼
content | 初始值(curr) | 當前元素(next) | 當前元素索引(index) | 當前元素所屬陣列(arr) | 函式初始值 |
---|---|---|---|---|---|
content | 10 | 1 | 0 | [1, 2, 3, 4, 5] | 10 |
content | 11 | 2 | 1 | [1, 2, 3, 4, 5] | 10 |
content | 13 | 3 | 2 | [1, 2, 3, 4, 5] | 10 |
content | 16 | 4 | 3 | [1, 2, 3, 4, 5] | 10 |
content | 20 | 5 | 4 | [1, 2, 3, 4, 5] | 10 |
reduce方法用於對陣列進行累積化操作,常用於陣列求和。接收兩個引數,第一個引數為操作函式,第二個引數為函式初始值。對於陣列的操作不會修改原始值。
filter
var res = arr1.filter((item, index) => {
console.log('data:',index,item);
return item > 3
});
console.log(res);
// [4, 5]
複製程式碼
filter方法用於過濾陣列的每一項,刪選出符合條件的項,並組成一個新的陣列。
every
var res = arr1.every((item, index) => {
return item > 3
});
console.log(res);
// false
複製程式碼
every方法用於檢查陣列的每一項是否符合條件,全部符合條件時返回true,否則返回false。
some
var res = arr1.some((item, index) => {
return item > 3
});
console.log(res);
// true
複製程式碼
some方法用於檢查陣列中的是否存在符合條件的項,存在則返回true,否則返回false。
另外
一副生動有趣的圖解,everyday will be better!