2

Preface

  1. I know the right code for these examples.
  2. What I want to know is why the following examples won't work as expected.

Code

  • With parentheses when calling sayIt function.

    function Fruit(type){
        this.type = type;
        this.taste = "Awful";
        this.thought = sayIt();
    }
    
    function sayIt(){
        return this.taste+" "+ this.type;
    }
    
    window.onload = function (){
        var lemon= new Fruit("Lemon");
        alert(lemon.thought);
    };
    

    This will alert "undefined undefined", why?

  • sayIt function without parentheses.

    function Fruit (type){
        this.type = type;
        this.taste = "Awful";
        this.thought = sayIt;
    }
    
    function sayIt(){
        return this.taste +" "+ this.type;
    }
    
    window.onload = function (){
        var lemon= new Fruit("Lemon");
        alert(lemon.thought);
    };
    

    This will literally write down the function on the alert box, why?

Thank you in advance.

1
  • 1
    these don't work as expected because what you're expecting is not accurate. Commented Jul 15, 2011 at 18:27

7 Answers 7

4

Notes inline, discussion below, references and further reading at the end:

  • With parentheses when calling sayIt function.

    function Fruit(type){
        this.type = type;
        this.taste = "Awful";
        // Here, you're *calling* the `sayIt` function and assigning its
        // return value to `this.thought`. During the call, `this` will
        // refer to the global object (not to the `Fruit` instance).
        this.thought = sayIt();
    }
    
    function sayIt(){
        // If this is called as it is above, `this` is the global object,
        // which is `window` in browsers. Since `window` doesn't have
        // `taste` or `type` properties, this returns "undefined undefined".
        // That's what `this.thought` above receives.
        return this.taste+" "+ this.type;
    }
    
    window.onload = function (){
        var lemon= new Fruit("Lemon");
        // You've said this alerts "undefined undefined", but I think you'll
        // find it just alerts "undefined" (singular). There is no `sayIt`
        // property on the `lemon` instance at all. If you alerted
        // `lemon.thought` instead, you'd see the "undefined undefined" we
        // stored there above.
        alert(lemon.sayIt);
    };
    
  • sayIt function without parentheses.

    function Fruit (type){
        this.type = type;
        this.taste = "Awful";
        // Here you're assigning the `sayIt` function to `this.thought`.
        // Perfectly normal stuff.
        this.thought = sayIt;
    }
    
    function sayIt(){
        return this.taste +" "+ this.type;
    }
    
    window.onload = function (){
        var lemon= new Fruit("Lemon");
        // Here you're also *referring* to the function object, not calling
        // it. (To call a function, you use `()` after it.) So since functions
        // are objects, you're passing an object reference into `alert`.
        // Alert will try to convert that to a string, and the
        // implementation of `toString` on `Function` objects in most
        // environments is to dump out a version of the source code of
        // the function (although this behavior is *not* standardized and
        // some browsers, esp. mobile browsers, don't do it).
        alert(lemon.thought);
    };
    

Key concepts from the above:

  1. Functions are objects. You call a function by using a reference to it followed by (), e.g.:

    x = foo();
    

    means "call foo and assign its return value to x".

    You refer to the function object by just using its name, sans (), e.g.:

    x = foo;
    

    means "assign the function object foo to x. (You could then call it: x().)

  2. Unlike some other languages, in JavaScript, this is defined entirely by how a function is called, not where it's defined. When you call a function via a free variable (e.g., foo()), you're doing nothing to explicitly set the this for the function call, and so this will be the default value, which is the global object (window on browsers).

    You can set what this is in two different ways:

    A. Put the function reference on an object property and call the function via the property's reference to it, e.g.:

    // To put it on whatever `this` is at the moment:
    this.thought = sayIt;
    
    // Or to put it on an object we have in the variable `x`:
    
    x.thought = sayIt;
    

    You'd then call it via the property:

    this.thought();
    x.thought();
    

    Within the function call, this will refer to the object from which you retrieved the property.

    B. Using the function object's intrinsic call or apply functions:

    sayIt.call(lemon);
    

    means "call the sayIt function, making this = lemon within the function call." If you pass further arguments to call, they'll be passed to the function, so:

    sayIt.call(lemon, 1, 2, 3);
    

    means "call sayIt with this = lemon and pass in 1, 2, and 3.

    There's also the apply function, which is just the same thing except you pass the arguments as an array rather than individually:

    // note ------------v-------v---- the square brackets create an array
    sayIt.applyl(lemon, [1, 2, 3]);
    
    // More explicitly:
    var a = [1, 2, 3];
    sayIt.apply(lemon, a);
    

    means "call sayIt with this = lemon and pass in 1, 2, and 3.

I've blogged a bit on these subjects, FWIW:

More to explore:

Sign up to request clarification or add additional context in comments.

Comments

3

I'm assuming there is a typo in the first example and you meant to write alert(lemon.thought()). The reason you're seeing undefined undefined is because this.thought is set to the return value of the sayIt function. In the sayIt function, this refers to the window object and not the Fruit object. Since window doesn't have a taste or type property, you will see undefined undefined.

In the second example (I'll again assume you have a typo and you meant to do alert(lemon.thought())), you this.thought is set to be a reference to the sayIt function, so you're not actually calling it. When you alert a reference to a function, it will print out the source of that function.

BONUS

You can get it to work the way you want if you do this:

this.thought = sayIt.call(this);

This will set the this to point to the Fruit object and now sayIt will return what you want.

In the second example, you will get what you want if you do this:

alert(lemon.thought());

lemon.thought refers to sayIt and the this will be set properly because you are calling a member function of lemon.

The first argument to call (or its friend apply) is for the value of this in the context of that function.

UPDATE

Dan, in the second example even without the change I made, that is if you still have lemon.thought = sayIt; and you say alert(lemon.thought);. You will still get the source of the function because you're not calling the function and passing its result to alert. You're passing the function reference itself to alert, and so it will print the source.

12 Comments

+1 If I had a complete understanding of the rules for where "this" points to at any given point I could die a happy man.
@kekekela this is tricky in Javascript :). You can set it explicitly when you use either apply or call.
Uppss.. yes it is a typo, it should be alert(lemon.thought);
@Vivin: "this is tricky in Javascript :)" this is very simple in JavaScript. I think there are two rules. So simple, it trips people up, I think because they expect it (reasonably, but incorrectly) to even vaguely resemble this in nearly any other language. ;-)
@Vivin: Like so much of the language. :-)
|
2

First code:

EDIT NB: edited to reflect edits in the question

function Fruit(type){
    this.type = type;
    this.taste = "Awful";
    this.thought = sayIt(); // this line invokes sayIt, with global context,
                            // so sets thought to 'undefined undefined'
}

function sayIt() {
    return this.taste+" "+ this.type; // as called, this == window, not the Fruit object
}

window.onload = function() {
    var lemon= new Fruit("Lemon");
    alert(lemon.thought);    // see above
};

Second code:

function Fruit (type){
    this.type = type;
    this.taste = "Awful";
    this.thought = sayIt;
}

function sayIt(){
    return this.taste +" "+ this.type;
}

window.onload = function (){
    var lemon= new Fruit("Lemon");
    alert(lemon.thought);  // doesn't -call- the function, results in .toString() on
                           // the function object
};

1 Comment

+1 "// as called, this == window" I wish I could get my head around how you guys know this, I'm always terrified to use 'this' because I'm worried its going to be pointing to "not what I think it is".
1

I think you have a mistake in the first example. You wrote alert(lemon.sayIt); where it should be alert(lemon.thought);. Anyway...

With parentheses when calling sayIt function. This will alert "undefined undefined", why?

Because when you execute this.thought = sayIt();, you are assigning the return value of sayIt to this.thought. When you call sayIt(), then this inside the function will refer to the global object which is window is browser. And window.taste and window.type are not defined. Hence this.thought will have string "undefined undefined" assigned to it.

sayIt function without parentheses. This will literally write down the function on the alert box, why?

In this case you are assigning a reference to the function itself to this.tought. The string representation of a function is the code itself. Now you can call the function via lemon.tought(). If you do so, this will refer to the lemon object and the output will be as expected.

So, call the function: alert(lemon.tought()).

I suggest you read about

Comments

0

Based on your code, the "lemon" object has the properties "type", "taste", and "thought".

alert(lemon.sayIt);

This line alerts the value of the "sayIt" property on "lemon", converted to a String. Since the "lemon" object doesn't have a "sayIt" property, it converts the value undefined to a string and displays it.

alert(lemon.thought);

This line alerts the value of the "thought" property on "lemon", converted to a String. Since the "thought" property is a function, the string conversion displays the text of the function.

What you probably want to do is call the function, and display its return value: alert(lemon.thought());

Comments

0
  1. The function sayIt is defined after it is called. Moving the definition of sayIt above the definition of fruit would fix.

  2. You're alerting the definition of a function and not the return value from calling that function.

Comments

0

sayIt is not a function of the Fruit object - it's a function of the window object.

Fruit.thought is a function, or a "function pointer" as it is assigned to the window.sayIt function.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.