在日常写代码时,我们有时候要更改函数中this的指向,所以我们会经常用到call、apply、bind三个方法,那么他们都什么共同之处,又有什么区别呢?下面我们来总结一下吧!
在总结之前呢,我们先来定义一个对象,用代码的形式来分析,能让我们更清楚直观一下哈
var personObj = {
name: "mike",
age: 16,
sex: "boy",
favorite: "football",
introduce: function(){
console.log("my name is " + this.name + ", i am " + this.age + " 岁" )
},
myFavorite: function(sex, favorite){
this.sex = sex;
this.favorite = favorite;
console.log("I am a " + this.sex + ", my favorite sport is " + this.favorite)
}
}
我们在上面定义了一个对象,然后我们将上面的introduce方法在不同的作用域下分别执行一下
personObj.introduce(). // my name is mike, i am 16 岁
let introduce = personObj.introduce
var name = "lucy"
var age = 18
introduce(). // my name is lucy, i am 18 岁
我们可以通过以上运行结果指导,this的指向与函数执行的作用域有关,它在什么作用域下执行,this就指向谁.所以,我们在personObj下调用introduce,它里面的this自然就指向personObj
然后,我们将personObj.introduce赋值给了一个变量introduce,这个变量是在全局下面定义的,所以,当我们执行introduce(),它里面的this就应该指向window
我们在全局作用域下定义了name、age变量,就相当于定义了window.name和window.age, 此时this.name和this.age自然就是全局变量了呀
对上面我们都已经很清楚了哈,那和我们本文开始提到的call、apply、bind又有什么关系呢?当然有关系,我们就是想用他们来更换函数执行中的this呢
下面我们来定义另一个对象
let obj2 = {
name: 'jhon',
age: 28,
sex: 'man',
favorite: 'pingpang'
}
可以看到这个对象上面并没有introduce的方法,但是我们在这里想要得到一个和personObj对象上introduce一样的效果,我们要怎么做呢?
这就回归到本文开始了,使用call、apply、bind方法来更改函数执行中this的指向,下面我们分别来说,在这之前,我们先来看看函数的结构
console.dir(introduce)
我们通过上面的结构可以看到,每个函数的原型都有一个apply,bind,call属性,下面我们分别来总结一下他们的使用规则吧
- call
官网解释: Function 实例的 call() 方法会以给定的 this 值和逐个提供的参数调用该函数。
语法: call(thisArg)
introduce.call(obj2). // my name is jhon, i am 28 岁
语法: call(thisArg, arg1, arg2, /* …, */ argN)
let myFavorite = personObj.myFavorite
obj2 // {name: 'jhon', age: 28, sex: 'man', favorite: 'pingpang'}
yFavorite.call(obj2, 'girl', 'dance'). // I am a girl, my favorite sport is dance
obj2. // {name: 'jhon', age: 28, sex: 'girl', favorite: 'dance'}
我们通过上面的代码看到,通过call方法,我们可以借用到其他对象上面的函数
传递给call的第一参数thisArg就是是访问借用函数的对象,并且通过传递arg列表,来给this分配新的属性值
这里,我们在用thisArg要特别注意一下:如果函数不处于严格模式,则 null 和 undefined 会被替换为全局对象,我们来运行一段代码
sex. // 'girl'
favorite // 'basketball'
myFavorite.call(null, 'boy', 'football'). // I am a boy, my favorite sport is football
sex. // 'boy'
favorite // 'football'
上面在call传入null作为执行对象的时候会被替换为全局对象,此时传入新的参数列表,全局对象中值也被更新了
- apply
官网解释: Function 实例的 apply() 方法会以给定的 this 值和作为数组(或类数组对象)提供的 arguments 调用该函数。
语法:
apply(thisArg)
apply(thisArg, argsArray)
下面我们来运行代码看看
obj2. // {name: 'jhon', age: 28, sex: 'girl', favorite: 'dance'}
introduce.apply(obj2). // my name is jhon, i am 28 岁
myFavorite.apply(obj2, ['boy', 'basketball']). // I am a boy, my favorite sport is basketball
obj2. // {name: 'jhon', age: 28, sex: 'boy', favorite: 'basketball'}
我们看到,在使用上apply和call都可以达到一样的效果,更改执行函数的this指向,并且通过传递数组参数来改变执行对象中的属性,他两唯一的区别就是传递参数的类型上面,apply是一个数组
特别注意: 如果函数不处于严格模式,apply中null 和 undefined 也会被替换为全局对象。 这里不再代码演示了,有兴趣的可以自己执行一遍代码加深印象哈
- bind
官方描述: Function 实例的 bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面。
语法:
bind(thisArg)
bind(thisArg, arg1, arg2, /* …, */ argN)
返回值:使用指定的 this 值和初始参数(如果提供)创建的给定函数的副本。
此处,需要特殊注意的是,使用bind方法的时候,他会返回一个使用指定的 this 的新的函数,而不是像call和apply那样立马执行
下面,我们来使用bind来运行代码看看
let newMyFavorite = myFavorite.bind(null, 'boy', 'football')
newMyFavorite
//newMyFavorite是一个函数: ƒ (sex, favorite){
this.sex = sex;
this.favorite = favorite;
console.log("I am a " + this.sex + ", my favorite sport is " + this.favorite)
}
newMyFavorite() //I am a boy, my favorite sport is football
我们通过上面代码看到指向bind方法后,会返回一个带有新的this指向的函数,而并不会立马执行.
而且在指向bind方法的时候参数可以在创建新的函数时候提供,也可以在指向新函数的时候在提供
以上就是call、apply、bind的一些总结