2

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?

AlicanC
  • 863
  • 1
  • 8
  • 12

5 Answers5

5

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);
};
hugomg
  • 68,213
  • 24
  • 160
  • 246
  • I actually want to use the class like the jQuery object (without instantiating): `Test('Do something'); Test.default = 500; Test('Do something with 500.')` – AlicanC Nov 12 '11 at 03:50
  • 2
    @AlicanC heres a post by John Resig on the topic. http://ejohn.org/blog/simple-class-instantiation/ – danem Nov 12 '11 at 04:03
  • @Pete that post is nice but it's from 2007. I can't find a single usage of `arguments.calee` in jQuery 1.7's source code now. Maybe they have moved to a new method? I can't seem to figure it out. – AlicanC Nov 12 '11 at 04:53
  • 2
    @AlicanC - From what I understand, what you're really looking for is a pattern like the one I described in my answer (i.e. `var Test = function Temp() { return Temp.stuff; };`). It's fast, and it's elegant. Plus, even if people later change the code like `Test = 5;`, it will not break your code as the internal name of the function (i.e. `Temp`), still points to the function itself. It can't be modified from outside. Hope this helps. Cheers. – Aadit M Shah Nov 13 '11 at 05:07
5

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:

  1. The instance of the constructor created when the function call is prefixed with the new keyword. (e.g. var foo = new Foo(); //the this in Foo points to foo [for the sake of explanation]).
  2. The object passed to the 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.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
3

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.

http://jsfiddle.net/aYh5y/1/

Ray Toal
  • 86,166
  • 18
  • 182
  • 232
3

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
  • What would be the difference between using arguments.callee and just repeating the function name directly? – hugomg Nov 12 '11 at 04:28
  • 1
    @missingno Preference? :) I have used both, depending on what I wanted to do and how "it fit" with the rest of the code. I cannot come up with a "universal" advantage or disadvantage either way off the top my head, but I would not be surprised if I was overlooking something. Just be careful to not to be "too clever"... Note that is is *different* from `var Test = function () { return Test.stuff }` in a global-context because `Test` might really be `window.Test`, a property, and not a closed-over variable. Someone might break things later with `Test = function(){}` ... never know :) –  Nov 12 '11 at 04:32
  • I don't like to use `arguments.callee`. Neither do I like to use `var Test = function () { return Test.stuff; };`. Both are slow methods to access data. This is because `callee` is a method of `arguments`, and the interpreter spends more time accessing it. Similarly, `Test` does not exist within the local scope. Hence the interpreter must travel upwards to locate it in a parent scope. Instead, I prefer the following method: `var Test = function Temp() { return Temp.stuff; };`. Here `Temp` is an alias for `Test` and it exists within the local scope. It's a faster and a more elegant solution. ;) – Aadit M Shah Nov 13 '11 at 04:43
  • @AaditMShah Shah Claims of "slow methods" rejected: *It Just Doesn't Matter*. List to Donald Knuth -- he *is* right about this topic. As far as "speed": a smart JavaScript engine can entirely eliminate scope traversal as it can be eliminated while preserving all the required semantics and, even with a dumb JavaScript engine, the *relative cost is insignificant* for most practical purposed. Willing to accept jsperf performance test-cases, bearing in mind they are micro-benchmarks. –  Nov 13 '11 at 05:35
  • @pst - [Speed Up Your Javascript - GoogleTechTalks - Nicholas C. Zakas](http://www.youtube.com/watch?v=mHtdZgou0qU "Speed Up Your JavaScript"). BTW what does Donald Knuth have to do with this? – Aadit M Shah Nov 13 '11 at 05:42
  • @pst - Here you are assuming that the JavaScript engine is __smart__. What if the code is being executed on IE 6? Did you take that into consideration? All I'm trying to say is that this __my__ preferred method to tackle such a problem. In any case, it's still faster than the one you described; and if you put it in a really long loop then the difference does become significant. It's a best practice which makes sense to me. I already described why. It's upto you to accept it or not. – Aadit M Shah Nov 13 '11 at 05:47
  • @AaditMShah Unfortunately your method appears flawed. Try this in both FF and IE: `var x = function y () { return y }; y = 42; x()`. In FF8 it evaluates to a function-object, in IE9 it evaluates to 42. I believe IE9 is "more correct", as I was unaware the name of the function was added to the local-scope of the function implicitly. There has historically been issues with function-declarations vs. function-expressions. –  Nov 13 '11 at 05:48
  • [As far as Knuth](http://scalibq.wordpress.com/2011/07/20/premature-design-is-the-root-of-all-evil/) -- I find the *end* of that quote excerpt better than the start. –  Nov 13 '11 at 05:55
  • @pst - IE9 evaluates it incorrectly. The identifier `y` must only exist within the local scope of the function. Thus the variable `x` should be an instance of `Function` and not `42`. The global and local variables `y` should be separate. In any case Mozilla's implementation is always "more correct" than IE9. See this [answer](http://stackoverflow.com/questions/1634268/explain-javascripts-encapsulated-anonymous-function-syntax/1634321#1634321 "Explain JavaScript's encapsulated anonymous function syntax") for more details. – Aadit M Shah Nov 13 '11 at 06:00
  • I read the article relating to Donald Knuth, and I agree that premature optimization in most cases is a waste of time. However, in this context it's not really the case of premature optimization. It's just about good coding practices. You don't really waste your time by using a certain pattern. You may use any pattern that suits you. I feel that this is a better pattern and hence I use it. It's a kind of ad-hoc optimization. One which comes as a side effect to your actual code. You're not really trying to squeeze the most performance, just following a good practice. Plus, it's simpler to read. – Aadit M Shah Nov 13 '11 at 06:08
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/4946/discussion-between-aadit-m-shah-and-pst) – Aadit M Shah Nov 13 '11 at 06:10
  • @AaditMShah While the linked post does discuss the differences between function-declarations and function-expressions it does not discuss the behavior of the function *name* inside function body as an *identifier* for said function-expression. The example `var Test = function Temp() { return Temp.stuff; };` relies upon this behavior. As shown in the previous examples, assuming a top-level scope, IE is semantically the same as `var Test = function Temp () { return window.Temp(); }`, while FF is not. It would be interesting to see where, in the specification FF (or IE) is "correcter". –  Nov 13 '11 at 07:47
  • @AaditMShah Proving that IE is incorrect does not validate the aforementioned approach; only proving that FF is correct does. Additionally, for the approach "to be better" than the closure (or callee) samples requires that the function *name* is indeed bound as a *local variable* within said functions scope, per specification. –  Nov 13 '11 at 07:50
  • @pst - See the 10th page of this [PDF](http://wiki.ecmascript.org/lib/exe/fetch.php?id=resources:resources&cache=cache&media=resources:jscriptdeviationsfromes3.pdf) from ECMAScript - section 2.7 (Function Expressions). That should answer all your questions. It clearly shows that IE does not play well with the standards. – Aadit M Shah Nov 13 '11 at 08:26
  • @pst - Every experienced web developer knows that any information which has its sources in IE is inadmissible in a court of law. – Aadit M Shah Nov 13 '11 at 08:36
  • @AaditMShah No, section 2.7 does not explain why your code works or is valid. Your code asserts that in the case of `var Test = function Temp () { return Temp.bar }`, that `Temp` is an implicitly-created local-scoped variable (which is required to make the assertion that it doesn't use a *chained* [[scope]]; IE clearly uses a *chained* [[scope]], as demonstrated). Again. Proving IE "wrong" does not make FF "correct". As far as I can tell, they are *both* wrong and your approach is neither *valid per specification* nor *consistent across browsers*. –  Nov 13 '11 at 20:26
  • @AaditMShah To prove this alternative is "better" over closures would require **THE SPECIFICATION** to explicitly state that a function *name* is introduced into the [[scope]] of the function as a local *variable* bound to said function-object. Short of this the code is simply relying upon JS implementation quirks and, as demonstrated, may lead to "unexpected" behavior if `Temp` is changed outside the function. –  Nov 13 '11 at 20:33
  • Where is the "arguments.callee is deprecated" warning? – Raynos Nov 13 '11 at 20:52
  • @Raynos I didn't know it was deprecated -- a link and I'll add it as appropriate :) Or ... feel free to edit away ^^ –  Nov 13 '11 at 21:23
  • @pst - Click on [this link](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf "ECMA-262 Edition 5.1") to open the official ECMAScript specification. Search for `FunctionExpression : function Identifier ( FormalParameterListopt ) { FunctionBody }`. It's on the 110th page of the PDF, page 98 of the official spec. Read the points 1 to 6. It's specified that in a named function expression's execution context's environment record there is an immutable binding called the String value of `Identifier` which is the function `closure` itself. The expression returns `closure`. =) – Aadit M Shah Nov 15 '11 at 03:54
  • Wow it took me a lot of time to find that, but it was worth proving that IE is wrong. =D – Aadit M Shah Nov 15 '11 at 03:56
0

The stuff property is assigned to the function, but read from the global this ... The two are seperate entities

Rich O'Kelly
  • 41,274
  • 9
  • 83
  • 114