前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Js篇-面试题5-如果浏览器不支持 bind 函数,实现一个函数让其兼容

Js篇-面试题5-如果浏览器不支持 bind 函数,实现一个函数让其兼容

作者头像
itclanCoder
发布2020-10-28 16:58:30
6280
发布2020-10-28 16:58:30
举报
文章被收录于专栏:itclanCoder

如果浏览器不支持 bind 函数,实现一个函数让其兼容

主要考察bind方法

  • bind作用: 创建一个新的函数(称为绑定函数), 当它被调用时,将其 this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列
  • call,apply方法区别: 都是Function对象内置的方法,挂载在Function原型下共享方法,它们的第一个参数都是用来更改调用方法中 this 的指向。需要注意的是 bind是返回新的函数,以便稍后调用,它往往跟事件结合使用,只有触发了事件,才会调用,而applycall 则是立即调用原函数
代码语言:javascript
复制
const module = {
  name: 'itclanCoder',
  getName: function() {
    return this.name;
  },
};

const unboundGetName = module.getName;
console.log(unboundGetName()); //  函数在全局范围内被调用,undefined,这里的this指向的是window,很多面试题在全局范围定义了一个var name = "川川",那么此处便会输出"川川",因为这里全局作用域内没有name,所以是undefined
const boundGetName = unboundGetName.bind(module); // 通过bind改变this的指向,指向module对象
console.log(boundGetName()); // itclanCoder

在上面的示例代码中,将一个方法从对象中拿出来(上面的 getName 就是 module 对象下的一个方法),然后再调用(module.getName 赋值给 unboundGetName),总期望方法中的 this 是原来的对象(在这里指的是 module 对象,比如在回调中传入这个方法)。

如果不做特殊处理的话,一般会丢失原来的对象。基于这个函数,用原始的对象创建一个绑定函数,巧妙地解决了这个问题(上面使用 bind 后就解决了这个问题,让 this 指向了 module 对象)

  • 语法
代码语言:javascript
复制
// 所要改变的this指向的那个函数.bind(thisArg[, arg1[, arg2[, ...]]])
function.bind(thisArg[, arg1[, arg2[, ...]]])
  • thisArg: 当绑定函数被调用时,该参数会作为 this 的指向。当使用 new 操作符调用绑定函数时,该参数无效,会被忽略,当使用bindsetTimeout中创建一个函数(作为回调提供)时,作为 thisArg 传递的任何原始值都将转换为object。如果 bind 函数的参数列表为空,或者 thisArgnullundefined,执行作用域的 this 将被视为新函数的 thisArg
  • arg1, arg2: 当目标函数被调用时,被预置入绑定函数的参数列表中的参数
  • 返回值返回一个原函数的拷贝,并拥有指定的 this 值和初始参数
  • 应用场景 1:可以对一个函数预设初始参数 只要将这些参数(如果有的话)作为 bind() 的参数写在 this 后面。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面,如下所示
代码语言:javascript
复制
function list() {
  return Array.prototype.slice.call(arguments);  // 将类数组转换成真正的数组
}

function addArguments(arg1, arg2) {
    return arg1 + arg2
}

var list1 = list(1, 2, 3); // [1, 2, 3]
var result1 = addArguments(1, 2); // 3

// 创建一个函数,它拥有预设参数列表。
var leadingThirtysevenList = list.bind(null, 37);

// 创建一个函数,它拥有预设的第一个参数
var addThirtySeven = addArguments.bind(null, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
var result2 = addThirtySeven(5); // 37 + 5 = 42
var result3 = addThirtySeven(5, 10);// 37 + 5 = 42 ,第二个参数会被忽略
  • 应用场景 2: 与setTimeout一起使用 在默认情况下,使用 window.setTimeout()时,此时this关键字会指向 window (或 global)对象。当类的方法中需要 this指向类的实例时,你可能需要显式地把this绑定到回调函数,这样就不会丢失该实例的引用

也就是说,当使用类的方法时需要this指向类实例,就可以使用bind()this 绑定到回调函数来管理实例

代码语言:javascript
复制
function Bloomer() {
  this.petalCount = Math.ceil(Math.random() * 12) + 1;
}

// 在 1 秒钟后声明 bloom
Bloomer.prototype.bloom = function() {
  window.setTimeout(this.declare.bind(this), 1000); // 这里的this原本指向window,但是通过bind方法后,指向Bloomer
};

Bloomer.prototype.declare = function() {
  console.log('我有 ' + this.petalCount + ' 朵花瓣!');
};

var flower = new Bloomer();
flower.bloom();   // 1秒后输出 我有 11 朵花瓣

注意

对于事件处理函数和 setInterval方法也可以使用上面的方法

  • 应用场景 3:绑定函数作为构造函数 绑定函数也适用于使用new操作符来构造目标函数的实例。当使用绑定函数来构造实例注意this 会被忽略,但是传入的参数仍然可用 如下实例代码所示
代码语言:javascript
复制
function Point(x, y) {   // 声明构造器Point函数
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function() {   // 构造器Point方法原型下添加toString
  return this.x + ',' + this.y;
};

var p = new Point(1, 2);
p.toString(); // '1,2'


var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
// 实现中的例子不支持,
// 原生bind方法支持
var YAxisPoint = Point.bind(null, 0/*x*/);

var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'

axisPoint instanceof Point; // true,axisPoint是Point的实例化出来的一对象
axisPoint instanceof YAxisPoint; // true,axisPoint是YAxisPoint实例化出来的一对象
new Point(17, 42) instanceof YAxisPoint; // true // 同上

在上面的示例代码中,PointYAxisPoint是共享原型对象,因此使用 instanceof 运算符判断时为 true

  • 应用场景 4:快捷调用-将类数组对象转换为真实的数组bind()也可以为需要特定 this 值的函数创造捷径
代码语言:javascript
复制
var slice = Array.prototype.slice;

slice.call(arguments);

如果使用 bind()的话,情况变得更简单

代码语言:javascript
复制
// 与前一段代码的 "slice" 效果相同,下面使用了bind
var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

slice(arguments);

在上面的示例代码中,bind()有很多的使用场景,但是bind()函数是在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。这就需要我们自己实现 bind()函数了,在面试题中,可以说是屡见不爽,屡见屡面

  • 方法 1: 模拟实现 bind 方法
代码语言:javascript
复制
 //在Function的原型上自定义myBind()方法
  Function.prototype.myBind = function myBind(context){
       //获取要操作的函数
       var _this=this;
       //获取实参(context除外)
      var args=Array.prototype.slice.call(arguments,1);
      //判断当前浏览器是否兼容bind()方法
     if('bind' in Function.prototype){
         //如果浏览器兼容bind()方法,则使用bind()方法,并返回bind()方法执行后的结果
         return _this.bind(context,args);
      }
      //如果不兼容bind()方法,则返回一个匿名函数
      return function(){
           _this.apply(context,args);
       }
 }
  • 自建 bind 函数来给一个对象绑定事件
  • 思路: 三个参数分别是 对象,事件,回调函数
  • 兼容性: 通过 if 判断对象是否存在addEventListener方法来区分浏览器,当然也可以跟上面一样通过bind是不是Function下的原型对象
  • this 问题的解决:由于传入的回调函数是浏览器调用的,我们是无法去直接操作的,所以我们在attachEvent()不直接传入回调函数,而是先定义一个匿名函数,然后在函数内部调用回调函数,并利用 call 方法改变 this
代码语言:javascript
复制
<!DOCTYPE html>
 <html>
      <head>
          <meta charset="UTF-8">
          <title></title>
          <script type="text/javascript">
            window.onload = function() {
                  var btn1 = document.getElementById("btn1");
                  bind(btn1, "click", function() {
                     alert(this);
                 });
             };
             //定义一个函数bind(),用来为指定元素绑定事件响应函数
             /*
             * 参数:
              *  obj 要绑定事件的对象
              *  eventStr 事件的字符串
              *  func 回调函数
              */
             function bind(obj, eventStr, func) {
                 //判断是否有addEventListener()方法
                 if(obj.addEventListener) {
                     //大部分浏览器兼容的方式,addEventListener是标准浏览器支持的
                     obj.addEventListener(eventStr, func, false);
                 } else {
                      //IE8及以下,注意使用的是on,绑定事件使用的是attachEvent
                     //obj.attachEvent("on"+eventStr, func);//此方法this为window下面提供解决方法
                     //统一this 不直接调用func而是在匿名函数内调用
                     obj.attachEvent("on" + eventStr, function() {
                         //在匿名函数内调用回调函数 利用call()方法将this改为obj
                         func.call(obj);
                     });
                 }
             };
         </script>
     </head>
     <body>
        <button id="btn1">btn1</button>
    </body>
</html>

以上就是bind方法使用相关介绍.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 itclanCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档