JS中改变this的指向 call、apply 和 bind 的区别

JS中 call、apply 和 bind

在JS中,这三者都是用来改变函数的this对象的指向的(关于this指向,请参考个人文章JS中 this 到底指向谁?)数组

关于这三者的区别,以下内容:bash

call() 与 apply()

这里为何把他们两个放在一块儿说呢?
其实对于 call apply 二者而言,做用彻底同样,只是接受参数的方式不太同样
app

  • call()接收的是参数列表
  • apply()接收参数数组

例如:有一个函数定义以下函数

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()

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则是当即调用


^_<

相关文章
相关标签/搜索