0. 写啥都得有的序
call,apply,bind, 这三个Function下的方法,作为修改this指向的三兄弟
,这里列举三个方法的demo之前。我们来简单说一下为啥要改this指向这件事。
this 作为js中非常类似于C语言指针的东西。它所指向的准确来说是对象的上下文。改变了引用的上下文便可以调用这个上下文中的标识符(它可以是对象、变量、方法)。
如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文(context),也就是该函数的this的值。——《JavaScript权威指南(第六版)》p165
1. call() 调用
Function.prototype.call() call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 注意:该方法的语法和作用与
apply()方法类似,只有一个区别,就是call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组。 ----MDN Web Docs
可以使用
apply()和call()方法以新创建的对象为上下文执行构造函数 ——《JavaScript 高级程序(第四版)》p243
call的主要用途有三点:
- 直接调用函数
- 可改变函数内this
- call 主要作用可以实现继承
我们来看看demo
let o = {
name: 'john'
};
function fn(a, b) {
console.log('call', this);
console.log(a + b)
}
fn.call(o, 1, 2);// 改变this指向 o 并且直接调用fn()
// 实现构造函数继承
function Papa(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
function Son(name, age, gender) {
Papa.call(this, name, age, gender)
}
let son = new Son('john', 18, 'M')
console.log(son)//{ name: 'john', age: 18, gender: 'M' }
2. apply() 应用
apply 与call 在用法上极为相识,唯一区别是在入参的形式下。从某种意义来讲,apply 更像是 call 封装的语法糖。
apply的特点:
1. 调用函数,改变内部this指向;
2. 参数必须为数组;
3. 主要应用于数组类;
var o = {
name: 'jervis'
};
function fn(a) {
console.log(this);
console.log(a)
}
fn.apply(o);// this 指向 o
fn.apply(o, ['jervis'])
const arr = [1,2,3,4,5,6,7]
const max = Math.max.apply(Math, arr);// 因为不需要改变指向,可以直接写null 但是在严格模式下会出现错误所以建议填函数的调用者
const min = Math.min.apply(Math, arr);
console.log(`max: ${max}; min: ${min};`)//max: 7; min: 1
3. bind() 绑定
接下来重点说一下bind。
作为ECMAScript 5语法 bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。 ----MDN Web Docs
bind的作用:
1. 改变原函数内部指向;(和另外两个不一样,它不会直接调用函数)
2. 返回改变指向后的拷贝;
let o = {
name: 'John'
};
function fn(a, b) {
console.log('function inner', this);
// console.log(a + b);
};
fn();// global
let f = fn.bind(o, 1, 2);
f();// o
fn();// global
bind 的使用场景:如果有的函数不需要立即调用又想改变这个函数的内部this
根据使用场景我们假设有这样一个需求:我们需要一个用于发送短信点击后便禁用,3秒后重新开启的按钮。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="button">button</button>
</body>
<script>
let btn1 = document.querySelector('button');
console.log(btn1);
btn1.onclick = function() {
this.disabled = true;// this 指向btn这个按钮
// let that = this;
// setTimeout(function() {
// //that.disabled = false;// 定时器内this指window(结合事件循环宏任务)
// this.disabled = false;
// }.bind(this), 2000);
setTimeout(()=> {// 因为箭头函数中的this跟随函数声明当前的上下文,所以定时器中的this在执行时已经指向了当前上下文对象
console.log(this)// btn1
this.disabled = false;
}, 2000);
}
</script>
</html>
4. 总结
经过上面的demo我们可以总结出写满几点:
三个api的相似点:
都可以改变函数内部this的指向
区别:
1. call 和apply 会调用函数,并改变函数内部this指向
2. call 和apply 传递的参数不一样,call(object, param1, param2 ) apply(object, [param1, param2])
2. bind 不会直接调用函数,不会改变函数内部this指向,但是会返回改变this指向的原函数的拷贝
主要应用场景:
1. call 经常用作继承构造函数;
2. apply 经常与数组有关系,比如借助Math实现数组求最大最小值;
3. bind 不调用函数,但是还想改变this指向,常配合定时器使用,但是在ES6普及的环境下(有了箭头函数)已经用的比较少了;
最后的最后,欢迎留言提供宝贵的阅读意见共同进步,原创不易~如果看到这里对您有点帮助请给个赞啦,嘻嘻