`
ry.china
  • 浏览: 139776 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

JavaScript的this,call(),apply(),bind()

 
阅读更多

为了建立一个scope chain, 每个JavaScript的代码执行上下文都提供了this关键字。In its most common usage, this serves as an identity function, providing our neighborhoods a way of referring to themselves. We can’t always rely on that behavior, however: Depending on how we get into a particular neighborhood, this might mean something else entirely. In fact, how we get into the neighborhood is itself exactly what this generally refers to. 需要注意特殊的四种情况:

  • Calling an Object’s Method

    在典型的面向对象编程时,我们需要一种方式去指向和引用我们调用的对象. this serves the purpose admirably, providing our objects the ability to examine themselves, and point at their own properties.

     

    1. var deep_thought = {  
    2.    the_answer: 42,  
    3.    ask_question: function () {  
    4.     return this.the_answer;  
    5.    }  
    6.   };  
    7.    
    8.   var the_meaning = deep_thought.ask_question();   


     

    This example builds an object named deep_thought, sets its the_answer property to 42, and creates an ask_question method. When deep_thought.ask_question() is executed, JavaScript establishes an execution context for the function call, setting this to the object referenced by whatever came before the last ”.”, in this case: deep_thought. The method can then look in the mirror via this to examine its own properties, returning the value stored in this.the_answer: 42.

  • Constructor

    Likewise, when defining a function to be used as a constructor with the new keyword, this can be used to refer to the object being created. Let’s rewrite the example above to reflect that scenario:

     

    1. <mce:script type="text/javascript"><!--  
    2.   function BigComputer(answer) {  
    3.    this.the_answer = answer;  
    4.    this.ask_question = function () {  
    5.     return this.the_answer;  
    6.    }  
    7.   }  
    8.    
    9.   var deep_thought = new BigComputer(42);  
    10.   var the_meaning = deep_thought.ask_question();  
    11.    
    12. // --></mce:script>   


     

    Instead of explicitly creating the deep_thought object, we’ll write a function to create BigComputer objects, and instantiate deep_thought as an instance variable via the new keyword. When new BigComputer() is executed, a completely new object is created transparently in the background. BigComputer is called, and its this keyword is set to reference that new object. The function can set properties and methods on this, which is transparently returned at the end of BigComputer’s execution.

    Notice, though, that deep_thought.the_question() still works just as it did before. What’s going on there? Why does this mean something different inside the_question than it does inside BigComputer? Put simply, we entered BigComputer via new, so this meant “the new object.” On the other hand, we entered the_question via deep_thought, so while we’re executing that method, this means “whatever deep_thought refers to”. this is not read from the scope chain as other variables are, but instead is reset on a context by context basis.

  • Function Call

    What if we just call a normal, everyday function without any of this fancy object stuff? What does this mean in that scenario?

    [javascript] view plaincopyprint?
    1. <mce:script type="text/javascript"><!--  
    2.   function test_this() {  
    3.    return this;  
    4.   }  
    5.   var i_wonder_what_this_is = test_this();  
    6.    
    7. // --></mce:script>   

     

     

    In this case, we weren’t provided a context by new, nor were we given a context in the form of an object to piggyback off of. Here, this defaults to reference the most global thing it can: for web pages, this is the window object.

  • Event Handler

    For a more complicated twist on the normal function call, let’s say that we’re using a function to handle an onclick event. What does this mean when the event triggers our function’s execution? Unfortunately, there’s not a simple answer to this question.

    If we write the event handler inline, this refers to the global window object:

     

    1.  <mce:script type="text/javascript"><!--  
    2.   function click_handler() {  
    3.    alert(this); // alerts the window object  
    4.   }  
    5.    
    6. // --></mce:script>   


     ...
     

    1. <button id='thebutton' onclick='click_handler()'>Click me!</button>   

     

    However, when we add an event handler via JavaScript, this refers to the DOM element that generated the event. (Note: The event handling shown here is short and readable, but otherwise poor. Please use a real addEvent function instead.):

     <script type="text/javascript">
      

    1. <mce:script type="text/javascript"><!--  
    2.   function click_handler() {  
    3.    alert(this); // alerts the button DOM node  
    4.   }  
    5.    
    6.   function addhandler() {  
    7.    document.getElementById('thebutton').onclick = click_handler;  
    8.   }  
    9.    
    10.   window.onload = addhandler;  
    11.    
    12. // --></mce:script>   


     ...
     

    1. <button id='thebutton'>Click me!</button>   

     

Complications

Let’s run with that last example for a moment longer. What if instead of running click_handler, we wanted to ask deep_thought a question every time we clicked the button? The code for that seems pretty straightforward; we might try this:

  1. <mce:script type="text/javascript"><!--  
  2.  function BigComputer(answer) {  
  3.   this.the_answer = answer;  
  4.   this.ask_question = function () {  
  5.    alert(this.the_answer);  
  6.   }  
  7.  }  
  8.    
  9.  function addhandler() {  
  10.   var deep_thought = new BigComputer(42),  
  11.    the_button = document.getElementById('thebutton');  
  12.    
  13.   the_button.onclick = deep_thought.ask_question;  
  14.  }  
  15.    
  16.  window.onload = addhandler;  
  17. // --></mce:script>   

 

对上面的代码,我们期望点击按钮, deep_thought.ask_question被执行,我们得到返回结果“42.” 但为什么得到的结果反而是undefined?哪里错了?

The problem is simply this: We’ve passed off a reference to the ask_question method, which, when executed as an event handler, runs in a different context than when it’s executed as an object method. 简而言之,ask_question 中的this关键字是指向产生事件的DOM元素节点,而不是BigComputer对象. DOM元素节点并没有the_answer属性,所以返回结果是undefined而不是“42.” setTimeout exhibits similar behavior, delaying the execution of a function while at the same time moving it out into a global context.

This issue crops up all over the place in our programs, and it’s a terribly difficult problem to debug without keeping careful track of what’s going on in all the corners of your program, especially if your object has properties that do exist on DOM elements or the window object.

Manipulating Context With .apply() and .call()

We really do want to be able to ask deep_thought a question when we click the button, and more generally, we do want to be able to call object methods in their native context when responding to things like events and setTimeout calls. Two little-known JavaScript methods, apply and call, indirectly enable this functionality by allowing us to manually override the default value of this when we execute a function call. Let’s look at call first:

  1. <mce:script type="text/javascript"><!--  
  2.  var first_object = {  
  3.   num: 42  
  4.  };  
  5.  var second_object = {  
  6.   num: 24  
  7.  };  
  8.    
  9.  function multiply(mult) {  
  10.   return this.num * mult;  
  11.  }  
  12.    
  13.  multiply.call(first_object, 5); // returns 42 * 5  
  14.  multiply.call(second_object, 5); // returns 24 * 5  
  15. // --></mce:script>   

 

In this example, we first define two objects, first_object and second_object, each with a num property. Then we define a multiply function that accepts a single argument, and returns the product of that argument, and the num property of its this object. If we called that function by itself, the answer returned would almost certainly be undefined, since the global window object doesn’t have a num property unless we explicitly set one. We need some way of telling multiply what its this keyword ought refer to; the call method of the multiply function is exactly what we’re looking for.

 call方法的第一个参数定义了this关键字在被调用方法的执行上下文中指向和对象,call方法的剩余参数则是被调用方法的参数。因此当multiply.call(first_object, 5)被执行, multiply函数被调用, 5 为传入方法的第一个参数, this 执行 first_object对象。 Likewise, when multiply.call(second_object, 5) is executed, the multiply function is called, 5 is passed in as the first argument, and the this keyword is set to refer to object second_object.

apply方法和 call方法基本一致,但是允许你以数组的形式向被调用的函数传递参数, which can be quite useful when programatically generating function calls. Replicating the functionality we just talked about using apply is trivial:

  1. <mce:script type="text/javascript"><!--  
  2.  ...  
  3.    
  4.  multiply.apply(first_object, [5]); // returns 42 * 5  
  5.  multiply.apply(second_object, [5]); // returns 24 * 5  
  6. // --></mce:script>   

 

apply and call are very useful on their own, and well worth keeping around in your toolkit, but they only get us halfway to solving the problem of context shifts for event handlers. It’s easy to think that we could solve the problem by simply using call to shift the meaning of this when we set up the handler:

  1. function addhandler() {  
  2.  var deep_thought = new BigComputer(42),  
  3.   the_button = document.getElementById('thebutton');  
  4.    
  5.  the_button.onclick = deep_thought.ask_question.call(deep_thought);  
  6. }   

 

上面的代码仍然存在问题: call是立即执行函数的,因此我们提供的 onclick handler是函数的执行结果而不是函数本身.我们需要JavaScript的另一个特性来解决这个问题:bind方法。

The Beauty of .bind()

I’m not a huge fan of the Prototype JavaScript framework, but I am very much impressed with the quality of its code as a whole. In particular, one simple addition it makes to the Function object has had a hugely positive impact on my ability to manage the context in which function calls execute: bind performs the same general task as call, altering the context in which a function executes. The difference is that bind returns a function reference that can be used later, rather than the result of an immediate execution that we get with call.

If we simplify the bind function a bit to get at the key concepts, we can insert it into the multiplication example we discussed earlier to really dig into how it works; it’s quite an elegant solution:

  1. <mce:script type="text/javascript"><!--  
  2.  var first_object = {  
  3.   num: 42  
  4.  };  
  5.  var second_object = {  
  6.   num: 24  
  7.  };  
  8.    
  9.  function multiply(mult) {  
  10.   return this.num * mult;  
  11.  }  
  12.    
  13.  Function.prototype.bind = function(obj) {  
  14.   var method = this,  
  15.    temp = function() {  
  16.     return method.apply(obj, arguments);  
  17.    };  
  18.    
  19.   return temp;  
  20.  }  
  21.    
  22.  var first_multiply = multiply.bind(first_object);  
  23.  first_multiply(5); // returns 42 * 5  
  24.    
  25.  var second_multiply = multiply.bind(second_object);  
  26.  second_multiply(5); // returns 24 * 5  
  27. // --></mce:script>   

 

First, we define first_object, second_object, and the multiply function, just as before. With those taken care of, we move on to creating a bind method on the Function object’s prototype, which has the effect of making bind available for all functions in our program. When multiply.bind(first_object) is called, JavaScript creates an execution context for the bind method, setting this to the multiply function, and setting the first argument, obj, to reference first_object. So far, so good.

The real genius of this solution is the creation of method, set equal to this (the multiply function itself). When the anonymous function is created on the next line, method is accessible via its scope chain, as is obj (this couldn’t be used here, because when the newly created function is executed, this will be overwritten by a new, local context). This alias to this makes it possible to use apply to execute the multiply function, passing in obj to ensure that the context is set correctly. In computer-science-speak, temp is a closure that, when returned at the end of the bind call, can be used in any context whatsoever to execute multiply in the context of first_object.

This is exactly what we need for the event handler and setTimeout scenarios discussed above. The following code solves that problem completely, binding the deep_thought.ask_question method to the deep_thought context, so that it executes correctly whenever the event is triggered:

  1. function addhandler() {  
  2.  var deep_thought = new BigComputer(42),  
  3.   the_button = document.getElementById('thebutton');  
  4.    
  5.  the_button.onclick = deep_thought.ask_question.bind(deep_thought);  
  6. }   

 

Beautiful.

分享到:
评论

相关推荐

    javascript中apply、call和bind的用法区分_.docx

    在JavaScript编程中,`apply`、`call`和`bind`这三个方法被广泛用于改变函数内部`this`的指向,这对于理解和编写复杂的JavaScript代码至关重要。虽然它们的功能相似,但在具体用法上存在一定的差异。 #### 相同之处...

    开启Javascript中apply、call、bind的用法之旅模式

    为了控制函数的this指向,JavaScript提供了apply、call以及bind方法。以下详细解释了apply、call以及bind的用法,并通过实例加深理解。 1. apply()和call()方法 apply()和call()方法都用于指定函数体内this的值。...

    浅谈javascript中的call、apply、bind_.docx

    JavaScript 中的 call、apply、bind 方法是 Function 对象自带的三个方法,这三个方法的主要作用是转变函数中的 this 指向,从而可以达到“接花移木”的效果。下面将对这三个方法进行具体的讲解,并列出几个经典应用...

    Javascript中call,apply,bind方法的详解与总结

    本文针对JavaScript中三个重要的函数方法——call、apply和bind,进行详尽的分析,并在文章的结尾部分对这三个方法之间的联系和区别进行了概括,以便于读者更深入地理解它们的用途和应用场景。 首先,我们来探讨...

    js代码-JavaScript的call/apply/bind函数实现

    在JavaScript中,`call()`, `apply()`, 和 `bind()` 是三个非常重要的函数,它们都与函数调用有关,但各自有不同的应用场景。本文将深入探讨这三个函数的实现原理及其使用方式。 首先,`call()` 函数允许我们调用一...

    【JavaScript源代码】JavaScript函数之call、apply以及bind方法案例详解.docx

    JavaScript中的call、apply和bind方法都是用来改变函数调用时的上下文(即this值)以及传递参数。它们之间的相同点在于,都能够指定函数执行时的this对象,并且都能接收参数。不同点在于它们的调用方式和执行时机。 ...

    跟我学习javascript的call(),apply(),bind()与回调

    本文将详细解释JavaScript中call(), apply(), 和 bind() 方法的作用、语法以及使用场景,并且会探讨回调函数的使用,帮助理解这些概念在实际编程中的应用。 首先,我们来探讨call() 和 apply() 方法。这两个方法都...

    javascript中call apply 与 bind方法详解

    在JavaScript中,`call`、`apply`和`bind`都是与函数调用相关的三个重要方法,它们都允许我们改变函数内部`this`的指向,从而实现不同对象间的方法复用和灵活控制上下文环境。下面我们将分别详细介绍这三个方法。 1...

    浅谈JavaScript中的apply/call/bind和this的使用

    apply/call/bind三者的联系就在于,都可以用来改变函数中 this 指向的值,且第一个参数为要指向的 this 的值,apply的第二个参数(或 bind 与 call 的不定参数)为要传入的参数。这就不得不提及 javascript 中函数的...

    javascript中call,apply,bind函数用法示例

    在JavaScript中,`call`, `apply`, 和 `bind` 是三个非常重要的函数,它们都用于改变函数内部 `this` 的指向。下面将详细介绍这三个函数的功能、使用方法以及它们之间的区别。 1. **call() 函数** `call()` 函数...

    javascript中apply、call和bind的使用区别

    在JavaScript中,`apply()`, `call()`, 和 `bind()` 都是用来操作函数的上下文,即改变函数内部 `this` 指向的方法。它们有三个共同点:第一,它们都能改变函数执行时的 `this` 值;第二,第一个参数都是指定的新 `...

    javascript中apply/call和bind的使用

    总结来说,apply()、call()和bind()在JavaScript中都是非常重要的方法,它们可以控制函数内部的this指向,允许参数的灵活传递,并且可以创建可重用的函数,从而提升代码的模块性和灵活性。通过理解这些方法的使用...

    JS中的call、apply、bind方法详解.pdf

    在JavaScript中,call、apply、bind三个方法都是函数对象的方法,它们的作用都是改变函数的调用对象。下面,我们将详细介绍这些方法的定义、语法、使用场景和区别。 一、call方法 call方法的语法是:call([thisObj...

    浅谈javascript中的call、apply、bind

    JavaScript中的call、apply和bind方法是Function对象自带的三个用于改变函数this指向的关键方法。理解这些方法有助于我们更好地掌握函数式编程和面向对象编程的高级技巧。 首先,我们来了解call方法。call方法可以...

    JavaScript必知必会(十) call apply bind的用法说明

    在JavaScript中,`call`, `apply`, 和 `bind` 是函数对象的三个核心方法,它们都与函数调用时的上下文(`this` 的值)以及参数传递有关。理解这三个方法是深入学习JavaScript的关键。 1. **call()**: - `call()` ...

    JavaScript中的this/call/apply/bind的使用及区别

    主要介绍了JavaScript中的this/call/apply/bind的使用及区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Global site tag (gtag.js) - Google Analytics