call、apply、bind等方法的真正强大之处就是可以扩充函数赖以运行的做用域。javascript
通俗一点讲,就是改变函数内部的this 的指向。java
关于对变幻莫测的this的指向的理解,能够参考个人另外一篇博文:Javascript千面之变幻莫测的this指向; 数组
背景
先讲些废话吧,小伙伴们相对于call,apply,bind的区别和使用相信在网上能够搜索到不少,笔者写这篇文章的初衷纯粹是为了修改this指向的说明,也为了加深小伙伴们对this指向的理解,从而拓展出来的一片文章,关于对变幻莫测的this的指向的理解,能够参考个人另外一篇博文:Javascript千面之变幻莫测的this指向; 后来越想越多,就越写越多,甚至还能延伸到不少继承和调用的使用场景。app
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对象ui
2.obj.fn.call(aaa):obj.fn是普通函数写法,遵循常规this规则,此时加了一个call(aaa),并传递了一个aaa的对象,此时this的指向发生偏移,指向了传递进来的aaa;this
apply和bind实践后的效果是同样的spa
3.obj.fn1.call(aaa):obj.fn1是ES6箭头函数写法,当箭头函数被定义的时候,this指向的是window对象,此时加了一个call(aaa),并传递了一个aaa的对象,this的指向仍是继续指向window.net
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
喜欢这篇文章的小伙伴们,请点个赞吧,只看不点赞,等于耍流氓~
●﹏●
●﹏●
●﹏●