开发者

How do Javascript objects work?

开发者 https://www.devze.com 2023-03-20 10:37 出处:网络
Preface I know the right code for these examples. What I want to know is why the following examples won\'t work as expecte开发者_如何学God.

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 expecte开发者_如何学God.

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.


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:

  • Mythical methods
  • You must remember this
  • Anonymouses anonymous (talks more about function references and assigning them)

More to explore:

  • The ECMAScript specification (yes, really)
  • MDC's JavaScript pages
  • Crockford's JavaScript pages (advanced, read and understand all of the above first)


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.


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
};


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

  • Functions
  • Working with Objects


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());


  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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消