Reputation: 893
When I run this code:
var Test = function() {
return this.stuff;
};
Test.stuff = 'Neat!';
document.write(Test() || 'Not neat.');
Why do I get 'Not neat.'? Why can't I access the stuff property using this.stuff
?
Upvotes: 2
Views: 230
Reputation:
While other people have posted why this occurs (the understanding of this
is incorrect), here is one solution which will work reliably.
Update: As Raynos noted, when using strict mode functions in ECMAScript 5th Edition, it is invalid to use arguments.callee
(it will throw a TypeError). Thus caution should be exercised if using this approach. (When using a [correct] ECMAScript 5th edition engine, there is no reason to use arguments.callee
over the name given to the function which is bound to the new scope -- see the end of the answer.)
var Test = function() {
// arguments.callee is the current function, if any
return arguments.callee.stuff
}
Test.stuff = 'Neat!'
alert(Test() || 'Not neat.') // Neat!
Another is to use a closure:
var Test = (function () {
function fn () {
// closure over fn, which names this function-object
return fn.stuff
}
fn.stuff = 'Neat!' // here
return fn // do not combine with function declaration!
})()
Test.stuff = 'Neat!' // or here
alert(Test() || 'Not neat.') // Neat!
Or, a closure over a variable directly:
var Test = (function () {
var stuff = 'Neat!'
return function () {
// variable closure, no property
return stuff
}
})()
alert(Test() || 'Not neat.') // Neat!
Or... so many ways.
Happy coding.
Another approach that was pointed out by Aadit M Shah is to use the function identifier to refer to the current function:
var Test = function Temp () {
return Temp.stuff
}
Test.stuff = 'Neat!'
alert(Test() || 'Not neat.') // Neat! (But see below.)
As Aadit points out, this is valid, as per the ECMAScript 5th edition specification, page 99:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
However, some browsers (at least IE9) implements this incorrectly (and I am not sure if the above noted behavior is well-defined in the 3rd edition). Consider:
var x = function y () { return y }; y = 42; x();
In IE9 it will yield 42 and in FF8 it will yield the function-object. IE9 is incorrect here as it introduces y
as a variable in the enclosing scope, which is forbidden by ECMAScript for function expressions. Here is an in-context example of how this incorrect implementation can lead to different results:
var Test = function Temp () {
return Temp.stuff
}
Test.stuff = "Neat!"
Temp = {}
alert(Test() || 'Not neat.') // 'Not neat.' in IE9, 'Neat!' in FF8
Upvotes: 3
Reputation: 69944
Class methods and variables go on the prototype property:
Test.prototype.stuff = 'Neat!'
Constructor functions (I'm assuming this is what you want, given the capital case and the this
) should be invoked with the new
operator:
new Test()
and they should not return a value (you should instead use the default that returns this
)
function Test(){
this.instanceVariable = 17;
//no return!
}
As for your real need , you can just access the function and its properties directly then
function Test(){
return Test.stuff;
}
However I am not a big fan of abusing functions for namespaces like that. I prefer to have a namespace object for doing things
//in a real case I would probably use the module pattern for private variables
//but whatever...
var Namespace = {};
Namespace.stuff = 'Neat!';
Namespace.F = function(){
console.log(Namespace.stuff);
};
Upvotes: 5
Reputation: 74204
This is what you have done:
var Test = function() { //Test is a Function object
return this.stuff; //this is a pointer to an object, not Test
};
Test.stuff = 'Neat!'; //Add a property to Test
document.write(Test() || 'Not neat.'); //this has no property stuff
Change the last line of your code to:
document.write(Test.call(Test) || 'Not neat.'); //this now points to Test
The reason your code didn't work is because the this
pointer points:
new
keyword. (e.g. var foo = new Foo(); //the this in Foo points to foo [for the sake of explanation]
).call
and apply
functions as the first parameter.What you want to do instead is something like:
var Test = function Temp() { //Test is a Function object, alias Temp
return Temp.stuff; //Temp is the same as Test, only locally
};
Test.stuff = 'Neat!'; //Add a property to Test
document.write(Test() || 'Not neat.'); //writes Neat!
Upvote this answer if you liked it. Cheers.
Upvotes: 5
Reputation: 88388
You called Test
from the global context, so this
refers to the global object, so this.stuff
refers to the global variable stuff
which is undefined
which is falsy. This is why you saw "Not neat."
You can make it show Neat!
like this:
See http://jsfiddle.net/aYh5y/
window.stuff = 'Neat!';
var Test = function() {
return this.stuff;
};
alert(Test() || 'Not neat.');
ADDENDUM
Here is a way you can make this
work as an enclosing object:
var Test = function () {
return this.stuff;
};
var example = {
g: Test,
stuff: "Pretty neat"
};
alert(example.g() || 'Not neat.');
Here we're calling Test
through a target object.
Upvotes: 3
Reputation: 41767
The stuff property is assigned to the function, but read from the global this ... The two are seperate entities
Upvotes: 0