翼度科技»论坛 编程开发 JavaScript 查看内容

手写bind

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
首先写一个bind的简单示例:
  1. 'use strict'
  2. function fn() {
  3.   console.log('this::', this)
  4.   console.log('arguments::', arguments)
  5. }
  6. // fn() // 这里调用时this 在严格模式下是undefined,非严格模式下是Window
  7. var fn1 = fn.bind('str', 1, 2, 3); // 这里把this改为了'str'
  8. fn1(4, 5, 6);
复制代码
fn1的调用结果如下:

根据以上示例总结几个bind的特征:
① 可以提前绑定this及参数
② 不会立刻调用,返回一个新函数
③ 新的函数调用时也可以传参
1. 初步雏形( 最后有完整代码 )
  1.     Function.prototype.mybind = function (asThis) {
  2.         console.log('mybind.....')
  3.     }
  4.     function fn() {
  5.     }
  6.     fn.mybind();
复制代码
2.提前缓存参数和this
  1.     // 提前缓存参数和this
  2.     var slice = Array.prototype.slice;
  3.     Function.prototype.mybind = function (asThis) {
  4.         // 1、第一个参数是this, 后边的参数才是额外的参数,所以要对参数进行一个截取
  5.         // 2、因为arguments是一个伪数组,所以没有数组的方法,
  6.         //    所以可以提前获取下数组的的方法slice
  7.         var cachedArgs = slice.call(arguments, 1);
  8.         console.log('mybind.....', cachedArgs) // [1, 2]
  9.         // 3、最后它是要返回一个新的函数,所以这里先定义一个要返回的函数
  10.         var innerFn = function () {
  11.             ///4、这里要先汇总一下新函数传过来的参数和提前缓存的参数
  12.             var args = slice.call(arguments);
  13.             cachedArgs = cachedArgs.concat(args);
  14.             console.log('cachedArgs::', cachedArgs) // [1, 2, 3, 4]
  15.             console.log('asThis::', asThis) // '我是this'
  16.         };
  17.         return innerFn;
  18.     }
  19.     function fn() {
  20.     }
  21.     var newFn = fn.mybind('我是this', 1, 2);
  22.     newFn(3, 4);
复制代码
这里对slice.call(arguments, 1)这行做一个说明:以为这里的slice是一个纯净的方法,是没有数据的。所以他要slice就要根据this去slice,所以这里就要把要截取的数组arguments当成它的this,这样就截取到了除第一个参数的外的额外参数如1和2.
3.下边就是随着newFn方法的调用,要这个方法可以执行起来,其实就是要改变this的这个方法fn要执行起来,这就要思考怎么在innerFn的里边拿到fn这个方法。
  1.     var slice = Array.prototype.slice;
  2.     Function.prototype.mybind = function (asThis) {
  3.         // 1、第一个参数是this, 后边的参数才是额外的参数,所以要对参数进行一个截取
  4.         ///2、因为arguments是一个伪数组,所以没有数组的方法,
  5.         //    所以可以提前获取下数组的的方法slice
  6.         var cachedArgs = slice.call(arguments, 1);
  7.         console.log('mybind.....', cachedArgs) // [1, 2]
  8.         // 3、最后它是要返回一个新的函数,所以这里先定义一个要返回的函数
  9.         // 5、① 这里保存fn的值
  10.         var callFn = this;
  11.         var innerFn = function () {
  12.             ///4、这里要先汇总一下新函数传过来的参数和提前缓存的参数
  13.             var args = slice.call(arguments);
  14.             cachedArgs = cachedArgs.concat(args);
  15.             console.log('cachedArgs::', cachedArgs) // [1, 2, 3, 4]
  16.             console.log('asThis::', asThis) // '我是this'
  17.             // 5、② 用fn 改变this,传递参数
  18.             // 原函数 改变this 参数
  19.             // 这里return 是因为要可以拿到newFn 的返回值
  20.             return callFn.apply(asThis, cachedArgs);
  21.         };
  22.         return innerFn;
  23.     }
  24.     function fn() {
  25.         console.log('this::', this)
  26.         console.log('arguments::', arguments)
  27.         return 'fn的返回值'
  28.     }
  29.     var newFn = fn.mybind('我是this', 1, 2);
  30.     console.log(newFn(3, 4)); // ''fn的返回值''
复制代码
4.要求返回的函数可以被new(第6、7步)
  1. var slice = Array.prototype.slice;
  2.     Function.prototype.mybind = function (asThis) {
  3.         // 1、第一个参数是this, 后边的参数才是额外的参数,所以要对参数进行一个截取
  4.         ///2、因为arguments是一个伪数组,所以没有数组的方法,
  5.         //    所以可以提前获取下数组的的方法slice
  6.         var cachedArgs = slice.call(arguments, 1);
  7.         console.log('mybind.....', cachedArgs) // [1, 2]
  8.         // 3、最后它是要返回一个新的函数,所以这里先定义一个要返回的函数
  9.         // 5、① 这里保存fn的值
  10.         var callFn = this;
  11.         var innerFn = function () {
  12.             ///4、这里要先汇总一下新函数传过来的参数和提前缓存的参数
  13.             var args = slice.call(arguments);
  14.             cachedArgs = cachedArgs.concat(args);
  15.             console.log('cachedArgs::', cachedArgs) // [1, 2, 3, 4]
  16.             console.log('asThis::', asThis) // '我是this'
  17.             console.log('查看调用的this:', this); //这里可以看到被调用时是Window 被new时是innerFn {}
  18.             // 所以我们就可以通过this instanceof innerFn来判断是否是被new的
  19.             // 6、这里区分是new的还是调用?
  20.             if (this instanceof innerFn) {
  21.                 // 7、这里模拟创建对象的4步曲
  22.                 var target = {}; //创建一个空对象
  23.                 // 原型挂载
  24.                 target.__proto__ = callFn.prototype;
  25.                 // 执行构造函数
  26.                 callFn.apply(target, cachedArgs);
  27.                 return target;
  28.             } else {
  29.                 // 5、② 用fn 改变this,传递参数
  30.                 //     原函数 改变this 参数
  31.                 //     这里return 是因为要可以拿到newFn 的返回值
  32.                 return callFn.apply(asThis, cachedArgs);
  33.             }
  34.         };
  35.         return innerFn;
  36.     }
  37.     function fn() {
  38.         this.tag = 'Green';
  39.         console.log('this::', this)
  40.         console.log('arguments::', arguments)
  41.         return 'fn的返回值'
  42.     }
  43.     var newFn = fn.mybind('我是this', 1, 2);
  44.     console.log('被调用::', newFn(3, 4)); // 'fn的返回值'
  45.     // 通过上边的打印可以看出fn() this是window new Xx 就是Xx的实例
  46.     // 要求返回的函数可以被new
  47.     var fnObj = fn.mybind('new的this', 5, 6);
  48.     var instance = new fnObj(7, 8);
  49.     console.log('被new::', instance); // fn {tag: 'Green'}
复制代码
5. 以上就完成了整个功能,下边就展示下完成代码:

这里再加个补充;我们在调用mybind时前边对象会被改变的,所以要确保它是个函数,否则告知要传一个函数
  1. var slice = Array.prototype.slice;
  2. Function.prototype.mybind = function (asThis) {
  3.     var cachedArgs = slice.call(arguments, 1);
  4.     var callFn = this;
  5.     if(typeof callFn !== 'function') throw new Error('当前实例非函数')
  6.     var innerFn = function () {
  7.         var args = slice.call(arguments);
  8.         cachedArgs = cachedArgs.concat(args);
  9.         // 区分是否被new
  10.         // 这里可以分别打印下被调用和被new时this的区别
  11.         if (this instanceof innerFn) {
  12.             var target = {};
  13.             target.__proto__ = callFn.prototype;
  14.             callFn.apply(target, cachedArgs);
  15.             return target;
  16.         } else {
  17.             return callFn.apply(asThis, cachedArgs);
  18.         }
  19.     };
  20.     return innerFn;
  21. }
  22. function fn() {
  23.     this.tag = 'ABC';
  24.     return 'fn的返回值'
  25. }
  26. // 被调用时
  27. var newFn = fn.mybind('我是this', 1, 2);
  28. console.log('被调用::', newFn(3, 4)); // 'fn的返回值'
  29. // 被new时
  30. var fnObj = fn.mybind('new的this', 5, 6);
  31. var instance = new fnObj(7, 8);
  32. console.log('被new::', instance); // fn {tag: 'ABC'}
复制代码
来源:https://www.cnblogs.com/lvkehao/p/18118755
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

上一篇: nvm & npm

下一篇: NestJS 基础概念

举报 回复 使用道具