原生JS实现call,apply,bind函数
1. 前言
使用原生JS实现call和apply函数,充分了解其内部原理。call和apply都是为了解决改变this的指向。作用都相同,只是传参的方式不同。除了第一个参数外,call可以接受一个参数列表,apply只接受一个参数数组。
2. call函数
2.1 描述
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
2.2 语法
fun.call(thisArg, arg1, arg2, ...)
2.3 参数
- thisArg:可选的。在 fun 函数运行时指定的
this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数在非严格模式下运行,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是 window 对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。 - arg1, arg2, ...:可选的。指定的参数列表。
2.4 返回值
使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined。
2.5 实现
Function.prototype.myCall = function (context){
if (typeof this !== 'function') {
throw new TypeError('Error')
}
if (context === null || context === undefined) {
context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
context.fn = this
//通过参数伪数组将context后面的参数取出来
let args = [...arguments].slice(1)
let result = context.fn(...args)
//删除 fn
delete context.fn
return result
}
实现思路:
- 首先,判断调用
mycall的是不是函数,如果不是,则直接抛出异常; - 接着,判断是否传入了第一个参数
context,也就是要指定的this值,如果没有传入,则默认为window全局对象; - 然后,谁将来调用
mycall,那么this就是谁,将其赋给context.fn; - 然后,通过参数伪数组将
context后面的参数取出来,并传给context.fn获得执行结果result; - 最后,删除掉
context.fn,并将result返回;
3. aplly函数
3.1 描述
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
3.2 语法
func.apply(thisArg, [argsArray])
3.3 参数
- thisArg:可选的。在 func 函数运行时使用的
this值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为null或undefined时会自动替换为指向全局对象,原始值会被包装。 - argsArray:可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给
func函数。如果该参数的值为null或undefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象。
3.4 返回值
调用有指定**this**值和参数的函数的结果。
3.5 实现
Function.prototype.myApply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
if (context === null || context === undefined) {
context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
} else {
context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
}
context.fn = this
let result
//判断是否存在第二个参数
//如果存在就将第二个参数也展开
if(arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
实现思路:
- 首先,判断调用
myapply的是不是函数,如果不是,则直接抛出异常; - 接着,判断是否传入了第一个参数
context,也就是要指定的this值,如果没有传入,则默认为window全局对象; - 然后,谁将来调用
myapply,那么this就是谁,将其赋给context.fn; - 然后,判断是否传入了第二个参数,如果传入了则将其使用展开运算符
...传给context.fn获得执行结果result,如果没有传入,则直接调用context.fn获得执行结果result; - 最后,删除掉
context.fn,并将result返回;
4. bind函数
4.1 描述
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被bind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。
4.2 语法
func.bind(thisArg[, arg1[, arg2[, ...]]])
4.3 参数
thisArg:调用绑定函数时作为
this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用bind在setTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg。arg1, arg2, ...:当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。
4.4 返回值
返回一个原函数的拷贝,并拥有指定的this值和初始参数。
4.5 实现
Function.prototype.mybind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
const args = [...arguments].slice(1)
//返回一个函数
return function F () {
if (this instanceof F) { // this是否是F的实例 也就是返回的F是否通过new调用
return new _this(...args, ...arguments)
}
return _this.apply(context,args.concat(...arguments))
}
}
实现思路:
- 首先,判断调用
mybind的是不是函数,如果不是,则直接抛出异常; - 接着,谁将来调用
mybind,那么this就是谁,将其赋给_this,缓存一下; - 然后,通过参数伪数组将
context后面的参数(预先添加到绑定函数的参数)取出来,记作args, - 然后,返回一个函数,并判断如果使用
new运算符构造绑定函数,则忽略传入的第一个参数context,并将预先添加到绑定函数的参数args和将来传入新函数的参数arguments分别通过展开运算符...依次传入给调用mybind的调用者,并将结果返回。 - 最后,如果不是使用
new运算符构造绑定函数,则对调用者使用apply方法,将传入的第一个参数以及预先添加到绑定函数的参数args和将来传入新函数的参数arguments分别通过展开运算符...依次传入给调用mybind的调用者,并将结果返回;
(完)
原生JS实现call,apply,bind函数的更多相关文章
- JS实现call,apply,bind函数
实现之前的预备知识 ...用作展开 ...用作剩余参数 Object.create()的作用 原型链与构造函数 这些有时间补上吧 call函数实现 Function.prototype.myCall ...
- JS 的 call apply bind 方法
js的call apply bind 方法都很常见,目的都是为了改变某个方法的执行环境(context) call call([thisObj[,arg1[, arg2[, [,.argN]]]] ...
- JS循环中使用bind函数的参数传递问题
JS循环中使用bind函数的参数传递问题,问题代码如下: for (var sc in result) { var tempp = '<div class="sidebar_todo_ ...
- call,apply,bind函数
一.call函数 a.call(b); 简单的理解:把a对象的方法应用到b对象上(a里如果有this,会指向b) call()的用法:用在函数上面 var Dog=function(){ this.n ...
- js中自己实现bind函数的方式
最近由于工作比较忙,好久都没时间静下心来研究一些东西了.今天在研究 call 和 apply 的区别的时候,看到 github 上面的一篇文章,看完以后,感觉启发很大. 文章链接为 https://g ...
- JS中call,apply,bind方法的总结
why?call,apply,bind干什么的?为什么要学这个? 一般用来指定this的环境,在没有学之前,通常会有这些问题. var a = { user: "小马扎", fn: ...
- 原生JS封装创建多级菜单函数
手写一个使用原生JS封装的多级菜单的函数,满足以下几点需求. 子类层级不确定,可根据数据自动生成多级菜单. 操作便捷,只需传入一个HTML标签. 缺点: 需要满足特定的数据结构 废话不多说,展示代码. ...
- 学习前端的菜鸡对JS的call,apply,bind的通俗易懂理解
call,apply,bind 在JavaScript中,call.apply和bind是Function对象自带的三个方法,都是为了改变函数体内部 this 的指向. a ...
- js笔记——call,apply,bind使用笔记
call和apply obj.call(thisObj, arg1, arg2, ...); obj.apply(thisObj, [arg1, arg2, ...]); 两者作用一致,都是把obj( ...
随机推荐
- Nullable Reference Types 可空引用类型
在写C#代码的时候,你可能经常会遇到这个错误: 但如果想避免NullReferenceException的发生,确实需要做很多麻烦的工作. 可空引用类型 Null Reference Type 所以, ...
- ORM查询总结版
目录 概要 ORM常用字段 ORM基础 自定义一个插入类型,即固定长度 创建类终极版 多对多关系表创建 常用几个代码 参数 ORM与数据库代码对应的关系 外键使用分表很麻烦,要先删除主表后,再删除 不 ...
- Java线程池的底层实现与使用
前言 在我们进行开发的时候,为了充分利用系统资源,我们通常会进行多线程开发,实现起来非常简单,需要使用线程的时候就去创建一个线程(继承Thread类.实现Runnable接口.使用Callable和F ...
- Java基础之集合框架(Collection接口和List接口)
首先我们说说集合有什么作用. 一.集合的作用 1.在类的内部,对数据进行组织: 2.简单而快速的搜索大数量的条目: 3.有的集合接口,提供一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关 ...
- Zookeeper未授权访问测试
前言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...
- 实验吧之【简单的sql注入 1、2、3】
实验吧的三道sql注入(感觉实验吧大部分web都是注入) 简单的SQL注入 地址:http://ctf5.shiyanbar.com/423/web/ 这道题也是sql注入,输入1,页面显示正常,输出 ...
- PHP 调试时中断了事务
1.错误:Uncaught PDOException: SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try res ...
- Ubuntu 16.04 集成安装Apache+PHP+Kerberos+LDAP+phpLDAPadmin
一.安装Apache 1.1.安装Apache apt-get update apt-get install apache2 过程如下: root@duke01:~# apt-get update命中 ...
- vue在一个方法执行完后再执行另一个方法
vue在一个方法执行完后执行另一个方法 用Promise来实现.Promise是ES6的新特性,用于处理异步操作逻辑,用过给Promise添加then和catch函数,处理成功和失败的情况 ES7中新 ...
- Python编程系列---Python中装饰器的几种形式及万能装饰器
根据函数是否传参 是否有返回值 ,可以分析出装饰器的四种形式: 形式一:无参无返回值 def outer(func): def wrapper(): print("装饰器功能1" ...