前言:讀者在看這篇文章的時候,你必須弄懂做用域以及JavaScript中this的做用和運用場景。
爲何會有call和apply? call和apply兩個方法的做用基本相同,它們都是爲了改變某個函數執行時的上下文(context)而創建的, 他的真正強大之處就是可以擴充函數賴以運行的做用域。通俗一點講,就是改變函數體內部this 的指向。javascript
window.color = "red";
var o = {color: "blue"};
function sayColor(){
alert(this.color);
}
sayColor();//red
sayColor.call(this);//red,把函數體sayColor內部的this,綁到當前環境(做用域)(這段代碼所處的環境)
sayColor.call(window);//red,把函數體sayColor內部的this,綁到window(全局做用域)
sayColor.call(o);//blue複製代碼
解釋:上面的栗子,很明顯函數sayColor是在全局做用域(環境/window)中調用的,而全局做用域中有一個color屬性,值爲"red",sayColor.call(this)這一行代碼就是表示 把函數體sayColor內部的this,綁到當前環境(做用域),而sayColor.call(window)這一行代碼就是表示 把函數體sayColor內部的this,綁到window(全局做用域),之因此這兩行的輸出都是"red"就是由於他當前做用域的this就是window(this === window); 最後,sayColor.call(o)這一行代碼就表示 把函數體sayColor內部的this,綁到o這個對象的執行環境(上下文)中來,也就是說sayColor內部的this——> o
window.color = "red";
var o = {color: "blue"};
function sayColor(){
alert(this.color);
}
sayColor.call(this);//red
sayColor.call(window);//red
sayColor.call();
sayColor.call(null);
sayColor.call(undefined);
sayColor.call(o);//blue複製代碼
注意:若是call方法沒有參數,或者參數爲 null或undefined,則等同於指向 全局對象。
var arr = [];
Object.prototype.toString.call(arr); // [object Array]
//把函數體Object.prototype.toString()方法內部的this,綁到arr的執行環境(做用域)複製代碼
這是由於toString()爲Object的原型方法,而Array ,function等引用類型做爲Object的實例,都重寫了toString方法。不一樣的對象類型調用toString方法時,根據原型鏈的知識,調用的是對應的 重寫以後的toString方法(function類型返回內容爲函數體的字符串,Array類型返回元素組成的字符串.....),而不會去調用Object上原型toString方法,因此採用arr.toString()不能獲得其對象類型,只能將arr轉換爲字符串類型;所以,在想要獲得對象的具體類型時,應該調用Object上原型toString方法。 參考: developer.mozilla.org/zh-CN/docs/…()_to_detect_object_class
var foo = {
count: 1
};
function bar() {
console.log(this.count);
}
bar.myCall(foo); // 1
--------------------------------------------------------------------
Function.prototype.myCall = function(context) {
// 取得傳入的對象(執行上下文),好比上文的foo對象,這裏的context就至關於上文的foo
// 不傳第一個參數,默認是window,
var context = context || window;
// 給context添加一個屬性,這時的this指向調用myCall的函數,好比上文的bar函數
context.fn = this;//這裏的context.fn就至關於上文的bar函數
// 經過展開運算符和解構賦值取出context後面的參數,上文的例子沒有傳入參數列表
var args = [...arguments].slice(1);
// 執行函數(至關於上文的bar(...args))
var result = context.fn(...args);
// 刪除函數
delete context.fn;
return result;
};複製代碼
很明顯,咱們看標題的能夠知道call和apply的一個區別了,它們兩個惟一的區別就是傳參列表的不一樣,apply是接收的參數是一個數組。
java
var foo = {
count: 1
};
function bar() {
console.log(this.count);
}
bar.myApply(foo); // 1
--------------------------------------------------------------------
Function.prototype.myApply = function(context) {
var context = context || window;
context.fn = this;
var result;
// 判斷第二個參數是否存在,也就是context後面有沒有一個數組
// 若是存在,則須要展開第二個參數
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn;
return result;
}複製代碼
var a = [10, 2, 4, 15, 9];
Math.max.apply(Math, a);//15
Math.min.apply(null, a);//2複製代碼
2. 能夠將一個相似(僞)數組的對象(好比arguments對象)轉爲真正的數組。 **前提:**被處理的對象必須有length屬性,以及相對應的數字鍵。
git
//接收的是對象,返回的是數組
Array.prototype.slice.apply({0: 1, length: 1}) // [1]
Array.prototype.slice.apply({0: 1}) // []
Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
Array.prototype.slice.apply({length: 1}) // [undefined]
//(切下)[].slice(1, n),返回索引爲1到索引爲n-1的數組複製代碼
3. 數組追加
github
var arr1 = [1,2,3];
var arr2 = [4,5,6];
[].push.apply(arr1, arr2);
console.log(arr1);//[1, 2, 3, 4, 5, 6]
console.log(arr2);//[4, 5, 6]複製代碼
4. 數組合並
數組
var arr1 = [1, 2, { id: 1, id: 2 }, [1, 2]];
var arr2 = ['ds', 1, 9, { name: 'jack' }];
// var arr = arr1.concat(arr2);//簡單作法
Array.prototype.push.apply(arr1,arr2)
console.log(arr1);複製代碼
developer.mozilla.org/zh-CN/docs/…app
call和apply它們兩個是改變this的指向以後當即調用該函數,而bind則不一樣,它是建立一個新函數,咱們必須手動去調用它。 MDN說法:bind()方法建立一個新的函數,在調用時設置this關鍵字爲提供的值。並在調用新函數時,將給定參數列表做爲原函數的參數序列的前若干項。(雖然這句話我還不太懂)函數
bind()是ES5新增的一個方法ui
傳參和call或apply相似this
不會執行對應的函數,call或apply會自動執行對應的函數spa
bind會返回對函數的引用
var a ={
name : "Cherry",
fn : function (a,b) {
console.log( a + b)
}
}
var b = a.fn;
b.call(a,1,2);//當即調用該函數
b.bind(a,1,2)();//手動調用(),它返回一個原函數的拷貝(新的,不是原函數),並擁有指定的this值和初始參數。複製代碼
var obj = {
name:'JuctTr',
age: 18
};
/** * 給document添加click事件監聽,並綁定ExecuteFun函數 * 經過bind方法設置ExecuteFun的this爲obj */
//document.addEventListener('click',ExecuteFun.call(obj),false);
document.addEventListener('click',ExecuteFun.bind(obj),false);
function ExecuteFun(a,b){
console.log(this.name, this.age);
}複製代碼
若是把bind換成call或apply,頁面控制檯會直接輸出JuctTr 18,由於call和apply是改變this的指向以後當即執行ExecuteFun函數,而bind它是返回一個新的函數
Function.prototype.myBind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
var _this = this
var args = [...arguments].slice(1)
// 返回一個函數
return function F() {
// 由於返回了一個函數,咱們能夠 new F(),因此須要判斷
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}複製代碼
更多文章請轉移:github.com/wangyicong