admin管理员组文章数量:1290237
In JavaScript, the "this" operator can refer to different things under different scenarios.
Typically in a method within a JavaScript "object", it refers to the current object.
But when used as a callback, it becomes a reference to the calling object.
I have found that this causes problems in code, because if you use a method within a JavaScript "object" as a callback function you can't tell whether "this" refers to the current "object" or whether "this" refers to the calling object.
Can someone clarify usage and best practices regarding how to get around this problem?
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
TestObject.prototype.callBack = function(o){
// do something with "this"
//when method is called directly, "this" resolves to the current object
//when invoked by the asyncRequest callback, "this" is not the current object
//what design patterns can make this consistent?
this.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}
In JavaScript, the "this" operator can refer to different things under different scenarios.
Typically in a method within a JavaScript "object", it refers to the current object.
But when used as a callback, it becomes a reference to the calling object.
I have found that this causes problems in code, because if you use a method within a JavaScript "object" as a callback function you can't tell whether "this" refers to the current "object" or whether "this" refers to the calling object.
Can someone clarify usage and best practices regarding how to get around this problem?
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
TestObject.prototype.callBack = function(o){
// do something with "this"
//when method is called directly, "this" resolves to the current object
//when invoked by the asyncRequest callback, "this" is not the current object
//what design patterns can make this consistent?
this.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}
Share
Improve this question
edited Sep 17, 2008 at 5:13
Jason Bunting
58.9k15 gold badges104 silver badges94 bronze badges
asked Sep 17, 2008 at 4:46
ChrisChris
1
- A great explanation on mysterious this behavior based on context here – RBT Commented Oct 23, 2017 at 10:59
10 Answers
Reset to default 88Quick advice on best practices before I babble on about the magic this variable. If you want Object-oriented programming (OOP) in Javascript that closely mirrors more traditional/classical inheritance patterns, pick a framework, learn its quirks, and don't try to get clever. If you want to get clever, learn javascript as a functional language, and avoid thinking about things like classes.
Which brings up one of the most important things to keep in mind about Javascript, and to repeat to yourself when it doesn't make sense. Javascript does not have classes. If something looks like a class, it's a clever trick. Javascript has objects (no derisive quotes needed) and functions. (that's not 100% accurate, functions are just objects, but it can sometimes be helpful to think of them as separate things)
The this variable is attached to functions. Whenever you invoke a function, this is given a certain value, depending on how you invoke the function. This is often called the invocation pattern.
There are four ways to invoke functions in javascript. You can invoke the function as a method, as a function, as a constructor, and with apply.
As a Method
A method is a function that's attached to an object
var foo = {};
foo.someMethod = function(){
alert(this);
}
When invoked as a method, this will be bound to the object the function/method is a part of. In this example, this will be bound to foo.
As A Function
If you have a stand alone function, the this variable will be bound to the "global" object, almost always the window object in the context of a browser.
var foo = function(){
alert(this);
}
foo();
This may be what's tripping you up, but don't feel bad. Many people consider this a bad design decision. Since a callback is invoked as a function and not as a method, that's why you're seeing what appears to be inconsistent behaviour.
Many people get around the problem by doing something like, um, this
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
You define a variable that which points to this. Closure (a topic all it's own) keeps that
around, so if you call bar as a callback, it still has a reference.
As a Constructor
You can also invoke a function as a constructor. Based on the naming convention you're using (TestObject
) this also may be what you're doing and is what's tripping you up.
You invoke a function as a Constructor with the new
keyword.
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
When invoked as a constructor, a new Object will be created, and this will be bound to that object. Again, if you have inner functions and they're used as callbacks, you'll be invoking them as functions, and this will be bound to the global object. Use that var that = this;
trick/pattern.
Some people think the constructor/new keyword was a bone thrown to Java/traditional OOP programmers as a way to create something similar to classes.
With the Apply Method.
Finally, every function has a method (yes, functions are objects in Javascript) named apply
. Apply lets you determine what the value of this will be, and also lets you pass in an array of arguments. Here's a useless example.
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
In JavaScript, this
always refers to the object invoking the function that is being executed. So if the function is being used as an event handler, this
will refer to the node that fired the event. But if you have an object and call a function on it like:
myObject.myFunction();
Then this
inside myFunction
will refer to myObject
. Does it make sense?
To get around it you need to use closures. You can change your code as follows:
function TestObject() {
TestObject.prototype.firstMethod = function(){
this.callback();
YAHOO.util.Connect.asyncRequest(method, uri, callBack);
}
var that = this;
TestObject.prototype.callBack = function(o){
that.secondMethod();
}
TestObject.prototype.secondMethod = function() {
alert('test');
}
}
this
corresponds to the context for the function call. For functions not called as part of an object (no .
operator), this is the global context (window
in web pages). For functions called as object methods (via the . operator), it's the object.
But, you can make it whatever you want. All functions have .call() and .apply() methods that can be used to invoke them with a custom context. So if i set up an object Chile like so:
var Chile = { name: 'booga', stuff: function() { console.log(this.name); } };
...and invoke Chile.stuff(), it'll produce the obvious result:
booga
But if i want, i can take and really screw with it:
Chile.stuff.apply({ name: 'supercalifragilistic' });
This is actually quite useful...
If you're using a javascript framework, there may be a handy method for dealing with this. In Prototype, for example, you can call a method and scope it to a particular "this" object:
var myObject = new TestObject();
myObject.firstMethod.bind(myObject);
Note: bind() returns a function, so you can also use it to pre-scope callbacks inside your class:
callBack.bind(this);
http://www.prototypejs.org/api/function/bind
I believe this may be due to how the idea of [closures](http://en.wikipedia.org/wiki/Closure_(computer_science) work in Javascript.
I am just getting to grips with closures myself. Have a read of the linked wikipedia article.
Here's another article with more information.
Anyone out there able to confirm this?
You can also use Function.Apply(thisArg, argsArray)... Where thisArg determines the value of this inside your function...the second parameter is an optional arguments array that you can also pass to your function.
If you don't plan on using the second argument, don't pass anything to it. Internet Explorer will throw a TypeError at you if you pass null (or anything that is not an array) to function.apply()'s second argument...
With the example code you gave it would look something like:
YAHOO.util.Connect.asyncRequest(method, uri, callBack.Apply(this));
If you're using Prototype you can use bind() and bindAsEventListener() to get around that problem.
As soon as callback methods are called from other context I'm usually using something that I'm call callback context:
var ctx = function CallbackContext()
{
_callbackSender
...
}
function DoCallback(_sender, delegate, callbackFunc)
{
ctx = _callbackSender = _sender;
delegate();
}
function TestObject()
{
test = function()
{
DoCallback(otherFunc, callbackHandler);
}
callbackHandler = function()
{
ctx._callbackSender;
//or this = ctx._callbacjHandler;
}
}
To understand this
binding we have to keep a few things in mind:
this
is a context(mostly object) which is automatically defined in the scope of the function.- It is not a author-time binding, but a run-time binding. So, there is no way to know the value of this
this
by looking at the function definition. this
value is only based on how the function is called.
Now based on how the function is being called. There are 4 main rules which decide what will this point to, in order of precedence:
Consider object:
function init(value, delta){
this.value = value;
this.delta = delta;
}
const count = {
value: null,
delta: null,
init: init
}
New binding: If the function is invoked with the new keyword (
new init(..)
), then this will point to a newly constructed object.Explicit context binding: If the function is called with
call()
,apply()
, or bound using bind (init.call(..)
), then this will point to the object given as an argument tocall/apply
.Implicit context binding: If the function is called with a context using dot notation (
count.init()
), then this will point to the object on the left-hand side of the.
(dot).Default binding: If none of the above is true and the function is called directly (
init(..)
), then this will point to undefined in strict mode and global otherwise.
For an in-depth guide on how each of these rules work you can check out this article.
'this' is one of the tricks topics in js first of all. So facing the mind bend during understanding about 'this' is normal. There is enough theory up there so code is here.
- function Case1(){ //for Function(),and also known as stand-alone func. console.log(this) }
Case1(); //> winddow object
In arrow Functions, there is no 'this' keyword. It takes values with the help of surrounding Lexical Scoping and proto.
But in the methods, it works fine.
const obj = { name: 'mani', class: '10', foo() { console.log(this.name); }, };
obj.foo(); //mani
And 'this' behavior in the 'new' constructor.
function New1(a,b){ this.a=a; this.b=b; }
const test = new New1(5,6) //new instantiate object(test) from 'constructor func' console.log(test)
And by saying, don't try to touch classes in js. if you don't want to get confused.Because these topics are confusing.
本文标签: In JavaScriptwhy is the quotthisquot operator inconsistentStack Overflow
版权声明:本文标题:In Javascript, why is the "this" operator inconsistent? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1737066727a1961240.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论