Javascript中bind()方法的使用与实现

我们先来看一道题目


1

2

3

4

var write = document.write; 

write("hello"); 

//1.以上代码有什么问题

//2.正确操作是怎样的

不能正确执行,因为write函数丢掉了上下文,此时this的指向global或window对象,导致执行时提示非法调用异常,所以我们需要改变this的指向

正确的方案就是使用 bind/call/apply来改变this指向

bind方法


1

2

var write = document.write;

write.bind(document)(‘hello‘);

call方法


1

2

var write = document.write;

write.call(document,‘hello‘);

apply方法


1

2

var write = document.write;

write.apply(document,[‘hello‘]);

bind函数

bind()最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的this值。常见的错误就像上面的例子一样,将方法从对象中拿出来,然后调用,并且希望this指向原来的对象。如果不做特殊处理,一般会丢失原来的对象。使用bind()方法能够很漂亮的解决这个问题:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<script type="text/javascript">

this.num = 9; 

var module = { 

    num: 81,

    getNum: function(){

        console.log(this.num);

    }

};

module.getNum(); // 81 ,this->module

var getNum = module.getNum;

getNum(); // 9, this->window or global

var boundGetNum = getNum.bind(module); 

boundGetNum(); // 81,this->module

</script>

偏函数(Partial Functions)

Partial Functions也叫Partial Applications,这里截取一段关于偏函数的定义:

Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.

这是一个很好的特性,使用bind()我们设定函数的预定义参数,然后调用的时候传入其他参数即可:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<script type="text/javascript">

function list() { 

  return Array.prototype.slice.call(arguments);

}

var list1 = list(1, 2, 3);

console.log(list1);// [1, 2, 3]

// 预定义参数37

var leadingThirtysevenList = list.bind(undefined, 37);

var list2 = leadingThirtysevenList();

console.log(list2);// [37] 

var list3 = leadingThirtysevenList(1, 2, 3);

console.log(list3);// [37, 1, 2, 3] 

</script>

和setTimeout or setInterval一起使用

一般情况下setTimeout()的this指向window或global对象。当使用类的方法时需要this指向类实例,就可以使用bind()将this绑定到回调函数来管理实例。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<script type="text/javascript">

function Bloomer() { 

  this.petalCount = Math.ceil(Math.random() * 12) + 1;

}

// 1秒后调用declare函数

Bloomer.prototype.bloom = function() { 

  window.setTimeout(this.declare.bind(this), 1000);

};

Bloomer.prototype.declare = function() { 

  console.log(‘我有 ‘ this.petalCount + ‘ 朵花瓣!‘);

};

var test = new Bloomer();

test.bloom();

</script>

绑定函数作为构造函数

绑定函数也适用于使用new操作符来构造目标函数的实例。当使用绑定函数来构造实例,注意:this会被忽略,但是传入的参数仍然可用。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<script type="text/javascript">

function Point(x, y) { 

  this.x = x;

  this.y = y;

}

Point.prototype.toString = function() { 

  console.log(this.x + ‘,‘ this.y);

};

var p = new Point(1, 2); 

p.toString(); // 1,2

var YAxisPoint = Point.bind(null,10);

var axisPoint = new YAxisPoint(5); 

axisPoint.toString(); // 10,5

console.log(axisPoint instanceof Point); // true 

console.log(axisPoint instanceof YAxisPoint); // true 

console.log(new Point(17, 42) instanceof YAxisPoint); // true  

</script>

上面例子中Point和YAxisPoint共享原型,因此使用instanceof运算符判断时为true

伪数组的转化

上面的几个小节可以看出bind()有很多的使用场景,但是bind()函数是在 ECMA-262 第五版才被加入;它可能无法在所有浏览器上运行。这就需要我们自己实现bind()函数了。

首先我们可以通过给目标函数指定作用域来简单实现bind()方法:


1

2

3

4

5

6

Function.prototype.bind = function(context){ 

  self = this;  //保存this,即调用bind方法的目标函数

  return function(){

      return self.apply(context,arguments);

  };

};

考虑到函数柯里化的情况,我们可以构建一个更加健壮的bind()


1

2

3

4

5

6

7

8

Function.prototype.bind = function(context){ 

  var args = Array.prototype.slice.call(arguments, 1),

  self = this;

  return function(){

      var innerArgs = Array.prototype.slice.call(arguments);

      var finalArgs = args.concat(innerArgs);

      return self.apply(context,finalArgs);

  };<br>}

这次的bind()方法可以绑定对象,也支持在绑定的时候传参。

继续,Javascript的函数还可以作为构造函数,那么绑定后的函数用这种方式调用时,情况就比较微妙了,需要涉及到原型链的传递:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

Function.prototype.bind = function(context){ 

  var args = Array.prototype.slice(arguments, 1),

  F = function(){},

  self = this,

  bound = function(){

      var innerArgs = Array.prototype.slice.call(arguments);

      var finalArgs = args.concat(innerArgs);

      return self.apply((this instanceof F ? this : context), finalArgs);

  };

  F.prototype = self.prototype;

  bound.prototype = new F();

  return bound;

};

这是《JavaScript Web Application》一书中对bind()的实现:通过设置一个中转构造函数F,使绑定后的函数与调用bind()的函数处于同一原型链上,用new操作符调用绑定后的函数,返回的对象也能正常使用instanceof,因此这是最严谨的bind()实现。

对于为了在浏览器中能支持bind()函数,只需要对上述函数稍微修改即可:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Function.prototype.bind = function (oThis) { 

    if (typeof this !== "function") {

      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");

    }

    var aArgs = Array.prototype.slice.call(arguments, 1),

        fToBind = this,

        fNOP = function () {},

        fBound = function () {

          return fToBind.apply(

              this instanceof fNOP && oThis ? this : oThis || window,

              aArgs.concat(Array.prototype.slice.call(arguments))

          );

        };

    fNOP.prototype = this.prototype;

    fBound.prototype = new fNOP();

    return fBound;

};

时间: 2024-08-18 02:12:17

Javascript中bind()方法的使用与实现的相关文章

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

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

【转载】JS中bind方法与函数柯里化

原生bind方法 不同于jQuery中的bind方法只是简单的绑定事件函数,原生js中bind()方法略复杂,该方法上在ES5中被引入,大概就是IE9+等现代浏览器都支持了(有关ES5各项特性的支持情况戳这里ECMAScript 5 compatibility table),权威指南上提到在ES3中利用apply模拟该方法的实现(JS权威指南中函数那章), 但无法真实还原该方法, 这也是真bind方法中的有趣特性. (原文这边理解有问题, 这段话的意思如果结合犀牛书上下文的意思, 再结合犀牛书中

仿javascript中confirm()方法的小插件

10天没有写博客了,不知道为什么,心里感觉挺不舒服的,可能这是自己给自己规定要去完成的事情,没有按照计划执行,总会心里不怎么舒服.最近事情挺多的,终于今天抽空来更新一下博客了. 今天写的是一个小插件.平时我们习惯于使用javascript中自带的confirm()函数做出一个弹窗的效果,但是问题在于这样的弹窗非常不美观,大大降低了网页的整体效果. 好吧废话不多说,首先先来了解一下confirm()函数,下面应该注释得很清楚了. if(confirm("我们去阿里转山吧,好吗?")){/

javascript中splice()方法的用法

在javascript中splice()方法,是一个很强的数组方法,它有多种用法. splice()主要用途是向数组的中部插入项. 有如下3种方式: 删除--可以删除任意数量的项,只需要指定2个参数:要删除的第一项的位置和要删除项的项数. 例如,splice(0,2)会删除数组中的前两项. 插入--可以向指定位置插入任意数量的项,只需要提供3个参数:骑士位置.0(要删除的项数)和要插入的项. 如果要插入多个项,可以再传入第四.第五,一直任意多个项. 例如,splice(2,1,"red"

Javascript中的方法和匿名方法

Javascript方法(函数) 声明函数 以function开头,后跟函数名,与C#.java不同,Javascript不需要声明返回值类型.参数类型.没有返回值就是undefined. 举个栗子更清楚:  无参数无返回值的方法: function f1(){ alert('这是一个方法'); } f1();//调用方法 无参数有返回值的方法: function f2(){ return 100; } var result=f2();//声明一个变量,接收f1()中的返回值 alert(res

javaScript中eval()方法转换json对象

原文:javaScript中eval()方法转换json对象 <script language="javascript"> var user = '{name:"张三",age:23,'+ 'address:{city:"青岛",zip:"266071"},'+ 'email:"[email protected]",'+ 'showInfo:function(){'+ 'document.wri

详解 JavaScript 中 splice() 方法

splice() 方法是一个比较少用的方法,但是功能确实很好,并且在我们 coding 的时候,经常有需要 splice() 方法,先介绍一下该方法. 在 JavaScript 中 splice() 方法,是一个很强的数组方法,它有多种用法. splice() 方法主要用来向数组中添加新的值. 1.删除(需要2个参数,第一个参数为"起始位",第二个参数表示删除多少个) 1 //创建数组 2 var array = []; 3 //添加值 4 array.push(1); 5 array

javascript中bind函数的作用

javascript的bind的作用 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <style> 6 button {background-color:#0f0;} 7 </style> 8 </head> 9 <body> 10 <button id="button"> 按钮

JavaScript中trim 方法实现

Java中的 String 类有个trim() 可以删除字符串前后的空格字符,jQuery中也有trim()方法可以删除字符变量前后的字符串.但是JavaScript中却没有相应的trim() 方法,幸好,JavaScript中有正则表达式,String 对象有replace() 方法,利用JavaScript的正则和replace方法来达到trim() 方法的效果. 接下来介绍两种方法,其实两种方法大同小异.都是在String 对象的prototype属性上进行trim()方法的定义,并提供实