js中自己实现bind函数的方式
最近由于工作比较忙,好久都没时间静下心来研究一些东西了。今天在研究 call 和 apply 的区别的时候,看到 github 上面的一篇文章,看完以后,感觉启发很大。
文章链接为 https://github.com/lin-xin/blog/issues/7 ,有兴趣的童鞋可以前往学习一下。
但是我主要想写的并不是我今天学习了这篇博文,那样也就太没有技术含量了对吧。
bind的实现
其实文章并不难理解,只要是对 js 有一定程度的了解的同学就能很容易看懂。我主要关心的是文章最后给出的自己动手实现 bind 函数的代码,代码如下:
if (!Function.prototype.bind) {
Function.prototype.bind = function () {
var self = this, // 保存原函数
context = [].shift.call(arguments), // 保存需要绑定的this上下文
args = [].slice.call(arguments); // 剩余的参数转为数组
return function () { // 返回一个新函数
self.apply(context,[].concat.call(args, [].slice.call(arguments)));
}
}
}
1
2
3
4
5
6
7
8
9
10
说实话,咋看这一段代码,感觉好像很平淡无奇,但是你要是细细去体味的话,简直能够让你回味无穷。
[].shift.call(arguments) 的含义
我相信,对于很多对 js 这门语言掌握并不算深的童鞋来说,这句代码的含义貌似并不怎么容易理解,我们其实细分一下,可以拆分出好几个知识点出来。
arguments 的含义
我相信,很多人应该知道 arguments,甚至于在实际的学习工作中也经常用到,但是我还是想在这个地方复习一下。
MDN 网站上对于 Arguments 对象是这么定义的:
arguments 是一个对应于传递给函数的参数的类数组对象。
我们可以看到 arguments 是一个类数组对象,他怎么用呢?别急,我们马上给出示例:
上面是我在 chrome 浏览器控制台进行的一个小测试,可以看到,arguments 是一个类数组对象,它的值中包含了我们在调用函数时候,朝里面传入的参数。
而我们知道,对于对象的调用,我们一般采用点号—— obj.key 的调用方式或者括号加对象的key的方式—— obj[key] ,这两种方式是完全等同的。
因此我们在创建函数的时候,其实并不需要朝里面传入变量,直接用 arguments 的方式来获取变量岂不是更好?依我个人的拙见,虽然这种方式接收参数很灵活,但是也存在弊端,函数的参数本来就是写起来让自己或者他人阅读起来更舒服的,君不见 C、Java 中参数还要指定类型呢,JS 已经够宽松了,再按照这种标准宽松下去,写起来是很随意,但是用起来就很蛋疼了。
[].shift
咋一看,你还不一定看得懂这一句代码,其实它就等同于 Array.prototype.shift ,不信你打开你的 chrome 浏览器控制台,试着输入 Array.prototype.shift === [].shift ,然后你看看是不是能够得到 true。也就是说,两者其实是同一个函数,只是调用的方式存在差异,一个是通过原型的方式直接在类上调用;一个是通过实例化,继承过来,然后再调用。
如果你对这个话题很感兴趣,刚好我在知乎上看到有相关话题的讨论:js中 [].slice 与 Array.prototype.slice 有什么区别?,你可以自行前往查看或者讨论。
call
如果还有不知道call为何物的同学,请前往 mdn 网站 call 页面去详细了解。
mdn 对 call 的定义为:
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
call 的语法为:
fun.call(thisArg, arg1, arg2, …)
在了解了上面三个小知识点以后,我们就能理解了 [].shift.call(arguments) 的含义了。arguments 是类数组对象,它没有 shift 等数组独有的方法,怎么办?现在,我想要拿出传入的参数中的第一个参数,怎么操作,就只有用这种方式了。
我们来试验一下:
function a(){return arguments;}
var temp = a("a","b");
temp.slice(); //Uncaught TypeError: temp.slice is not a function
1
2
3
4
我们可以看到,如果你想直接对 temp 用 slice() 函数,不可能的,做不到的!因为 arguments 是类数组对象,并非真正的对象,并没有 slice 方法。
但是如果你用下面方法:
[].slice.call(temp); //["a", "b"]
1
我们可以看到的是,用上面的方式,就神奇般的得到了想要的结果。
我们在类数组对象上面,成功的运用了数组的 slice 方法。也就是说,相当于,先将 temp 转为数组,然后再对其运用了 slice 方法,然后返回了我们想要的结果。
当然,这种写法,在 es6 面前,已经成为了过去式,es6 为数组新增了一个 from 方法,这个函数的使用方式如下:
Array.from(temp).slice(); //["a", "b"]
1
感兴趣的同学,可以去 mdn 网站去了解 Array.from() 的详细解释。
因此,我们可以总结下,原来 context = [].shift.call(arguments) 这一句就是把参数中的第一个剪切出来,赋给 context,那么也就相当于起到了将 参数中的 this 保存的目的。
args = [].slice.call(arguments);
这一句,将除了 this 上下文的所有参数,传给了 args ,以备后来使用。
bind的理解
要读懂返回值,必须得理解,bind 的作用是什么。
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
有兴趣的同学,可以前往 Function.prototype.bind() 页面查看详细解读。简单点理解,bind 就是用来绑定上下文的,强制将函数的执行环境绑定到目标作用域中去。与 call 和 apply 其实有点类似,但是不同点在于,它不会立即执行,而是返回一个函数。因此我们要想自己实现一个 bind 函数,就必须要返回一个函数,而且这个函数会接收绑定的参数的上下文。
返回函数
return function () { // 返回一个新函数
self.apply(context,[].concat.call(args, [].slice.call(arguments)));
}
1
2
3
这一段其实很好理解,因为bind 绑定了上下文,因此 self.apply 的第一个参数,是之前我们保存的 context。接下来,我们将 bind 的其余参数和调用bind后返回的函数在执行的过程中接收的参数进行拼接
js中自己实现bind函数的方式的更多相关文章
- JS中的高阶函数
JS中的高阶函数 高阶函数是指以函数作为参数的函数,并且可以将函数作为结果返回的函数. 1. 高阶函数 接受一个或多个函数作为输入 输出一个函数 至少满足以上一个条件的函数 在js的内置对象中同样存在 ...
- JavaScript学习12 JS中定义对象的几种方式
JavaScript学习12 JS中定义对象的几种方式 JavaScript中没有类的概念,只有对象. 在JavaScript中定义对象可以采用以下几种方式: 1.基于已有对象扩充其属性和方法 2.工 ...
- JS中的自执行函数
本来规划的是2013年,狠狠的将JS学习下,谁知计划赶不上变化,计划泡汤了.13年的我对JS来说可以说是属于跟风,对它的理解和认识也仅仅是皮毛而已,也是因为要完成<ArcGIS API for ...
- JavaScript学习12 JS中定义对象的几种方式【转】
avaScript学习12 JS中定义对象的几种方式 转自: http://www.cnblogs.com/mengdd/p/3697255.html JavaScript中没有类的概念,只有对象. ...
- main.js中封装全局登录函数
1. 在 main.js 中封装全局登录函数 通过 vue 对象的原型扩展,可以扩展一个函数,这样这个函数就可以在每一个界面通过类似指向对象的方式,去访问这个函数. 如下是 main.js 扩展的函数 ...
- js中声明Number的五种方式
转载自:http://www.jb51.net/article/34191.htm <!DOCTYPE html> <html> <head> <meta c ...
- js中定义变量的三种方式const,val,let 的区别
js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. 1 const b = 2;//正确 2 // const b;//错误,必须初始 ...
- js中的三种函数写法
js中的三种函数写法 <script type="text/javascript"> //普通的声明方式 function myFun(m,n){ alert(m+n) ...
- JS中构造函数和普通函数有什么区别
JS中构造函数有普通函数有什么区别? 1.一般规则 构造函数都应该以 一个大写字母开头,eg: function Person(){...} 而非构造函数则应该以一个小写字母开头,eg: functi ...
随机推荐
- PHP数据结构之五 栈的PHP的实现和栈的基本操作
栈和队列是两种应用非常广泛的数据结构,它们都来自线性表数据结构,都是“操作受限”的线性表. 栈栈在计算机的实现有多种方式:硬堆栈:利用CPU中的某些寄存器组或类似的硬件或使用内存的特殊区域来实现.这类 ...
- Centos手动编译安装vim8
系统:CentOs6.9 gcc版本:5.1.0 github上拉取vim工程之后,cd src,执行一下命令: $ ./configure $ sudo make gcc -c -I. -Iprot ...
- laravel数据迁移(创建错误列不能创建)
创建数据表的命令 php artisan make:migration create_users_table 执行这个迁移的命令, php artisan migrate 其实感觉就像简单的方法创建数 ...
- 基于rank的优化
------------------siwuxie095 基于 rank 的优化 基于 size 的优化,在大多数情况下 ...
- Angular27 指令
1 自定概述 2 自定义指令 详情参见<揭秘Angular2> 2.1 属性指令 >工具代码 <div class="panel panel-primary" ...
- 面试题:java实例变量,局部变量,类变量 背1
一.实例变量 也叫对象变量.类成员变量:从属于类由类生成对象时,才分配存储空间,各对象间的实例变量互不干扰,能通过对象的引用来访问实例变量.但在Java多线程中,实例变量是多个线程共享资源,要注意同步 ...
- [GO]局部变量的特点
package main import "fmt" func main() { //定义在{}里的变量就是局部变量,只能在{}里起作用 //作用域,变量起作用的范围 //执行到定义 ...
- Word文件乱码XML
文章介绍 一个朋友写的文档因为异常关机,导致全部文件变成了xml的乱码,正好帮他解决了,感觉这些或许有些帮助,就先记录下来了. 破损文件介绍 文件破坏之后,打开全是xml格式的文档,结构如下. 恢复过 ...
- mybatis使用count返回int的方法
<select id="countByExample" resultType="java.lang.Integer" > select count( ...
- Codeforces 12D Ball(线段树)
N ladies attend the ball in the King's palace. Every lady can be described with three values: beauty ...