JS, “objects”, this and that. Coming from python T

2019-07-24 05:18发布

问题:

Heres a bit of cleaned up code from a picture gallery im writing to learn js. I create the gallery as an object but at some point i lose track of what "this" points to. It doesnt make sense to me what happens at the bottom (look at the comments). Can someone please explain?

function Gallery(parentID)
{
        // ...
    this.showDiv = document.createElement("div");
        // ...
    this.show = function ()
        {
        document.body.appendChild(this.showDiv); //will be given css absolute position to "pop up"
        this.showDiv.innerHTML = '<img class="displayImage" src="' + this.picList[this.showIndex] + '">'; //fill with image
        this.showDiv.focus();
        this.showDiv.onclick = this.hide;
        }   

    this.hide = function ()
        {
        alert(this.innerHTML); // <= WHY DOES THIS SHOW THE INNERHTML CONTENTS OF this.showDiv??????
            //alert(this.showDiv.innerHTML); // <= shouldnt this be correct?
        this.parentNode.removeChild(this); //doesnt work
        }
}

Let me know if i cleaned up some code that might have affected the results and ill fill it in.

Thanks.

回答1:

    this.showDiv.onclick = this.hide;

This line is the problem. It doesn’t work like in Python; this.hide is not a bound method. It’s just the hide function, not bound to any particular this.

One way to fix it is:

    this.showDiv.onclick = this.hide.bind(this);

But the .bind() method of functions is a rather new standard; it isn’t in all old browsers. So you might want to roll your own instead:

function bindMethod(object, func) {
    return function () { return func.apply(object, arguments); };
}

this.showDiv.onclick = bindMethod(this, this.hide);

(Generally this is kind of strangely behaved in JavaScript; the main thing to remember is that this always refers to the innermost function’s this, which could be whatever the caller wants it to be. Sometimes you see JS code do things like var self = this; to give a particular function’s this a name that can be used in nested functions; in Python, of course, that is just sort of the way things work automatically.)



回答2:

This refers to the object that called the function. So when you say this.showDiv.onclick, showDiv is what is calling the function. Therefore this.innerHTML actually means this.showDiv.innerHTML



回答3:

If you insist on using a constructor function, rather than a factory, then keep this in mind --

If you have a method (or any function you call as a method, and not as a constructor), and that method references this, that this statement gets resolved at the instant the function is called rather than when it is defined.

function sayName () { console.log(this.name); }

var bob = { name : "Bob" },
      doug = { name : "Doug" };

bob.greet = sayName;
doug.greet = sayName;

In a strict class-based language, there'd be all kinds of errors, here.

But through the magic of late-binding (deciding what this means at the last possible second, during execution), it goes off without a hitch.

There is one hitch, however: Functions inside of functions. You want this to point to the this of the outer method. It doesn't. In old (current) JS, it points at window. In future JS, it points at nothing.

function sayNameAndAge () {
   var outerThis = this,
         name = this.name;

   function sayAge () { console.log(outerThis.age); }

     console.log(name);
     sayAge();
 }

Assuming Bob has both a name and an age, now, you can assign the function like last time, and it will work. The function will see that bob was in front of the dot, and thus the subject of this (note: not really how it works, but how it appears to work, one function deep). outerThis will save the reference for the inner function to use.

Another option is:

sayNameAndAge.call(bob);

call is a method that functions have (did I mention that functions are objects), which lets you specify this for that particular execution, rather than giving another object a method (a reference, really).

bind works just like call, except it returns a function where the this is permanently set to value you passed in. And bind isn't in OldIE.

One other major point of reference: When you use event-listeners, this points to the element you put the listener on.

You can delegate listeners (attaching one listener to a <ul>, that listens for any click on any of its <li>, for example. In that instance, this would be the <ul>, because it would be the object which the listener was attached to, when it was triggered.

Hope that makes things a little clearer.