Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - this of this

Tags:

javascript

oop

String.prototype.foo = {};
String.prototype.foo.bar = function() {
    //How can you reference the "grandparent" string?
    console.log(this.parent.parent); //obviously, doesn't exist
}

As in, "Hello, Nurse!".foo.bar() would log "Hello, Nurse!".

Will it make a difference if there's control over foo?

Edit: There, foo is defined.

Edit2: Fine, instead of this.this.this, this.parent.parent. Of course parent doesn't exist, but hopefully now the semantics won't get in the way.

Edit3: There isn't a specific case. The provided details are pretty much all I got: There's an object foo, part of a prototype. foo.bar is a method of foo, and is supposed to access its grandparent. That's it. Nothing else. That's all the information I have.

Edit4: Resolved. Based on the answer provided (and some second-hand help from Douglas Crockford):

String.prototype.foo = function() {
    var that = this;
    return {
        bar : function() {
            console.log(that.valueOf());
        }
    }
}
//Called:
"Hello, Nurse!".foo().bar();
like image 283
Zirak Avatar asked Apr 21 '11 23:04

Zirak


2 Answers

The only way this can be done, is to turn foo() into a function. Think of it as initialising the foo namespace for a particular string:

String.prototype.foo = function () {
    var str = String(this);
    var o = Object(this)
    o.bar = function () {
         console.log(str);
    };
    return o;
};

Then you can use:

"foobar".foo().bar(); // logs "foobar"

Or if we rename foo and bar into something more exciting:

"Hello!".console().log(); // logs "Hello!"

Why is it so complicated?

Each function is called with a particular context that is a single object. Whether it is called with a.b() or a.b.c.d() doesn't matter - it is given the object immediately to the left of the function call as its context. So the context for a.b() would be a, and the context for a.b.c.d() is c. The keyword this references the context. Because c is just an object (not a running function) it has no context, and it has no concept of this, so this.this makes no sense.

Therefore, it is not possible to generically access the so-called "parent". Juan's answer gives a good conceptual explanation why. However, if what you want to achieve is namespacing in prototype functions, then you can do this by returning an augmented object from foo.

Notice I also had to convert this into an Object above. This is because you can't attach properties to primitive values like strings. var str = "foo"; str.bar = 1 will work, but only because JS automatically converts "foo" into an object. However, since str references the primitive, not the automatically created object, the object is then immediately discarded, and we lose bar.

like image 161
David Tang Avatar answered Oct 07 '22 04:10

David Tang


There is no way for an object to know what object it's a property of. The same object (in your case a function) could be attached to multiple objects. Here's an example:

var myObj = {};
var objA = {prop: myObj};
var objB = {nother: myObj}

If given a reference to myObj, how could you possibly know which parent object you're referring to? In one case, it's nothing, another case, it's objA, the last case, it's a 'child' object of objB. If you explain why you'd like this behavior, we can help you solve the problem at hand. But the answer to the question is that you CAN'T do it.

like image 3
Juan Mendes Avatar answered Oct 07 '22 03:10

Juan Mendes