JavaScript的this,call(),apply(),bind()
2009-03-27 15:58
886 查看
为了建立一个scope chain, 每个JavaScript的代码执行上下文都提供了this关键字。In its most common usage,
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,
Calling an Object’s Method
在典型的面向对象编程时,我们需要一种方式去指向和引用我们调用的对象.
This example builds an object named
Constructor
Likewise, when defining a function to be used as a constructor with the
Instead of explicitly creating the
Notice, though, that
Function Call
What if we just call a normal, everyday function without any of this fancy object stuff? What does
In this case, we weren’t provided a context by
Event Handler
For a more complicated twist on the normal function call, let’s say that we’re using a function to handle an
If we write the event handler inline,
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.):
对上面的代码,我们期望点击按钮,
The problem is simply this: We’ve passed off a reference to the
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元素节点,而不是
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
Manipulating Context With
We really do want to be able to ask
In this example, we first define two objects,
which can be quite useful when programatically generating function
calls. Replicating the functionality we just talked about using
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
上面的代码仍然存在问题:
The Beauty of
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
If we simplify 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:
First, we define
The real genius of this solution is the creation of
This is exactly what we need for the event handler and
Beautiful.
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,
thismight mean something else entirely. In fact, how we get into the neighborhood is itself exactly what
thisgenerally refers to. 需要注意特殊的四种情况:
Calling an Object’s Method
在典型的面向对象编程时,我们需要一种方式去指向和引用我们调用的对象.
thisserves the purpose admirably, providing our objects the ability to examine themselves, and point at their own properties.
var deep_thought = { the_answer: 42, ask_question: function () { return this.the_answer; } }; var the_meaning = deep_thought.ask_question();
This example builds an object named
deep_thought, sets its
the_answerproperty to 42, and creates an
ask_questionmethod. When
deep_thought.ask_question()is executed, JavaScript establishes an execution context for the function call, setting
thisto the object referenced by whatever came before the last ”.”, in this case:
deep_thought. The method can then look in the mirror via
thisto 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
newkeyword,
thiscan be used to refer to the object being created. Let’s rewrite the example above to reflect that scenario:
<mce:script type="text/javascript"><!-- function BigComputer(answer) { this.the_answer = answer; this.ask_question = function () { return this.the_answer; } } var deep_thought = new BigComputer(42); var the_meaning = deep_thought.ask_question(); // --></mce:script>
Instead of explicitly creating the
deep_thoughtobject, we’ll write a function to create
BigComputerobjects, and instantiate
deep_thoughtas an instance variable via the
newkeyword. When
new BigComputer()is executed, a completely new object is created transparently in the background.
BigComputeris called, and its
thiskeyword 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
thismean something different inside
the_questionthan it does inside
BigComputer? Put simply, we entered
BigComputervia
new, so
thismeant “the new object.” On the other hand, we entered
the_questionvia
deep_thought, so while we’re executing that method,
thismeans “whatever
deep_thoughtrefers to”.
thisis 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
thismean in that scenario?
<mce:script type="text/javascript"><!-- function test_this() { return this; } var i_wonder_what_this_is = test_this(); // --></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,
thisdefaults to reference the most global thing it can: for web pages, this is the
windowobject.
Event Handler
For a more complicated twist on the normal function call, let’s say that we’re using a function to handle an
onclickevent. What does
thismean 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,
thisrefers to the global
windowobject:
<mce:script type="text/javascript"><!-- function click_handler() { alert(this); // alerts the window object } // --></mce:script>
...<button id='thebutton' onclick='click_handler()'>Click me!</button>
However, when we add an event handler via JavaScript, [code]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">, we wanted to ask<mce:script type="text/javascript"><!-- function click_handler() { alert(this); // alerts the button DOM node } function addhandler() { document.getElementById('thebutton').onclick = click_handler; } window.onload = addhandler; // --></mce:script>
...<button id='thebutton'>Click me!</button>Complications
Let’s run with that last example for a moment longer. What if instead of running [code]click_handler
deep_thoughta question every time we clicked the button? The code for that seems pretty straightforward; we might try this:
<mce:script type="text/javascript"><!-- function BigComputer(answer) { this.the_answer = answer; this.ask_question = function () { alert(this.the_answer); } } function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById('thebutton'); the_button.onclick = deep_thought.ask_question; } window.onload = addhandler; // --></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.”
setTimeoutexhibits 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
windowobject.
Manipulating Context With .apply()
and .call()
We really do want to be able to ask deep_thoughta 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
setTimeoutcalls. Two little-known JavaScript methods,
applyand
call, indirectly enable this functionality by allowing us to manually override the default value of
thiswhen we execute a function call. Let’s look at
callfirst:
<mce:script type="text/javascript"><!-- var first_object = { num: 42 }; var second_object = { num: 24 }; function multiply(mult) { return this.num * mult; } multiply.call(first_object, 5); // returns 42 * 5 multiply.call(second_object, 5); // returns 24 * 5 // --></mce:script>
In this example, we first define two objects,
first_objectand
second_object, each with a
numproperty. Then we define a
multiplyfunction that accepts a single argument, and returns the product of that argument, and the
numproperty of its
thisobject. If we called that function by itself, the answer returned would almost certainly be
undefined, since the global
windowobject doesn’t have a
numproperty unless we explicitly set one. We need some way of telling
multiplywhat its
thiskeyword ought refer to; the
callmethod of the
multiplyfunction 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
multiplyfunction is called,
5is passed in as the first argument, and the
thiskeyword 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
applyis trivial:
<mce:script type="text/javascript"><!-- ... multiply.apply(first_object, [5]); // returns 42 * 5 multiply.apply(second_object, [5]); // returns 24 * 5 // --></mce:script>
applyand
callare 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
callto shift the meaning of
thiswhen we set up the handler:
function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById('thebutton'); the_button.onclick = deep_thought.ask_question.call(deep_thought); }
上面的代码仍然存在问题:
call是立即执行函数的,因此我们提供的
onclickhandler是函数的执行结果而不是函数本身.我们需要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 Functionobject has had a hugely positive impact on my ability to manage the context in which function calls execute:
bindperforms the same general task as
call, altering the context in which a function executes. The difference is that
bindreturns 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
bindfunction 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:
<mce:script type="text/javascript"><!-- var first_object = { num: 42 }; var second_object = { num: 24 }; function multiply(mult) { return this.num * mult; } Function.prototype.bind = function(obj) { var method = this, temp = function() { return method.apply(obj, arguments); }; return temp; } var first_multiply = multiply.bind(first_object); first_multiply(5); // returns 42 * 5 var second_multiply = multiply.bind(second_object); second_multiply(5); // returns 24 * 5 // --></mce:script>
First, we define
first_object,
second_object, and the
multiplyfunction, just as before. With those taken care of, we move on to creating a
bindmethod on the
Functionobject’s
prototype, which has the effect of making
bindavailable for all functions in our program. When
multiply.bind(first_object)is called, JavaScript creates an execution context for the
bindmethod, setting
thisto the
multiplyfunction, 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
multiplyfunction itself). When the anonymous function is created on the next line,
methodis accessible via its scope chain, as is
obj(
thiscouldn’t be used here, because when the newly created function is executed,
thiswill be overwritten by a new, local context). This alias to
thismakes it possible to use
applyto execute the
multiplyfunction, passing in
objto ensure that the context is set correctly. In computer-science-speak,
tempis a closure that, when returned at the end of the
bindcall, can be used in any context whatsoever to execute
multiplyin the context of
first_object.
This is exactly what we need for the event handler and
setTimeoutscenarios discussed above. The following code solves that problem completely, binding the
deep_thought.ask_questionmethod to the
deep_thoughtcontext, so that it executes correctly whenever the event is triggered:
function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById('thebutton'); the_button.onclick = deep_thought.ask_question.bind(deep_thought); }
Beautiful.
相关文章推荐
- JavaScript 中的 this, call, apply, bind...
- javascript this 和 es6 箭头函数this 的理解以及call()、apply()、bind()的用法
- Javascript中的this,call,apply,bind!
- JavaScript的this,call(),apply(),bind()
- javascript学习之this、apply、call、bind
- JavaScript的this,call(),apply(),bind()
- JavaScript的this,call(),apply(),bind() ---(转载)
- javascript中this、apply、call、bind的用法和区别
- javascript--关键字 apply bind call this
- 浅谈JavaScript中的apply/call/bind和this的使用
- JavaScript中的call(),apply(),bind()方法 关于this指向
- JavaScript中call,apply,bind方法的总结(改变this指向)
- Javascript中的this,call,apply,bind!
- JavaScript的this,call(),apply(),bind()
- 深入浅出 妙用Javascript中apply、call、bind
- 理解javascript里的ABC--apply bind call
- [js]Javascript call() & apply() vs bind()
- JavaScript中call,apply,bind方法的总结。
- 前端见微知著JavaScript基础篇:你所不知道的apply, call 和 bind
- 深入浅出 妙用Javascript中apply、call、bind