资讯专栏INFORMATION COLUMN

模拟实现bind方法

array_huang / 1977人阅读

摘要:前言最近接触到方法,为了加深理解,自己模拟实现一下。我们模拟的方法参考上的。基本上关于模拟的实现就是这样。所以不能肯定刚才的就是总结完全重写构造函数原型对象要放在构造函数后面,如果放在构造函数里面,可能原型链会有错误。

前言

最近接触到bind方法,为了加深理解,自己模拟实现一下。

首先,谈一下bind()方法的特点:

把调用bind()的函数(以后统称为初试函数)中的this指向绑定到某个对象上,bind的第二个及其以后的参数作为作为 初始函数的 参数,bind()执行完返回一个新的函数。

新返回的函数如果用 new 字符来调用的话,那么之前this指向绑定到某个对象上 将会失效,并且初始函数中的this指向绑定到 new调用的 函数实例上。函数实例上还可以调用初始函数的原型上的方法

那么先实现第一个特点

// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
   if(typeof this !== "function") {
         throw new TypeError(this+" is not a function");
   }
   
   // 调用bindFn方法的函数的引用
   var self = this;
   // 以数组形式保存第二个及其以后的参数
   var beforeArg = [].slice.call(arguments,1);

   var bound = function() {
        // 以数组形式保存着当前函数的所有参数
        var afterArg = [].slice.call(arguments);
        // bindFn第二个及其以后的参数和当前函数所有参数的 集合
        // 全部传到self函数的参数里
        var finalArgs = beforeArg.concat(afterArg);
        // self中的this指向绑定到 thisArg
        return  self.apply(thisArg, finalArgs);
   }
   return bound;
}
var obj1={sex:23};

function m1(a,b) {
    console.log(a+b); // 6
    console.log(this); // {sex:23}
}

var bound=m1.bindFn(obj1,2);
var result=bound(4); 

可见m1中的this指向了obj1对象,2,4分别传给了形参a,b。

进一步完善bindFn,实现第二个特点

// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
   if(typeof this !== "function") {
         throw new TypeError(this +" is not a function");
   }
   
   // 调用bindFn方法的函数的引用
   var self = this;
   // 以数组形式保存第二个及其以后的参数
   var beforeArg = [].slice.call(arguments,1);

   var bound = function() {
        // 以数组形式保存着当前函数的所有参数
        var afterArg = [].slice.call(arguments);
        // bindFn第二个及其以后的参数和当前函数所有参数的 集合
        // 全部传到self函数的参数里
        var finalArgs = beforeArg.concat(afterArg);
        // 如果new调用的话,this指向实例对象
        // 否则this指向需要绑定的对象
        // this instanceof bound并不准确,可以用es6中的new.target来解决
        return  self.apply(this instanceof bound ? this : thisArg, finalArgs);
   }
   
   // new调用的时候有用
   // 避免es6中的箭头函数
   // 箭头函数没有prototype
   if(this.prototype) {
         // 避免修改 bound.prototype 污染到 this.prototype
      function a(){}
      a.prototype=this.prototype;
      bound.prototype=new a();
      bound.prototype.constructor=bound;
   }
   return bound;
}
var obj1={sex:23};

function m1(a,b) {
    this.name="m1";
    console.log(a+b); // 6
    console.log(this); // {name:"m1"}
}

m1.prototype.allKey=function() {
    console.log("this is allkey");
}

var bound=m1.bindFn(obj1,2);
var result=new bound(4); 
result.allKey(); // this is allkey

可见,m1中的this指向了result这个实例对象,并且result 通过原型链 继承了m1.prototype上的方法。

那么我们看下官方bind的使用:

// 关键部分
function m1(a,b) {
    this.name="m1";
    console.log(a+b); // 6
    console.log(this); // {name:"m1"}
}
var bound=m1.bind(obj1,2);
var result=new bound(4); 
result.allKey(); // this is allkey

由上图可见,虽然原型链上有所不同,但我们模拟的方法还是实现了bind的功能。我们模拟的方法bindFn参考mdn上的Polyfill。基本上关于模拟bind的实现就是这样。

现在我们在延伸一下关于原型链的知识,如果刚才的bound.prototype完全重写在了bound构造函数内,又会怎样?

// 因为需要所有函数都能执行,所以绑定到Function.prototype上
Function.prototype.bindFn=function(thisArg) {
   if(typeof this !== "function") {
         throw new TypeError(this +": is not a function");
   }
   
   // 调用bindFn方法的函数的引用
   var self = this;
   // 以数组形式保存第二个及其以后的参数
   var beforeArg = [].slice.call(arguments,1);

   var bound = function() {
        // 以数组形式保存着当前函数的所有参数
        var afterArg = [].slice.call(arguments);
        // bindFn第二个及其以后的参数和当前函数所有参数的 集合
        // 全部传到self函数的参数里
        var finalArgs = beforeArg.concat(afterArg);
        // 用new 方式调用
        if(this instanceof bound) {
             // 避免es6箭头函数
             // 箭头函数没有prototype
         if(self.prototype) {
                 // 避免修改 bound.prototype 污染到 self.prototype
              function a(){}
              a.prototype=self.prototype;
              bound.prototype=new a();
              bound.prototype.constructor=bound;
            
         }
         // self中的this指向 new生成的实例对象
            return self.apply(this,finalArgs);
        }
        // 无new 调用方式, self中的this指向thisArg
        return  self.apply(thisArg, finalArgs);
   }
     bound.prototype.getKey=function() {

              }
   return bound;
};
var obj1={sex:23};

function m1(a,b) {
    this.name="m1";
    console.log(a+b); // 6
    console.log(this);  
}

m1.prototype.allKey=function() {
    console.log("this is allkey");
}

var bound=m1.bindFn(obj1,2);
var result=new bound(4); 
result.allKey(); // 报错 result.allKey is not a function

按理说 result原型链上应该有allKey方法的,结果却报错了,我们把m1中的this打印一下看看

这里我们看到原型链上并没有m1.prototype, 所以在构造函数内部 完全重写 构造函数原型对象的话,原型链会与预想的不同。

那么图上的bound.prototype打个 ? ,因为并不确定这个是bound.prototype,如果真是bound.prototype的话,那么下面的话如何解释?

console.log(result.__proto__ === bound.prototype ) // false

按理说 result.__proto__ 指向的是bound.prototype,结果却为false。所以不能肯定刚才的就是bound.prototype

总结: 完全重写构造函数原型对象 要放在构造函数后面,如果放在构造函数里面,可能原型链会有错误。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99462.html

相关文章

  • 【进阶3-4期】深度解析bind原理、使用场景及模拟实现

    摘要:返回的绑定函数也能使用操作符创建对象这种行为就像把原函数当成构造器,提供的值被忽略,同时调用时的参数被提供给模拟函数。 bind() bind() 方法会创建一个新函数,当这个新函数被调用时,它的 this 值是传递给 bind() 的第一个参数,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。bind返回的绑定函数也能使用 n...

    guyan0319 评论0 收藏0
  • JavaScript专题之模拟实现bind

    摘要:但是三作为构造函数时函数其实还有一个非常重要的特点返回的函数如果作为构造函数,搭配关键字出现的话,我们的绑定就需要被忽略。其次,当返回的函数作为构造函数时,之前绑定的会失效。 本文共 1100 字,读完只需 4 分钟 概述 前一篇文章我们尝试模拟实现了 call 和 apply 方法,其实 bind 函数也可以用来改变 this 的指向。bind 和 call和 apply 两者的区别...

    刘明 评论0 收藏0
  • 面试官问:能否模拟实现JS的bind方法

    摘要:点击那么面试官可能会问是否想过到底做了什么,怎么模拟实现呢。另外前不久写过一篇文章面试官问能否模拟实现的操作符。所以相当于调用时,的返回值函数内部要模拟实现实现的操作。文章中的例子和测试代码放在中模拟实现。 前言 用过React的同学都知道,经常会使用bind来绑定this。 import React, { Component } from react; class TodoItem ...

    Julylovin 评论0 收藏0
  • JavaScript深入之bind模拟实现

    摘要:也就是说当返回的函数作为构造函数的时候,时指定的值会失效,但传入的参数依然生效。构造函数效果的优化实现但是在这个写法中,我们直接将,我们直接修改的时候,也会直接修改函数的。 JavaScript深入系列第十一篇,通过bind函数的模拟实现,带大家真正了解bind的特性 bind 一句话介绍 bind: bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数...

    FingerLiu 评论0 收藏0
  • call、bind模拟实现

    摘要:大致就是这样所以可以模拟实现和。这些参数作为的第二个参数跟在后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。 一个前端知识点汇总综合了学习过程中的知识点,比如this、闭包、BFC、ES6等,如果大佬们觉得还可以的话,求个star啦! call和apply 每个函数都包含两个非继承而来的方法:apply()和call() 用途相同,都是在...

    mmy123456 评论0 收藏0
  • JavaScript进阶之模拟call,apply和bind

    摘要:模拟和模拟一样,现摘抄下面的代码添加一个返回值对象然后我们定义一个函数,如果执行下面的代码能够返回和函数一样的值,就达到我们的目的。 原文:https://zhehuaxuan.github.io/... 作者:zhehuaxuan 目的 本文主要用于理解和掌握call,apply和bind的使用和原理,本文适用于对它们的用法不是很熟悉,或者想搞清楚它们原理的童鞋。 好,那我们开始...

    CoderBear 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<