apply call bind的用法与实现
概念
apply call 和bind
允许为不同的对象分配和调用属于一个对象的函数/方法。同时它们可以改变函数内 this 的指向。
区别
apply 和 call 接收的参数形式不同
apply 和 call 都是直接调用函数并得到函数执行结果,而 bind 会返回待执行函数,需要再次调用
用法演示
我们先创建一个对象 parent
const parent = {
name: 'parent',
sayPerson (age, addr) {
return {
name: this.name,
age,
addr
}
}
}
显然它具有 name 属性,及方法 sayPerson,我们现在可以通过 parent.sayPerson()
来输出该对象信息。
const person = parent.sayPerson(60, 'shenzhen');
// {name: "parent", age: 60, addr: "shenzhen"}
现在我们再创建一个对象 son
const son = {
name: 'son'
}
我们现在也想得到 son 的信息,但是 son 对象没有 sayPerson 函数怎么办?借助已有的 parent 对象和 call 方法,我们可以这样写
const person = parent.sayPerson.call(son, 26, 'shenzhen');
// {name: "son", age: 26, addr: "shenzhen"}
可以看出,通过调用 call 函数,我们为 son 对象分配了 sayPerson 方法并进行调用。实现了一个对象可以调用不属于它自己的方法,并且函数内的 this 指向该对象。apply 方法的用法其实一样,只是传参有些区别
const person = parent.sayPerson.call(son, [26, 'shenzhen']);
// {name: "son", age: 26, addr: "shenzhen"}
bind 函数则不直接调用函数,而是返回待调用函数
const sayPersonFn = parent.sayPerson.bind(son, 26, 'shenzhen');
const person = sayPersonFn();
// {name: "son", age: 26, addr: "shenzhen"}
以上就是三者的使用方法和区别,下面我们来看看它们是如何实现的
实现
call的实现
实现原理就是为对象 obj 添加需要调用的方法,接着调用该方法(此时 this 指向 obj),调用过后再删除该方法
简单版
Object.prototype.callFn = function (...args) {
// 第一个参数为目标对象
const context = args[0];
args.shift();
// 为对象赋值需要调用的方法
context.fn = this;
// 调用该方法
context.fn(...args);
// 删除方法
delete context.fn;
}
加上返回值
Object.prototype.callFn = function (...args) {
// 第一个参数为目标对象
const context = args[0];
args.shift();
// 为对象赋值需要调用的方法
context.fn = this;
// 调用该方法
const result = context.fn(...args);
// 删除方法
delete context.fn;
return result;
}
在测试中发现,我们调用 call,如果第一个参数是 null 或者 undefined,那么 call 将以全局 window 来调用方法,此时 this 也指向 window。如果第一个参数不是对象类型,则以空对象 {} 来调用方法。
Object.prototype.callFn = function (...args) {
// 第一个参数为目标对象
let context = args[0];
// undefined 和 null 指向 window
if (context === null || context === undefined) {
context = window;
}
// 不是对象类型则创建空对象
if (typeof context !== 'object') {
context = {};
}
args.shift();
// 为对象赋值需要调用的方法
context.fn = this;
// 调用该方法
const result = context.fn(...args);
// 删除方法
delete context.fn;
return result;
}
至此,我们实现了一个完整的 call 方法。
apply的实现
既然和 call 只存在传参的区别,那我们只需要简单修改下已实现的 call 方法即可。
Object.prototype.applyFn = function (...args) {
let context = args[0];
if (context === null || context === undefined) {
context = window;
}
if (typeof context !== 'object') {
context = {};
}
args.shift();
context.fn = this;
// 和 call 存在差异的地方
const result = context.fn(...args[0]);
delete context.fn;
return result;
}
bind的实现
在实现了 apply 和 call 的前提下,bind 的实现也比较简单。
Object.prototype.bindFn = function (...args) {
// 实际就是多包了层待执行函数
return () => {
return this.applyFn(args[0], (args || []).slice(1));
}
}
至于以 bind 方法返回的函数作为构造函数来创建对象会存在的问题请参考JavaScript深入之bind的模拟实现。
总结
call apply bind
在工作中实际上是比较常见的函数,特别是在一些框架或库的源码中,但是经常有人会混淆它们的用法。希望大家通过此篇文章可以彻底弄清它们的作用与区别,并且知道其实现原理,知其然知其所以然。
参考
欢迎到前端学习打卡群一起学习~516913974
apply call bind的用法与实现的更多相关文章
- js中call、apply、bind的用法
原文链接:http://www.cnblogs.com/xljzlw/p/3775162.html var zlw = { name: "zlw", sayHello: funct ...
- call、apply、bind的用法
数组追加 //用apply拼接 var arr1=[12,'name:foo',2048]; var arr2=['Joe','Hello']; Array.prototype.push.apply( ...
- javascript中call()、apply()、bind()的用法理解
一.bind的用法 第一个:obj.showInfo('arg','arg_18');中传的2个参数通过showInfo方法改变的是obj下中的name和age 第二个:obj.showInfo.bi ...
- JS中的call()、apply() 以及 bind()方法用法总结
JS中的call()方法和apply()方法用法总结 : 讲解: 调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域. function add(c,d){ return thi ...
- javascript中call()、apply()、bind()的用法终于理解
其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge; //17 obj.myFun() //小张年龄undefined 例2 shows( ...
- call,apply,bind的用法与区别
1.call/apply/bind方法的来源 首先,在使用call,apply,bind方法时,我们有必要知道这三个方法究竟是来自哪里?为什么可以使用的到这三个方法? call,apply,bind这 ...
- (转)javascript中call()、apply()、bind()的用法
其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge; //17 obj.myFun() //小张年龄undefined 例2 shows( ...
- <JavaScript> call()、apply()、bind() 的用法
其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例 1 obj.objAge; obj.myFun() // 小张年龄 undefined 例 2 shows() ...
- call、apply、bind 的用法
例1 obj.objAge; //17 obj.myFun() //小张年龄undefined 例2 shows() //盲僧 比较一下这两者this 的差别,第一个打印里面的this 指向obj,第 ...
- JS中call()、apply()、bind()的用法
其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge; //17 obj.myFun() //小张年龄undefined 例2 shows( ...
随机推荐
- lua使用笔记1:Linux 中安装lua
1.lua安装 1)http://www.lua.org/download.html为下载页面 linux中运行 wget http://www.lua.org/ftp/lua-5.2.3.tar.g ...
- Fiddler抓包过滤
1.User Fiters启用 2.Action Action:Run Filterset now是否运行,Load Filterset加载,Save Filterset保存: 3.Hosts过滤 Z ...
- 使用mysqlbinlog查看二进制日志
(一)mysqlbinlog工具介绍 binlog类型是二进制的,也就意味着我们没法直接打开看,MySQL提供了mysqlbinlog来查看二进制日志,该工具类似于Oracle的logminer.my ...
- PHP(ThinkPHP5.0) + PHPMailer 进行邮箱发送验证码
GitHub下载最新版第三方类库PHPMailer: 第一步: 打开网址https://github.com/PHPMailer/PHPMailer/ 下载PHPMailer,PHPMailer 需要 ...
- java学习(第二篇)语法学习
1.java标识符 类名.变量名以及方法名都被称为标识符. 关于 Java 标识符,有以下几点需要注意: 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($).或者下划线(_)开始 首字符之 ...
- Makefile: missing separator(did you mean TAB instead of 8 spaces?). Stop.
通常我们会对vimrc文件加以配置(如将TAB键自动转换为4个空白键). 但正是由于将tab键转换为n个空白键,使得用vim编写的Makefile中不存在tab键(即“\t”)了.恰恰Makefile ...
- [Qt] 去除窗体右上角的问号
this->setWindowFlags(windowFlags()&~Qt::WindowContextHelpButtonHint);
- 用三维的视角理解二维世界:完美解释meshgrid函数,三维曲面,等高线,看完你就懂了。...
完美解释meshgrid函数,三维曲面,等高线 #用三维的视角理解二维世界 #完美解释meshgrid函数,三维曲面,等高线 import numpy as np import matplotlib. ...
- ES6特性之:参数默认值
作为一个开发者,跟进行业步伐是非常需要的,不能躺在现有的知识和经验温床上做美梦.JavaScript的ES2015标准(即我们说的ES6)在2016年已经被广泛应用了,还没开始使用的朋友,赶紧来磨一下 ...
- Vue Cli 3 搭建单页应用项目刷新 404 问题 解决方案(以Apache为例)
vue 项目 版本 Vue Cli 3.3 官方文档 https://router.vuejs.org/zh/guide/essentials/history-mode.html 因为本项目部署在 A ...