你的位置:首页 > Java教程

[Java教程]简要谈谈javascript bind 方法

  最近去参加了场面试,跟面试官聊了很多JS基础上的东西,其中有个问题是谈谈对apply、call、bind的理解和区别。顿时一愣,apply、call我知道,经常用的东西,bind是什么鬼!!!好像见过,也瞅过类似的文章,但是...不记得了...难道和jQuery的事件绑定的bind一样...

  既然不知道,那就整理总结下啰~

 

一、apply和call

  既然提到提到了这两兄弟,也跟着简单做下知识整理。在javascript中,this的指向是一个经常要处理的问题。比较经典的一个问题就是,document.getElementById太长了,敲着好累好烦,用一个短点的方法,对它进行封装一下,就下面这样,两行代码搞定,多么简单~

<div id="test"></div><script> var getDom = document.getElementById; console.log(getDom("test"))</script>

但是。。。调用时,系统提示Uncaught TypeError: Illegal invocation 啊..........这是什么鬼错误 !其实这就是this的问题,使用document.getElementById,函数执行时this指向document,但是使用getDom,函数执行时,this指向的是window。而在getElementById在内核实现中又使用了this,so 就报错了啰!!!

正确的封装方式为:(即用apply/call设定下函数调用时的this指向)

<div id="test"></div><script>var getDom = (function(func){ return function(){  return func.apply(document , arguments); }})(document.getElementById);console.log(getDom("test"))</script>

  总结:apply和call的用法是一致的,就是改变和确定函数执行时的this指向,区别就是apply后一个参数是数组,call后是一堆参数。话不多说,进入正题....

 

二、bind基本

首先来谈谈它跟apply/call的区别,bind方法返回的是一个函数,不会立即执行,待用()调用时才会执行,而apply/call则是立即执行函数。

MDN的解释是:bind()方法会创建一个新函数,称为绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

接下来用demo简单使用下

1、单纯的绑定this

var person1 = { name : "sky" , getName : function(){  return this.name; }}var person2 = {name : "moon"};var getName = person1.getName.bind(person2);  //绑定person2作为getName函数执行的thisconsole.log(getName());   //打印的值为moon

这样用法比较简单

 

2、传参数的形式

var self = {name : "sky" , age: 26}var getDescription = function(country , city){ console.log("my name is " + this.name + " , my age is " + this.age + " , I'am from " + country + " " + city);}.bind(self , "China");getDescription("WuHan");   //my name is sky , my age is 26 , I'am from China WuHan

个人感觉,这种就是相当于通过bind方法预设一些参数,比如此时就是预设的country参数,调用时在传递动态的参数,比如这里的WuHan。通过这样的方式绑定this,配合预设参数,灵活程度十分的高

 

三、bind的使用场景

水平有限,仅谈谈个人。我在多层函数嵌套的代码编写中,喜欢使用var _this = this;来保存上级函数作用域中的this,这样在内嵌的函数中就是可以用_this获取上级作用域的this。如果用bind则可以换一种方式,具体对比如下:

<div id="test1">test1</div><div id="test2">test2</div><script> //旧写法,使用_this保存 var tool1 = {  name: "sky1",  bindEvent: function() {   var _this = this;   document.getElementById("test1").addEventListener("click", function() {    console.log(_this.name)   }, false)  } } //新写法,使用bind绑定 var tool2 = {  name: "sky2",  bindEvent: function() {   document.getElementById("test2").addEventListener("click", function() {    console.log(this.name)   }.bind(this), false)  } } tool1.bindEvent(); tool2.bindEvent();</script>

怎么说呢,代码看起来更加优美一些把....我是颜值控

 

四、bind 兼容性

bind方法是ES5中扩展的方法,所以IE6、7、8均不兼容...整个人都不好了

怎么办呢,网上有一堆的兼容资料,我比较喜欢自己造轮子...其实也蛮简单的,就是apply/call的封装使用,学习嘛,循序渐进的来,分成两步,以下均用skyBind方法表示...

1、仅仅实现修改执行环境即this的功能

Function.prototype.skyBind = function(context){ var func = this;  //就是它!!!这个func就是需要执行,且要绑定context的函数!!!!!! return function(){  return func.apply(context , arguments);   //绑定且执行 }}var person1 = { name : "sky" , getName : function(){  return this.name; }}var person2 = {name : "moon"};var getName = person1.getName.skyBind(person2);  console.log(getName());   //打印的值为moon 

很简单有没有!!!

 

2、加入传参的功能

Function.prototype.skyBind = function(){ var func = this , //还是它!!!这个func就是需要执行,且要绑定context的函数!!!!!!  context = [].shift.call(arguments) ,   //此时传入的参数就不单单有this的指向,还是其他的参数,按规定第一个参数就是执行环境,拿到它  args = [].slice.call(arguments);    //arguments具有数组的属性,毕竟JS是个弱类型的语言,但是它毕竟不是数组,用这个方法就是把arguments转换成数组,不用slice换成splice或者其他的都行 return function(){  /* 绑定且执行,执行函数的参数为两个数组的合并   * 这里需要解释的是args是bind绑定时传入的参数,比如下面demo中的China,而下面的arguments则是实际调用时传入的参数WuHan   * */  return func.apply(context , [].concat(args , [].slice.call(arguments) ));    }}//demovar self = {name : "sky" , age: 26}var getDescription = function(country , city){ console.log("my name is " + this.name + " , my age is " + this.age + " , I'am from " + country + " " + city);}.skyBind(self , "China");getDescription("WuHan");   //my name is sky , my age is 26 , I'am from China WuHan

  

 搞定!!!