本文共 1100 字,读完只需 4 分钟
前一篇文章咱们尝试模拟实现了 call 和 apply 方法,其实 bind 函数也能够用来改变 this 的指向。bind 和 call和 apply 二者的区别在于,bind 会返回一个被改变了 this 指向的函数。javascript
本文介绍如何模拟实现 bind 函数: java
首先观察 bind 函数有什么特色:数组
var person = { name: 'jayChou' } function say(age, sex) { console.log(this.name, age, sex); } var foo = say.bind(person, '男', 39); foo(); // jayChou 男 39
既然 bind 内部也要用改变 this 指向,咱们能够用现成的 call 函数来实现这一功能。app
Function.prototype.newBind = function(context) { if(typeof this !== 'function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var self = this; return function () { return self.call(context) } }
说明一下:
if 判断是为了校验,只能让函数来调用此方法,并抛出错误。
第二个 return
是为了让返回的函数有返回值的功能。函数
测试一下:测试
var person = { name: 'jayChou' }; var say = function() { console.log(this.name); } var foo = say.newBind(person); foo(); // jayChou
成功啦。this
刚才的函数是没有传递参数,固然不行,因此咱们想办法把函数的参数也传递进去。 spa
bind 函数有个特色,就是在绑定的时候能够传参,返回的函数还能够继续传参。prototype
var person = { name: 'jayChou' }; var say = function(p1, p2) { console.log(this.name, p1, p2); } var foo = say.bind(person, 18); foo(20); // jayChou 18 20
还能够写成:code
say.bind(person, 18)(20); // jayChou 18 20
好,进入正题,考虑传参的事。在前面的文章,咱们讲过 arguments 类数组对象的一些特性,不能直接调用数组的方法,可是能够用原型方法间接来调用,咱们采用 apply 的方式来传递参数。
Function.prototype.newBind = function(context) { if(typeof this !== 'function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var self = this; var args = Array.prototype.slice.call(arguments, 1); // 间接调用数组方法,获取第一次传的参数 return function () { var innerArgs = Array.prototype.slice.call(arguments); return self.apply(context, args.concat(innerArgs)) } } var person = { name: 'jayChou' }; var say = function(p1, p2) { console.log(this.name, p1, p2); } var foo = say.newBind(person, 18); // 第一次传参 foo(20); // 第二次传参
最后输出:
jayChou 18 20
结果正确,以上就完成了 bind 函数基本功能的实现。
可是!
bind 函数其实还有一个很是重要的特色:
bind返回的函数若是做为构造函数,搭配new关键字出现的话,咱们的绑定this就须要“被忽略”。
意思就是指:当使用 nuw 关键字把 bind 返回的函数做为构造函数,以前改变了指向的 this 就失效了。返回的函数的 this 就关联到了构造函数的实例对象上。
针对这个特色,须要再作一些修改:
Function.prototype.newBind = function(context) { if(typeof this !== 'function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var self = this; var args = Array.prototype.slice.call(arguments, 1); // 间接调用数组方法,获取第一次传的参数 let tempFn = function {}; // 利用一个空函数做为中转 tempFn.prototype = this.prototype; // 修改返回函数的 prototype 为绑定函数的 prototype,实例就能够继承绑定函数的原型中的值 var resultFn = function () { var innerArgs = Array.prototype.slice.call(arguments); if (this instanceof tempFn) { // 若是 返回函数被当作构造函数后,生成的对象是 tempFn 的实例,此时应该将 this 的指向指向建立的实例。 return self.apply(this, args.concat(innerArgs)); } else { return self.apply(context, args.concat(innerArgs)) } } resultFn = new tempFn(); return resultFn; }
本文尝试模拟实现了 bind 函数,bind 函数与 call,apply 函数的区别在于,bind 函数返回一个指定了 this 的函数,函数并未执行。其次,当返回的函数做为构造函数时,以前绑定的 this 会失效。
欢迎关注个人我的公众号“谢南波”,专一分享原创文章。