开发者

"privileged" method calling member method issue

开发者 https://www.devze.com 2023-03-12 07:45 出处:网络
Consider the following code: function Dog() { this.foo = function() {}; this.walk = function() { if(canWalk())

Consider the following code:

function Dog()
{
    this.foo = function() {};

    this.walk = function()
    {
        if(canWalk())
        {
            alert('walking');
            return;
        }

        alert('I have no legs!');
    }

    canWalk = function()
    {
        this.foo();
        return false;
    }
}

var instan开发者_如何学Pythonce = new Dog();
instance.walk();

There is a bug in that code with the "privileged" canWalk() method assuming the this pointer points to the instance of Dog. It actually appears to be pointing to the global object which is confusing me! I've read that due to closures I can grab a reference to this in the scope of the constructor then use that reference in my "privileged" method but that seems like a hack.

I just barely grasp the behavior of the this pointer in various contexts. I understand that a method that is "attached" to an object will receive a this pointing to the attached object. For example, foo.doStuff() would have a this pointing to the foo instance.

What's troubling is that while I think I'm being clever and creating "privileged" methods on my OBJECT, it appears that I'm ACTUALLY dumping functions into global scope! Perhaps there was a global method called init() (quite possible) from another library or file not my own creation. If I then defined a nice little encapsulated object with a "privileged" method called init() I would be replacing the other global init() method.

My question is, what is the best practice for creating "privileged" methods (and fields for that matter) that are scoped to the object they are intended to belong to? What other approach to my sample object would provide private or privileged methods while also being properly scoped. I'm not looking for obscure solutions that meet the requirements but rather what the best practice is.


Don't use this. Put var self = this; at the top of your Dog function and use self. That way you will always have a 'safe' reference to the Dog.

In your example this should point to the Dog though unless I'm missing something as well.

As for your privileged method, you forgot to define the variable. Add var canWalk;

There is another way: closures. I will give an example:

function create_dog() {
    function canWalk() {
        return false;
    }
    function walk() {
        if (canWalk()) {
            alert("walking");
            return;
        }
        alert("I have no hands!");
    }
    return {
        "walk": walk // points to the function walk()
    }; // this the API of dog, like public functions (or properties)
}
var dog = create_dog();
dog.walk();

Now you don't need this or new. Additionally you could do this:

function create_dog() {
    function canWalk() {
        return false;
    }
    function walk() {
        if (canWalk()) {
            alert("walking");
            return;
        }
        alert("I have no hands!");
    }
    var dog = {
        "walk": walk
    };
    return dog;
}
var dog = create_dog();
dog.walk();

So you can have references to the dog in your priveledges functions. I would advise the closure approach if you're not going to be using prototyping.

By the way. To avoid confusion:

function my_func() {}

and

var my_func = function () {};

Are equivalent. The latter can be useful if you have a circular reference between two functions and you want to enforce define-before-use.


First, you need a var in front of your canWalk. That way, the canWalk function is confined to the scope of dog(), rather than being an implicit global.

this points to the "owner" of the function. When using the new operator, the function creates its own scope. So, within the body of dog(), this refers to the dog instance object. Inside your walk function, this also refers to the dog instance, because you're setting the walk function to an instance of dog (this.walk = function () { ... }). However, in canWalk, there is no owner, so this points to the global object, window.

I realize that my explanation might be confusing, so here's the code annotation version of my explanation:

var obj = {
    'f': function () {
        // this === obj, because obj "owns" this function
        // In other words, this function is callable by obj.f()
    },
    'o': {
         'f': function () {
             // this === obj.o, because obj.o "owns" this function
             // In other words, this function is callable by obj.o.f()
         }
    }
};

// The new operator essentially sets "this" within the 
// function to an empty object and returns that object
var instance = new Dog();

function Dog() {
    // this === instance, because of the "new" operator

    // For demonstration
    var self = this;

    this.walk = function () {
        // this === instance === self, because self/instance "owns" this function
        // In other words, this function is callable by self.walk()
    };

    var canWalk = function () {
        // What's the owner of this object? Nothing,
        // so it defaults to the global object, window
        // this === window

        // Instead, we can access the Dog instance by
        // using the "self" variable we set earlier.
        // self === instance
    };
}

Hope that clears things up.


As you've gathered, calling canWalk straight up like that means that the 'this' keyword is set to the global object for that function call.

If you want to run the call under the context of the current dog you can explicitly do so like this:

canWalk.call(this)

0

精彩评论

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

关注公众号