简体   繁体   中英

JavaScript class notation and the 'this' keyword

I'm aware there have been a few other questions on this topic, but none of them seem to give a conclusive answer.

I'm building a HTML/CSS/JS mobile app, and have been trying the following style of notation to define some classes:

Style A

var Thing = (function ()
{
    var _instance;

    var _firstName;
    var _lastName;

    function Thing(firstName, lastName)
    {
        _instance = this;

        _firstName = firstName;
        _lastName = lastName;
    }

    Thing.prototype.getMyName = function ()
    {
        return _firstName + " " + _lastName;
    }

    Thing.prototype.speak = function ()
    {
        return ("My name is " + _instance.getMyName());
    }

    return Thing;
}());

The advantages of this are:

  • Member variables are encapsulated and can be referred to directly (eg without the this prefix).
  • I can use the _instance variable and therefore avoid ambiguity around the identity of this .
  • The notation is reasonably clean and readable.

I also gave the following alternatives a try:

Style B

function Thing(firstName, lastName)
{
    this._firstName = firstName;
    this._lastName = lastName;
}

Thing.prototype.getMyName = function()
{
    return this._firstName + " " + this._lastName;
};

Thing.prototype.speak = function()
{
    return "My name is " + this.getMyName();
};

Style C

class Thing
{
    constructor (firstName, lastName)
    {
        this._firstName = firstName;
        this._lastName = lastName;
    }

    getMyName ()
    {
        return this._firstName + " " + this._lastName;
    }

    speak ()
    {
        return ("My name is " + this.getMyName());
    }
}

But despite their advantages, I have found B and C difficult to work with because of problems associated with the this keyword; that is, depending on the context of the caller this can refer to different things within the class methods. Furthermore in both these cases, using an _instance variable as I have in A is not possible because all members need to prefixed with this. .

However, as pointed out in the comments, Style A does not work when multiple instances of the class are created.

What's the best way to write such classes but avoid problems with this ?

If you want to avoid prototype methods with late-bound this at all costs, you can use the following style in ES6:

class Thing {
    constructor(firstName, lastName) {
        this.getMyName = () => firstName + " " + lastName;
        this.speak = () => "My name is " + this.getMyName();
    }
}

(you can also use a function declaration, but the class has the advantage that it prevents calls without new automatically)

You are creating a closure and then sharing the closure among instantiated objects by the Thing constructor. It will not work as intended.

var elton = new Thing("Elton", "Johnnavartangula");
elton.getMyName(); // <- "Elton Johnnavartangula"
var fenry = new Thing("Fenry", "Honda");
elton.speak(); // <- "My name is Fenry Honda"

sharing privates among instantiated objects is another topic and can be done in several ways like in one of my previous answer or like

function Source(){
var priv = "secret";
return {gp : function(){return priv},
        sp : function(v){priv = v}
       }
}
sharedProto = Source(); // priv is now under closure to be shared
var p1 = Object.create(sharedProto); // sharedProto becomes o1.__proto__
var p2 = Object.create(sharedProto); // sharedProto becomes o2.__proto__

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM