我们知道,每个函数在调用的时候会产生一个执行上下文环境,而这个执行上下文环境中包含了诸如 this 等等信息。即当我们调用函数的时候,内部的 this 已经明确地隐式绑定到了某一个对象上。如果我们希望更换 this 的指向,我们该如何更改?

  call/apply/bind 这三个函数能够满足我们的需要。

一个示例:

var common = 'common';
var name = 'global';
var obj = {
name: 'obj'
} function fn(params) {
console.log(params + ' ' + this.name);
} fn(common) // common global
fn.call(obj, common) // common obj

  我们可以看到,通过 call(),函数内部的 this指向了 obj 对象。

call/apply

// 素材函数
var func = function(arg1, arg2) { };

  具体如下:

func.call(yourObj, arg1, arg2);
func.apply(yourObj, [arg1, arg2]);

  所以我们可以看出,apply 和 call 在功能上完全一致,仅仅是传参方式不一致,这样的好处是在传参个数不一定时,可以使用 apply。比如:

// 定义一个 log 方法,让它可以代理 console.log 方法

function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2

  当然,在使用 call/apply 的时候,语句是立即执行的。

bind

  func.bind(yourObj,xxx,xxx) 执行之后会返回一个新函数,是 func 函数的副本,不同的是新函数内部 this 永远指向 yourObj,当然这意味着在调用 bind 完成绑定之后,需要手动执行一下这个新函数。

  其它用法/功能大致与 call 一致,不过在参数传递上有些许不一致:

function fn(a, b, c) {
console.log(a, b, c);
}
var newFn = fn.bind(null, 'Dot'); fn('A', 'B', 'C'); // A B C
newFn('A', 'B', 'C'); // Dot A B
newFn('B', 'C'); // Dot B C

  可以看到,我们在 bind() 的时候传入了一个参数,新方法的实参都是在 bind 中参数的基础上在往后排。

2019-05-16 更新 ==============================================

手动实现 call / apply / bind


call 的实现:

Function.prototype.myCall = function(context = window) { // context, 上下文环境,其实就是传进来的作用域(对象)
// 1. this 的指向
// 我们是通过 fun.call(...) 这种形式调用 call 函数,即点调用,故 call 内部的 this 将指向 fun 这个目标函数;
// 2. context.fn
// 因为 context 就是我们传进来的作用域对象,而 context.fn 其实就是在 context 上添加一个 fn 属性;
// 3. 故这句话完成的任务是:
// 在 context 对象上添加一个函数,这个函数就是我们的目标函数
context.fn = this;
let args = [...arguments].slice(1);
let result = context.fn(...args);
// 删除 context 手动添加的目标函数,必须
delete context.fn;
return result;
}

apply 的实现:

  这个的实现和 myCall 没啥其他区别,多了参数处理这一步。

Function.prototype.myApply = function(context = window) {
context.fn = this
let args = arguments[1]
let result = args ? context.fn(...args) : context.fn();
delete context.fn
return result
}

bind 的实现:

  这里面需要注意的是 bind() 返回值是一个函数,并且这个函数的内部 this 指向永久改变。另外,因为返回值是函数,故这个函数可以被当成构造函数,如果是构造函数的话, this 应该指向构造出来的实例。

Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let args = [...arguments].slice(1)
return function F() {
// 判断是否被当做构造函数使用
if (this instanceof F) {
return _this.apply(this, args.concat([...arguments]))
}
return _this.apply(context, args.concat([...arguments]))
}
}

call, apply 和 bind 方法的更多相关文章

  1. Javascript中call,apply,bind方法的详解与总结

    在 javascript之 this 关键字详解 文章中,谈及了如下内容,做一个简单的回顾: 1.this对象的涵义就是指向当前对象中的属性和方法. 2.this指向的可变性.当在全局作用域时,thi ...

  2. JS中的call、apply、bind方法

    JS中的call.apply.bind方法 一.call()和apply()方法 1.方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[,   [,.argN]]] ...

  3. 理解JS中的call、apply、bind方法(*****************************************************************)

    在JavaScript中,call.apply和bind是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向. call.apply.bind方法的共同点和区别:app ...

  4. 重写apply, call, bind方法

    重写apply, call, bind方法 //原生JavaScript封装apply方法,第四版 Function.prototype.applyFour = function(context) { ...

  5. 简单模拟实现javascript中的call、apply、bind方法

    目录 引子 隐式丢失 硬绑定 实现及原理分析 总体实现(纯净版/没有注释) 写在最后 引子 读完<你不知道的JavaScript--上卷>中关于this的介绍和深入的章节后,对于this的 ...

  6. javascript中函数的call,apply及bind方法

    call 方法调用一个对象的一个方法,以另一个对象替换当前对象.call([thisObj[,arg1[, arg2[,  [,.argN]]]]])参数thisObj可选项.将被用作当前对象的对象. ...

  7. js中的call()、apply()和bind()方法的区别

    call(thisObj,param1,param2....)方法:调用一个对象的方法,用另外的对象去替换当前对象. 下面给出一个例子: function add(a,b){ return a+b; ...

  8. JS中的call、apply、bind方法详解

    bind 是返回对应函数,便于稍后调用:apply .call 则是立即调用 . apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(co ...

  9. 继承:call、apply、bind方法

    javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. call,apply,bind这 ...

  10. JavaScript中的call、apply、bind方法的区别

    在JavaScript 中,this的指向是动态变化的,很可能在写程序的过程中,无意中破坏掉this的指向,所以我们需要一种可以把this的含义固定的技术,于是就有了call,apply 和bind这 ...

随机推荐

  1. count()聚合函数正确用法

    count()聚合计算 count()是聚合函数,对于返回的结果集,一行行地判断,累计值加1,最后返回累计值,count(*).count(主键ID)和count(1)表示返回满足条件的结果集的总行数 ...

  2. 【大数据安全】Kerberos集群安装配置

    1. 概述 Kerberos是一种计算机网络认证协议,它允许某实体在非安全网络环境下通信,向另一个实体以一种安全的方式证明自己的身份.它也指由麻省理工实现此协议,并发布的一套免费软件.它的设计主要针对 ...

  3. Java面试 32个核心必考点完全解析

    目录 课程预习 1.1 课程内容分为三个模块 1.2 换工作面临问题 1.3 课程特色 课时1:技术人职业发展路径 1.1 工程师发展路径 1.2 常见技术岗位划分 1.3 面试岗位选择 1.4 常见 ...

  4. 在docker中初次体验.net core 2.0

    .net core的跨平台有了Linux,不能没有docker……网上的系列文章一大推,特别是docker还有了中文官网:https://www.docker-cn.com/ .上面说的很清楚了,这里 ...

  5. Snapde电子表格编写Exprtk脚本进行数据运算

    Snapde,一个专门为编辑超大型数据量CSV文件而设计的单机版电子表格软件:它运行的速度非常快,反应非常灵敏. 一.打开文件:用Snapde打开需要运算的CSV文件 二.添加行列:在编辑菜单找到设置 ...

  6. Android远程桌面助手(B1391)

    ARDC(B1391), Download:https://files.cnblogs.com/files/we-hjb/ARDC%28B1391%29_EN.7z Android远程桌面助手(B13 ...

  7. android---EventBus的简单使用(一)

    是什么: 就是用来发消息通信的 怎么用: 定义事件:(消息体) public class MessageEvent { /* Additional fields if needed */ } 准备订阅 ...

  8. 纯Java实现微信朋友圈分享图

    纯Java实现微信朋友圈分享图 1.实现分享图的效果 2.开发环境 2.1 JDK * oracle's jdk 1.8以上 2.2 字体 * 若选择了微软雅黑字体又是代码部署到Linux,则需要安装 ...

  9. 利用Navicat高效率postgresql转mysql数据库

    本人很喜欢postgresql数据库,也一直认为postgresql比mysql要更好更强大. 可生态环境太差了,无奈,最近要把一个小站转成mysql数据库. 小站主要表数据110万,pg_dump备 ...

  10. Active Directory Replication(复制)

    KCC(The Knowledge Consistency Checker)知识一致性检查器是一个内置的程序,在所有与管理器上运行,为林创建复制拓扑.默认,KCC15分钟间隔运行一次,根据当时存活的最 ...