The right way to 'call' a class attribute inside the same class

Erik python at lucidity.plus.com
Sun Dec 18 17:52:09 EST 2016


NOTE: If you found this message by searching for help on how Python 
works, be aware that it's discussing how JavaScript works, not Python! 
Look elsewhere :)

Chris, this isn't directed at you (I think you get it) - just following 
up with some detail for anyone who might discover this sub-thread when 
searching in future.

On 18/12/16 01:21, Chris Angelico wrote:
> On Sun, Dec 18, 2016 at 12:01 PM, Erik <python at lucidity.plus.com> wrote:
>> I wish I could find the resource I originally learned this stuff from,
>> because it's quite enlightening and I'd like to link to it here

> Sounds like how Michael Schwern introduces his "Git for Ages 4 and Up"
> talk

Just in case anyone is remotely interested, I've found a reasonable link 
(but it's not the one I was looking for).

I realise this is off-topic for a Python list, but it has come up 
because of a Python question so I thought why not ... (I wish someone 
had explained this to me in roughly-Python terms when I first had to 
deal with JS ;)).

In short, AIUI, all JS functions have an implicit initial parameter 
which is named 'this'. What that parameter is bound to depends on the 
context of the call to that function object. In Python the callable is 
of a particular type (method, function, generator) but on JS (for the 
purposes of this discussion (pun intended ;)) they are all equal and 
it's the calling context that determines what happens.

A JS function, whether declared with "function Foo(a, b, c) {}" or as an 
expression "function (a, b, c) {}" has an implicit initial parameter 
which is bound to the variable 'this' (so they are really "function 
Foo(this, a, b, c) {}", "function (this, a, b, c) {}").

The value of that initial first parameter is determined by how they are 
called. There are three main ways:

1) Method call:
    "obj.foo(1, 2, 3)" is syntactic sugar for "obj.foo(obj, 1, 2, 3)".

2) Function call:
    "foo(1, 2, 3)" is syntactic sugar for "foo(GLOBAL_OBJECT, 1, 2, 3)" 
where "GLOBAL_OBJECT" is whatever the execution environment defines as 
being the global object. In a browser, I think it's "window", in the JS 
strict mode, I think it's "undefined" (i.e., no object), in Node.js I 
think it might be something else entirely.

3) Constructor call:
    "new foo(1, 2, 3)" is syntactic sugar for "foo(NEWOBJ, 1, 2, 3)" 
where NEWOBJ is a newly allocated, empty object.

The other way of invoking a function object is via its "call" method 
where the initial 'this' parameter can be explicitly given - 
"foo.call(SPAM, 1, 2, 3)" - which is how to implement bound methods and 
that sort of thing.


The main area of confusion seems to be when passing function expressions 
(closures) as a callback to another function:

function Foo() {
   ham(this.a, this.b, function (x) { this.frobnicate(x); });
}

Let's assume this is called with "obj.Foo();". In the function itself, 
'this', is bound to 'obj'. However, in the body of the (anonymous) 
function expression/closure (which sort of looks to be at the same scope 
as everything else) 'this' is bound according to the rules above being 
applied to at the point it is called - the object it is bound to almost 
certainly doesn't have the "frobnicate" method expected.

This is why the following works:

function Foo() {
   _this = this;
   ham(this.a, this.b, function (x) { _this.frobnicate(x); });
}

The closure now knows how to address the 'this' object for the original 
method even though it is called later by whatever 'ham()' does. Using a 
bound function as the callback works in the same way.



Anyway, off-topic as I said, but if it helps other Pythoneers get to 
grips with some of the weird JS semantics, it's all good :)



The link I *did* find (which probably has a bit more depth) is:

https://rainsoft.io/gentle-explanation-of-this-in-javascript/


Cheers, E.


More information about the Python-list mailing list