call、apply、bind等方法的真正强大之处就是能够扩充函数赖以运行的作用域。
通俗一点讲,就是改变函数内部的this 的指向。
关于对变幻莫测的this的指向的理解,可以参考我的另一篇博文:Javascript千面之变幻莫测的this指向;
背景
先讲些废话吧,小伙伴们相对于call,apply,bind的区别和使用相信在网上可以搜索到很多,笔者写这篇文章的初衷纯粹是为了修改this指向的说明,也为了加深小伙伴们对this指向的理解,从而拓展出来的一片文章,关于对变幻莫测的this的指向的理解,可以参考我的另一篇博文:Javascript千面之变幻莫测的this指向; 后来越想越多,就越写越多,甚至还能延伸到很多继承和调用的使用场景。
1.为了修改this指向
举个例子:
var aaa = { num: 10 } var obj = { num: 20, fn: function () { console.log(this); }, fn1: () => { console.log(this); } } obj.fn() ------> {num: 20, fn, fn1} obj.fn.call(aaa) ------> {num: 10} obj.fn1.call(aaa) ------> window对象
obj.fn.call(aaa) ------> {num: 10} obj.fn.apply(aaa) ------> window对象 obj.fn1.call(aaa) ------> {num: 10} obj.fn1.apply(aaa) ------> window对象 obj.fn.bind(aaa)() ------> {num: 10} obj.fn1.bind(aaa)() ------> window对象
实例解析:
1.obj.fn():执行此方法时,this指向的时obj对象
2.obj.fn.call(aaa):obj.fn是普通函数写法,遵循常规this规则,此时加了一个call(aaa),并传递了一个aaa的对象,此时this的指向发生偏移,指向了传递进来的aaa;
apply和bind实践后的效果是一样的
3.obj.fn1.call(aaa):obj.fn1是ES6箭头函数写法,当箭头函数被定义的时候,this指向的是window对象,此时加了一个call(aaa),并传递了一个aaa的对象,this的指向还是继续指向window
apply和bind实践后的效果是一样的
总结:改变this指向,常用call、apply、bind方法;
在应用于常规函数中指向传递进来的对象或者节点;
在应用于箭头函数中永远指向window
注意:如果call、apply、bind方法没有参数,或者参数为null或undefined,则等同于指向全局window对象。
2.区别和使用
obj.fn.call(this, argu1, argu2, argu3,...) obj.fn.apply(this, [argu1, argu2, argu3,...]) obj.fn.bind(this, argu1, argu2, argu3,...)()
call:
第一个参数是调用的this的指向对象或者节点
第二,三,四...个参数,可以传递进相关函数方法中,以参数列表的形式进行参数传递
apply:
第一个参数是调用的this的指向对象或者节点
第二个参数是一个数组,我们需要传递的参数,全部存储在一个数组中进行传递
bind:
第一个参数是调用的this的指向对象或者节点
第二,三,四...个参数,可以传递进相关函数方法中,以参数列表的形式进行参数传递
小伙伴们可以发现,call和apply的使用方法差不多,不同的是调用函数的参数的传递形式不一样。都是第一个参数是this的指向的对象,后面的参数是调用函数的使用参数。
bind的用法和call的用法差不多,不一样的是bind拷贝一个函数方法,不会主动执行,需要我们手动调用,所以bind(arguments...)后面加一个括号(),表示去执行这个返回的函数方法。
总给: call/apply:fn执行的结果
bind:返回fn的拷贝,并拥有指定的this值和初始参数
注意:很多小伙伴使用的时候,call/bind和apply的参数传递形式会使用错误,这里笔者教给大家一个小技巧哦~
apply的首字母是a,就表明参数是以Array数组的形式传递,其余都是用参数列表形式传递
这样会不会更加容易记得呢?哈哈哈~
3.call、apply、bind的使用场景
在笔者看来,call、apply、bind方法的主要设计理念就是为了:借用方法;
举个生活中的例子:
今天我想喝汤,我去买了菜,但是我没有砂锅;这个时候我去邻居家借了一个锅,既达到了炖汤的目的,又节省了开支,一举两得。
想想看这个例子是不是也适用于程序代码中:
我想写一个方法,存在A对象中。而对象B因为某种原因也需要用到A对象中存的同样的这个方法。这个时候是专门在B对象也扩展一个这样的方法吗?当然不可能,只需要从A对象中借来这个方法使用,既能完成我们的需求,也节省了内存占用。
这就是笔者理解的借用方法式的.call、apply、bind方法调用。 以下这些应用场景,多加体会就可以发现它们的理念都是:借用方法
使用场景:
1.数据类型判断 / 正则验证 / 工具类等等......
<script>
let utils = {
regList: {
phone: /^1[3456789]\d{9}$/,
email: /^([A-Za-z0-9_\-\.])+\@(163.com|qq.com|42du.cn)$/
},
typeList: {
'[object String]': 'string',
'[object Number]': 'number',
'[object Boolean]': 'boolean',
// ... 等等其他类型
}
}
function fn_check(phoneNumber, emailUrl) {
console.log(this)
// this指向obj.regList
let isPhone = this.phone.test(phoneNumber)
let isEmail = this.email.test(emailUrl)
}
// 调用
fn_checkphone.call(utils.regList, 13162636388, 'qwer@163.com')
fn_checkphone.apply(utils.regList, [13162636388, 'qwer@163.com'])
fn_checkphone.bind(utils.regList, 13162636388, 'qwer@163.com')()
</script>
2.数组之间的合并
<script>
var fruitList = ['苹果', '橘子']
Array.prototype.push.call(fruitList, '葡萄', '火龙果');
console.log(fruitList) // (4) ["苹果", "橘子", "葡萄", "火龙果"]
Array.prototype.push.apply(fruitList, '葡萄', '火龙果');
console.log(fruitList) // (4) ["苹果", "橘子", "葡萄", "火龙果"]
Array.prototype.push.bind(fruitList, '葡萄', '火龙果')();
console.log(fruitList) // (4) ["苹果", "橘子", "葡萄", "火龙果"]
</script>
通过上面的这个实例,是不是可以举一反三出“数组追加”, “数组合并”等其他的常用用法呢?聪明的小伙伴们应该很快就可以想到,笔者在这里就不再献丑了~
3. apply获取数组中的最大值和最小值
apply直接传递数组做要调用方法的参数,也省一步展开数组,比如使用Math.max
、Math.min
来获取数组的最大值/最小值:
const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6
4.call、apply的继承使用
function Animal(name){
this.name = name;
this.showName = function(){
console.log(this.name);
}
}
function Cat(name){
Animal.call(this, name);
}
// Animal.call(this) 的意思就是使用this对象代替Animal对象,那么
// Cat中不就有Animal的所有属性和方法了吗,Cat对象就能够直接调用Animal的方法以及属性了
var cat = new Cat("TONY");
cat.showName(); //TONY
5.多继承使用
function Class1(a,b) {
this.showclass1 = function(a,b) {
console.log(`class1: ${a},${b}`);
}
}
function Class2(a,b) {
this.showclass2 = function(a,b) {
console.log(`class2: ${a},${b}`);
}
}
function Class3(a,b,c) {
Class1.call(this);
Class2.call(this);
}
let arr10 = [2,2];
let demo = new Class3();
demo.showclass1.call(this,1); // class1: 1,undefined
demo.showclass1.call(this,1,2); // class1: 1,1
demo.showclass2.apply(this,arr10); // class2: 1,2
喜欢这篇文章的小伙伴们,请点个赞吧,只看不点赞,等于耍流氓~
●﹏●
●﹏●
●﹏●
来源:oschina
链接:https://my.oschina.net/u/3419683/blog/4306671