zoukankan      html  css  js  c++  java
  • 改变this指向的三个函数call, apply, bind的实现

    (一)call源码解析

    先上一个call使用

    1 function add(c, d) {
    2   return this.a + this.b + c + d;
    3 }
    4 
    5 const obj = { a: 1, b: 2 };
    6 
    7 console.error(add.call(obj, 3, 4)); // 10

    大统上的说法就是,call改变了this的指向。然后,介绍this xxx什么一大堆名词,反正不管你懂不懂,成功绕晕你就已经ok了,但是实际发生的过程,可以看成下面的样子。

    1 const o = {
    2   a: 1,
    3   b: 2,
    4   add: function(c, d) {
    5     return this.a + this.b + c + d
    6   }
    7 };
    1. 给o对象添加一个add属性,这个时候 this 就指向了 o,
    2. o.add(5,7)得到的结果和add.call(o, 5, 6)相同。
    3. 但是给对象o添加了一个额外的add属性,这个属性我们是不需要的,所以可以使用delete删除它。

    so, 基本为以下三部。

    1 // 1. 将函数设为对象的属性
    2  o.fn = bar
    3 // 2. 执行该函数
    4  o.fn()
    5 // 3. 删除该函数
    6  delete o.fn

    //基于ES3 实现call

     1 Function.prototype.es3Call = function (context) {
     2   var content = context || window;
     3   content.fn = this;
     4   var args = [];
     5   // arguments是类数组对象,遍历之前需要保存长度,过滤出第一个传参
     6   for (var i = 1, len = arguments.length ; i < len; i++) {
     7     // 避免object之类传入
     8     args.push('arguments[' + i + ']');
     9   }
    10   var result = eval('content.fn('+args+')');
    11   delete content.fn;
    12   return result;
    13 }
    14 console.error(add.es3Call(obj, 3, 4)); // 10
    15 console.log(add.es3Call({ a: 3, b: 9 }, 3, 4)); // 19
    16 console.log(add.es3Call({ a: 3, b: 9 }, {xx: 1}, 4)); // 12[object Object]4

    //基于ES6 实现call, 其实差别不大,es6新增… rest,这就可以放弃eval的写法

     1 // ES6 call 实现
     2 Function.prototype.es6Call = function (context) {
     3   var context = context || window;
     4   context.fn = this;
     5   var args = [];
     6   for (var i = 1, len = arguments.length; i < len; i++) {
     7     args.push('arguments[' + i + ']');
     8   }
     9   const result = context.fn(...args);
    10   return result;
    11 }
    12 
    13 console.error(add.es6Call(obj, 3, 4));
    14 console.log(add.es3Call({ a: 3, b: 9 }, {xx: 1}, 4)); // 12[object Object]4

    (二)apply源码解析

    apply和call区别在于apply第二个参数是Array,而call是将一个个传入

    // 基于es3实现

     1 Function.prototype.es3Apply = function (context, arr) {
     2   var context = context || window;
     3   context.fn = this;
     4   var result;
     5   if (!arr) {
     6     result = context.fn();
     7   } else {
     8     // 获取参数
     9     var args = [];
    10     for (var i = 0, len = arr.length; i < len; i++) {
    11       args.push('arr[' + i + ']');
    12     }
    13     // 执行函数
    14     result = eval('context.fn(' + args + ')')
    15   }
    16   delete context.fn;
    17   return result
    18 }
    19 
    20 console.log(add.es3Apply(obj, [1, 'abc', '2'])); // 4abc
    21 // console.log(add.apply(obj, [1, 2]));

    // 基于es6实现

     1 Function.prototype.es6Apply = function (context, arr) {
     2   var context = context || window;
     3   context.fn = this;
     4   var result;
     5   if (!arr) {
     6     result = context.fn();
     7   } else {
     8     if (!(arr instanceof Array)) throw new Error('params must be array');
     9     result = context.fn(...arr);
    10   }
    11   delete context.fn;
    12   return result
    13 }
    14 
    15 console.error(add.es6Apply(obj, [1, 2])); // 6

    (三)bind源码解析

    bind() 方法会创建一个新函数。
    当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,
    之后的一序列参数将会在传递的实参前传入作为它的参数。

    //ES3实现

     1 Function.prototype.es3Bind = function (context) {
     2   if (typeof this !== "function") throw new TypeError('what is trying to be bound is not callback');
     3   var self = this;
     4   var args = Array.prototype.slice.call(arguments, 1);
     5   const fBound = function () {
     6     // 获取函数的参数
     7     var bindArgs = Array.prototype.slice.call(arguments);
     8     // 返回函数的执行结果
     9     // 判断函数是作为构造函数还是普通函数
    10     // 构造函数this instanceof fNOP返回true,将绑定函数的this指向该实例,可以让实例获得来自绑定函数的值。
    11     // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
    12     return self.apply(this instanceof fNOP ? this: context, args.concat(bindArgs));
    13   }
    14   // 创建空函数
    15   var fNOP = function () {};
    16   // fNOP函数的prototype为绑定函数的prototype
    17   fNOP.prototype = this.prototype;
    18   // 返回函数的prototype等于fNOP函数的实例实现继承
    19   fBound.prototype = new fNOP();
    20   // 以上三句相当于Object.create(this.prototype)
    21   return fBound;
    22 }
    23 
    24 var func = foo.es3Bind({a: 1}, '1st');
    25 func('2nd');  // 1 100 1st 2nd
    26 func.call({a: 2}, '3rd'); // 1 100 1st 3rd
    27 new func('4th');  //undefined 100 1st 4th

    //es6实现

     1 Function.prototype.es6Bind = function(context, ...rest) {
     2   if (typeof this !== 'function') throw new TypeError('invalid invoked!');
     3   var self = this;
     4   return function F(...args) {
     5     if (this instanceof F) {
     6       return new self(...rest, ...args)
     7     }
     8     return self.apply(context, rest.concat(args))
     9   }
    10 }
    11 
    12 var func = foo.es3Bind({a: 1}, '1st');
    13 func('2nd');  // 1 100 1st 2nd
    14 func.call({a: 2}, '3rd'); // 1 100 1st 3rd
    15 new func('4th');  //undefined 100 1st 4th
     1     var obj ={
     2         value: 1
     3     }
     4     function bar(yeah, ok){
     5         this.age = "18"
     6         console.log(yeah)
     7         console.log(ok)
     8         console.log(this.value)
     9     }
    10   var fbind =  bar.bind(obj,'我很棒')
    11    var fBind = new fbind('是的')
    12   console.log(fBind.age) //18
    13    //fbind('是的')  // 1
    14 
    15    // 实现   1、保存this返回一个函数  2、可以传入参数 3、一个绑定函数也能使用new操作符创建对象:这种行为就像把函数当成构造器。提供的this值被忽略,同时调用时的参数被提供给模拟函数
    16    Function.prototype.bind2 = function(context){
    17        var self = this
    18        //console.log(this) // this指向bar函数    接下来让this指向obj
    19        var args = Array.prototype.slice.call(arguments,1)  // 将类数组arguments转为真正的数组
    20       // console.log(args)
    21        var fResult =  function(){
    22         var args2 = Array.prototype.slice.call(arguments)
    23         // console.log(args2)
    24        // console.log(this)
    25         self.apply(this instanceof self? this : context,args.concat(args2)) // 三元表达式处解读 this是bar函数的实例吗 是返回this 不是返回context  为了兼容 2 、3点需求
    26        }
    27        fResult.prototype =bar.prototype   // bar函数的原型赋给fResult函数 就拥有bar函数属性与方法了 
    28        return fResul
    29    }
    30    var fbind2 = bar.bind2(obj,'孤独的人会走的更远')
    31    var fBind2 = new fbind2('脑子不用就会生锈')
    32    console.log(fBind2.age)
    33   // fbind2('脑子不用就会生锈')


    原文链接:https://blog.csdn.net/u010377383/java/article/details/80646415

  • 相关阅读:
    Selenium常用API的使用java语言之7-控制浏览器操作
    Selenium常用API的使用java语言之6-WebDriver常用方法
    Selenium常用API的使用java语言之5-selenium元素定位
    Selenium常用API的使用java语言之4-环境安装之Selenium
    Selenium常用API的使用java语言之3-selenium3 浏览器驱动
    Selenium常用API的使用java语言之2-环境安装之IntelliJ IDEA
    ES6常用七大特性
    css实现类似朋友圈九宫格缩略图完美展示
    NodeJS入门篇
    viewport其实没那么难理解
  • 原文地址:https://www.cnblogs.com/xfcao/p/12978977.html
Copyright © 2011-2022 走看看