理解javascript中bind的使用和模拟实现

前面提到了call/apply的理解和模拟,再次谈谈具有相似性的bind函数的用法和模拟,bind同样是Function.prototype上面的函数,是es5中新增方法。

bind的作用

bind()方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。

// bind 函数的特点:返回一个函数,可以传入参数
function sayName(name, age) {
  this.name = name;
  this.age = age;
  console.log(this.name, this.age, this.gender);
}
var foo = { gender: "man" };
sayName.bind(foo)("bar", 11); // foo 11 man

// 可以把返回的绑定函数当作构造函数,用new操作符创建实例
var bindFun = sayName.bind(foo);
var obj = new bindFun("bar", 12); // bar 12 undefined
console.log(obj); // sayName {name: "bar", age: 12}

以上得出四个结论:

  • bind绑定对象返回一个函数
  • 绑定对象后,函数内的this指向了被绑定的对象
  • 返回的绑定函数可以传参执行
  • 返回的绑定函数可以传参执行当绑定函数当作构造函数,用new创建实例对象的时候其执行的环境不再是绑定的对象,bind 时指定的 this 值会失效

下面我们模拟bind的实现方式,实现一个bind函数。

第一步:返回一个函数,其上下文被切换到指定的对象context,我们这里利用apply完成对象的绑定(为了传参更简洁)

Function.prototype.bindSub = function(context) {
  var self = this;
  return function(){
    return self.apply(context)
  }
}; 

第二步:实现传参功能

Function.prototype.bindSub = function(context) {
  var self = this;
  // 获取绑定时的参数
  var bindArgs = Array.prototype.slice.call(arguments, 1);
  return function() {
    // 获取调用时的参数 :需要绑定和调用是的参数合并
    var invokeArgs = Array.prototype.slice.call(arguments);
    return self.apply(context, bindArgs.concat(invokeArgs));
  };
};

现在基本实现了bind的基本功能:返回函数,传递参数,下面还有一个重要的功能需要完成

一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。即,假如给对象绑定了一个函数返回了一个绑定函数,当我用new去创建实例的时候,仅仅是把函数当作构造函数,原本已绑定的对象上下文失效!

第三部:实现当用new去创建实例的时候,把this(这里需要先对new理解才能知道怎么去做)new的描述(MDN)

new操作符创建实例的三个步骤:以new Foo(...)为例

1.创建一个对象继承构造函数的实例对象 :继承Foo.prototype

2.使用制定参数调用构造函数,并将this指向实例对象

3.由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

Function.prototype.bindSub = function(context) {
  var self = this;
  // 获取绑定时的参数
  var bindArgs = Array.prototype.slice.call(arguments, 1);
  var fBound = function() {
    var fbContext; //函数内的上下文
    var invokeArgs = Array.prototype.slice.call(arguments);
    // 当作为构造函数时,this指向实例,将绑定函数的this指向实例,可以让实例获取绑定函数的值
    if (this instanceof fBound) {
        fbContext = this; // 指向实例
    } else {
        // 当作为绑定函数调用时,this指向被绑定函数的上下文context
        fbContext = context;
    }
    return self.apply(fbContext, bindArgs.concat(invokeArgs));
  };
  // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
  fBound.prototype = this.prototype;
  return fBound;
};

优化最后代码, 为了避免fBound的原型和被绑定函数的原型的引用相同我们需要使用空对象过度的方式继承

Function.prototype.bindSub = function(context) {
  // 不是绑定函数则报类型错误
  if (typeof this !== "function") {
    throw new Error(
      "Function.prototype.bind - what is trying to be bound is not callable"
    );
  }
  var self = this;
  // 获取绑定时的参数
  var bindArgs = Array.prototype.slice.call(arguments, 1);
  var fNOP = function() {};
  var fBound = function() {
    var invokeArgs = Array.prototype.slice.call(arguments);
    // 当作为构造函数时,this指向实例,将绑定函数的this指向实例,可以让实例获取绑定函数的值
    // 当作为绑定函数调用时,this指向被绑定函数的上下文context
    return self.apply(
      this instanceof fNOP ? this : context,
      bindArgs.concat(invokeArgs)
    );
  };
  // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
  fNOP.prototype = this.prototype;
  fBound.prototype = new fNOP();
  fBound.prototype.constructor = fBound;
  return fBound;
};

// 测试
function sayName(name, age) {
  this.name = name;
  this.age = age;
  console.log(this.name, this.age, this.gender);
}
var foo = { gender: "man" };

// 当作一般返回函数调用
sayName.bindSub(foo)("haha", 12); // haha 12 man
var bindFun = sayName.bindSub(foo);
var newFun = new bindFun("xixi", 15); // xixi 15 undefined
console.log(newFun); //fBound {name: "xixi", age: 15}
console.log(newFun instanceof sayName) // true

原文地址:https://www.cnblogs.com/chrissong/p/10493597.html

时间: 2024-08-08 13:59:09

理解javascript中bind的使用和模拟实现的相关文章

理解 JavaScript 中的 this

前言 理解this是我们要深入理解 JavaScript 中必不可少的一个步骤,同时只有理解了 this,你才能更加清晰地写出与自己预期一致的 JavaScript 代码. 本文是这系列的第三篇,往期文章: 理解 JavaScript 中的作用域 理解 JavaScript 中的闭包 什么是 this 消除误解 在解释什么是this之前,需要先纠正大部分人对this的误解,常见的误解有: 指向函数自身. 指向它所在的作用域. 关于为何会误解的原因这里不多讲,这里只给出结论,有兴趣可以自行查询资料

面试官:能解释一下javascript中bind、apply和call这三个函数的用法吗

一.前言    不知道大家还记不记得前几篇的文章:<面试官:能解释一下javascript中的this吗> 那今天这篇文章虽然是介绍javascript中bind.apply和call函数,但是多少也和this有点关联. 假如在前面那场面试末尾,面试官不依不饶继续问你javascript中的this,那看完本篇文章后一定还会有收获. (本篇文章不会站在this的角度去回答问题,而是重于解释bind.apply和call这三个函数的用法和使用场景) 二.正戏开始 面试官:能解释一下javascr

深入理解JavaScript中创建对象模式的演变(原型)

创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Object构造函数和对象字面量方法 工厂模式 自定义构造函数模式 原型模式 组合使用自定义构造函数模式和原型模式 动态原型模式.寄生构造函数模式.稳妥构造函数模式 第一部分:Object构造函数和对象字面量方法 我之前在博文<javascript中对象字面量的理解>中讲到过这两种方法,如何大家不熟悉,可以点进去看一看回顾一下.它们的优点是用来创建单个的对象非常方

深入理解JavaScript中的属性和特性

深入理解JavaScript中的属性和特性? JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaScript中理解对象的本质.理解对象与类的关系.对象与引用类型的关系 对象属性如何进行分类 属性中特性的理解 第一部分:理解JavaScript中理解对象的本质.理解对象与类的关系.对象与引用类型的关系 对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值.对象或者函数.即

【干货理解】理解javascript中实现MVC的原理

理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法.模型有对数据直接访问的权利.模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作. 视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面.当然也包括一些事件的注册或者aja

JavaScript大杂烩6 - 理解JavaScript中的this

在JavaScript开发中,this是很常用的一个关键字,但同时也是一个很容易引入bug的一个关键字,在这里我们就专门总结一下页面中可能出现的this关键字(包括几种在其他页面文件中出现的this). JavaScript中的this关键字通常只使用在函数中,它指向当前函数的调用者,这是this关键字的本质,所有的使用方式都是围绕这个展开的,让我们来看一下在各种性质的函数中this的用法.1. 在对象的函数中使用this var person = { name: 'Frank', say: f

全面理解Javascript中Promise

全面理解Javascript中Promise 最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为 比较好的文章,供大家更好地学习js中非常有趣的Promise Promise概念 2015 年 6 月,ECMAScript 6 的正式版 终于发布了. ECMAScript 是 JavaScript 语言的国际标准,javascript 是 ECMAScript 的实现.ES6 的目标,是使得 JavaScript 语言可以用来编写大

转载 深入理解JavaScript中的this关键字

转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字 1. 一般用处 2. this.x 与 apply().call() 3. 无意义(诡异)的this用处 4. 事件监听函数中的this 5. 总结 在JavaScript中this变量是一个令人难以摸清的关键字,this可谓是非常强大,充分了解this的相关知识有助于我们在编写面向对象的JavaScript程

js架构设计模式——理解javascript中的MVVM开发模式

理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewModel,这种架构模式最初是由微软的MartinFowler作为微软软件的展现层设计模式的规范提出,它是MVC模式的衍生物,MVVM模式的关注点在能够支持事件驱动的UI开发平台,例如HTML5,[2][3] WindowsPresentation Foundation (WPF), Silverligh