在JS中,這三者都是用來改變函數的this對象的指向的(關於this指向,請參考個人文章JS中 this 到底指向誰?)數組
關於這三者的區別,以下內容:bash
這裏爲何把他們兩個放在一塊兒說呢?
其實對於 call apply 二者而言,做用徹底同樣,只是接受參數的方式不太同樣app
例如:有一個函數定義以下函數
let Test = function(arg1,arg2){ };
複製代碼
能夠經過以下方式來調用:post
Test.call(this,arg1,arg2);
Test.apply(this,[arg1,arg2])
複製代碼
在沒學this以前,一般會有以下的問題:ui
let m = {
call:"Hello...",
invoke:function(){
console.log(this.call);
}
}
let n = m.invoke;
n(); // undefined
複製代碼
這裏咱們想要打印m裏面的call,可是卻打印出來undefined,若是執行 m.invoke() 是能夠的this
let m = {
call:"Hello...",
invoke:function(){
console.log(this.call);
}
}
m.invoke() // Hello...
複製代碼
這裏可以打印出所需的內容,由於這裏的this指向的是函數m,那麼爲何上面的不行這個問題就須要咱們瞭解this的指向問題spa
雖然這種方法能夠達到咱們的目的,可是有時候咱們不得不將這個對象保存到另外的一個變量中,那麼就能夠經過call apply與bind 實現prototype
1.call()code
let m = {
call:"Hello...",
invoke:function(){
console.log(this.call);
}
}
let n = m.invoke;
n.call(m)
複製代碼
經過在call方法,給第一個參數添加要把n添加到哪一個環境中,簡單來講,this就會指向那個對象
call方法除了第一個參數之外還能夠添加多個參數,以下:
let m = {
call:"Hello...",
invoke:function(x,y){
console.log(this.call);
console.log(x+y);
}
}
let n = m.invoke;
n.call(m,1,2);
複製代碼
call的原理
function f1(a, b, c) {
console.log("f1...")
return a + b + c
}
Function.prototype.call = function (context) {
// 處理context可能爲空的狀況
context = context ? Object(context) : window
context.fn = this;
let args = [];
for (let i = 1; i < arguments.length; i++) {
args.push(arguments[i])
}
let r = context.fn(...args)
delete context.fn
return r
}
let obj
console.log(f1.call(obj, 1, 2, 3))
複製代碼
2.apply()
apply方法和call方法有些類似,它也能夠改變this的指向,和上面第一個例子同樣,只需把call換成apply就能夠了
一樣apply也能夠有多個參數,可是不一樣的是,第二個參數必須是一個數組,以下:
let m = {
call:"Hello...",
invoke:function(x,y){
console.log(this.call);
console.log(x+y);
}
}
let n = m.invoke;
// n.call(m,1,2);
n.apply(m,[1,2])
複製代碼
注:若是call()和apply()的第一個參數是null,在非嚴格模式下,第一個參數爲null或者undefined時會自動替換爲指向全局對象(window),
bind方法和call、apply方法有些不一樣,可是他也是能夠改變this指向的,接下來就看看有哪些不一樣之處
let m = {
call:"Hello...",
invoke:function(){
console.log(this.call);
}
}
let n = m.invoke;
n.bind(m);
複製代碼
運行結果發現沒有結果打印,這就是不一樣,實際上bind方法返回的是一個修改事後的函數
let m = {
call:"Hello...",
invoke:function(){
console.log(this.call);
}
}
let n = m.invoke;
let t = n.bind(m);
console.log(t); // 這裏打印的是一個函數
複製代碼
若是執行函數t,那麼能不能打印出對象m裏的call呢?
let m = {
call:"Hello...",
invoke:function(){
console.log(this.call);
}
}
let n = m.invoke;
let t = n.bind(m);
t(); // Hello...
複製代碼
這裏是能夠打印出想要的結果的,一樣bind也能夠有多個參數,而且參數能夠執行的時候再次添加,可是要注意的是,參數是按照形參的順序進行的
let m = {
call:"Hello...",
invoke:function(x,y,z){
console.log(this.call);
console.log(x,y,z); //1,2,3
}
}
let n = m.invoke;
let t = n.bind(m,1);
t(2,3);
複製代碼
bind原理
function f(name, age, n, a) {
console.log(name, age, n, a)
console.log(this)
}
let obj = { name: "wangcai" }
Function.prototype.bind = function (context) {
let that = this;
let newArr = Array.prototype.slice.call(arguments, 1);
return function () {
let newArr2 = Array.prototype.slice.call(arguments)
return that.apply(context, newArr.concat(newArr2))
}
}
let newF = f.bind(obj, "hello", "world", "lalala")
newF("xxxx")
複製代碼
總結:
apply 、 call 、bind 三者都是用來改變函數的this對象的指向的
apply 、 call 、bind 三者第一個參數都是this要指向的對象,也就是想指定的上下文;
apply 、 call 、bind 三者均可以利用後續參數傳參;
bind是返回對應函數,便於後面調用;apply、call則是當即調用