在JS中call、bind、apply都是Function.propotype的方法,这三个方法均可以改变this指向
三者的基本语法:
- call:函数名.call(绑定的对象,arg1,arg2...argn)
- apply:函数名.apply(绑定的对象, [arg1,arg2...argn])
- bind:函数名.bind(绑定的对象,arg1,arg2...argn)()
let obj = {
name: "孙悟空",
age: 1000,
sayInfo() {
return `我是一只猴子,我叫${this.name},今年${this.age}岁了`
}
}
console.log(obj.sayInfo())// 我是一只猴子,我叫:孙悟空,今年1000岁了
let obj2 = {
name:"猪八戒",
age:800
}
console.log(obj.sayInfo.call(obj2)) //我是一只猴子,我叫猪八戒,今年800岁了
console.log(obj.sayInfo.apply(obj2)) //我是一只猴子,我叫猪八戒,今年800岁了
console.log(obj.sayInfo.bind(obj2)()) //我是一只猴子,我叫猪八戒,今年800岁了
当函数需要传参的时候:
function testThis(gender, hobby) {
return `我是${this.name},性别:${gender},今年${this.age}岁了,我的兴趣爱好是${hobby}`
}
let obj = {
name: "孙悟空",
age: 1000
}
let obj2 = {
name: "猪八戒",
age: 800
}
let obj3 = {
name: "唐僧",
age: 18
}
console.log(testThis.call(obj,"男", "打白骨精")) //我是孙悟空,性别:男,今年1000岁了,我的兴趣爱好是打白骨精
console.log(testThis.apply(obj2,["男", "看美女"])) //我是猪八戒,性别:男,今年800岁了,我的兴趣爱好是看美女
console.log(testThis.bind(obj3,"男", "念紧窟咒")()) //我是唐僧,性别:男,今年18岁了,我的兴趣爱好是念紧窟咒
三者的区别和相同点:
三个方法均可以改变函数this的指向
三个方法的第一个参数都是要绑定的对象
call和apply的用法基本相同,只有传参的时候,apply要使用数组
call、apply和bind的主要区别在于,call和apply绑定之后立即执行函数,但是bind返回了一个函数,要手动执行
拓展1:ES2015+之前如何实现继承
使用call和原型链:单纯使用原型链在属性上没办法继承,单纯使用call,无法继承父类的方法
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.eat = function() {
console.log("吃饭~")
}
Person.prototype.run = function() {
console.log("跑步!")
}
function myPerson(name, age) {
//属性继承,改变Person构造函数的this指向
Person.call(this, name, age)
}
// myPerson原型指向到Person实例对象
myPerson.prototype = new Person()
//添加的方法一定要放在原型链之后
myPerson.prototype.say = function() {
return "我是" + this.name + ",我今年" + this.age + "岁了"
}
var person = new myPerson("孙悟空", 1000)
console.log(person.say()) // 我是孙悟空,我今年1000岁了
person.eat() // 吃饭~
person.run() // 跑步!
拓展2:在异步执行中获取父级的this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
</body>
<script>
window.name = "六耳猕猴"
let obj = {
name: "孙悟空",
sayName() {
setTimeout(function() {
console.log(this.name)
}, 200)
}
}
obj.sayName() //打印结果为六耳猕猴,原因是在setTimeout(异步)中的函数this指向了window
/**
* 解决方法一:利用call/apply/bind改变this的指向
*/
let obj2 = {
name: "孙悟空",
sayName() {
setTimeout(function() {
console.log(this.name)
}.call(this), 200)
}
}
obj2.sayName()//孙悟空
/**
* 解决方法2:使用箭头函数让this指向父级
*/
let obj3 = {
name: "孙悟空",
sayName() {
let that = this
setTimeout(() => {
console.log(that.name)
}, 200)
}
}
obj3.sayName()//孙悟空
/**
* 解决方法3:在setTimeout外部定义一个变量保存this
*/
let obj4 = {
name: "孙悟空",
sayName() {
let that = this
setTimeout(function() {
console.log(that.name)
}, 200)
}
}
obj4.sayName()//孙悟空
</script>
</html>