3

I'm trying to write an application in JavaScript, which I've been using for bits of scripting in web pages for years, and I find my understanding of scope and object orientation is coming up somewhat short. My background is mostly in object oriented languages like C# and Ruby so JavaScript's weird pseudo object-oriented functional approach is confusing me no end.

What I'm having trouble with is this and why I seem to always need it in every reference to anything in my class. It just seems to result in an inordinate amount of typing in order to write much that is useful in JS and I can't help but feel I must be doing it wrong somehow:

function MyClass()
{ 
  var a=1;
  this.b = 2;

  this.internalMethod= function()
  {
   console.log("a is: "+a); // returns "a is: 1"
   // console.log("b is: "+b); - this fails because b is undefined
   console.log("this.a is: "+this.a); // returns "this.a is: undefined" but doesn't crash.
   console.log("this.b is: "+this.b); // returns "this.b is: 2"
 }

}

MyClass.prototype.externalMethod = function()
{
//     console.log("a is: "+a); - fails
//     console.log("b is: "+b); - fails
       console.log("this.a is: "+this.a);  // "this.a is: undefined"
       console.log("this.b is: "+this.b);  // "this.b is: 2"
}

var m = new MyClass();
m.internalMethod();
m.externalMethod();

What I am understanding from this is that if I am adding a new public method through the class.prototype approach, I only have access to properties that are defined with this.

If I create an internal method in the class definition function I have access to any var values in the class itself and I can access this.property as long as I include the this.

Is this correct? Do I always have to include an explicit object reference to access properties in any functions that use the prototype method? If so, should I be declaring all my functions within the parent class function rather than using the class.prototype pattern for adding them?

In essence I am looking for a standard approach to managing variable scope when writing object oriented Javascript. Most of the articles I can find on this topic are either basic introductions to object orientation or seem to gloss over this area. A lot of my classes are fairly data intensive and having to make calls in the form this.myMethod( this.firstArray, this.secondArray, this.thirdArray, this.fourthArray ) seems like a long cut and impedes the readability of code.

(I am aware of the var that=this trick for avoiding caller scope problems but I didn't want to mess up my examples with it as as far as I know it's not really pertinent to my question.)

4

5 回答 5

2

What I am understanding from this is that if I am adding a new public method through the class.prototype approach, I only have access to properties that are defined with this.

If I create an internal method in the class definition function I have access to any var values in the class itself and I can access this.property as long as I include the this.

There are no such things as "internal" and "external" properties, only "own" and "inherited" properties. In your example, internalMethod is directly assigned to the new object, while externalMethod is inherited from the prototype. They are not different or special in any way. Any two functions that are defined in different scopes are always different.

internalMethod has access to the local variables in the constructor because it is a closure.

Do I always have to include an explicit object reference to access properties in any functions that use the prototype method?

It's important to understand that there is no implicit connection between functions and the objects "they are assigned" to. Functions are independent entities.

The connection is only determined at runtime, by setting this accordingly. So yes, you will need this, for every function, not only those assigned to the prototype. Learn more about this.

If so, should I be declaring all my functions within the parent class function rather than using the class.prototype pattern for adding them?

This is extensively discussed here:

In essence I am looking for a standard approach to managing variable scope when writing object oriented Javascript.

My subjective advice:

Keep it simple. Don't try to simulate private variables/methods through local variables and closures. Initialize and assign instance-specific data in the constructor, and assign anything that should be shared between instances (such as methods) to the prototype. Use a naming convention, such as this.privateProperty_ to indicate properties that should be accessed by external code.

A lot of my classes are fairly data intensive and having to make calls in the form this.myMethod( this.firstArray, this.secondArray, this.thirdArray, this.fourthArray ) seems like a long cut and impedes the readability of code.

In this example, this inside myMethod will refer to the object itself, so you can access this.firstArray, this.secondArray, etc, just in like that inside the function. You don t have to pass them.

于 2013-02-28T14:20:29.933 回答
2
function MyClass()
{ 
  var a=1;
  this.b = 2;

  this.internalMethod= function()
  {
   console.log("a is: "+a); // returns "a is: 1"
   // console.log("b is: "+b); - this fails because b is undefined
   console.log("this.a is: "+this.a); // returns "this.a is: undefined" but doesn't crash.
   console.log("this.b is: "+this.b); // returns "this.b is: 2"
 }

}

In that code, the first console.log will return the right value, because you declared the variable a and then declared the function, which grabs it's environment and saves it all into something called closure (this explanation might be a bit off), so what happens here, is that when you call internalMethod, that function in it's environment also has the definition of a. Which might be confusing, because when you do your second console.log your see "undefined", which is because the a variable is not an attribute of this, but rather just a global variable to the scope of your internalMethod.

MyClass.prototype.externalMethod = function()
{
//     console.log("a is: "+a); - fails
//     console.log("b is: "+b); - fails
       console.log("this.a is: "+this.a);  // "this.a is: undefined"
       console.log("this.b is: "+this.b);  // "this.b is: 2"
}

In that code, a is not defined, becuase it didn't exist when you declared your method, but this.b does, because is part of the attributes of MyClass, which is what you're working on.

So, to summarize, you'll want to use the this keyword when adding or using internal attributes to your "class" (which you should not call class, since that doesn't exists here). It might be a pain, but it's the only way to reference internal attributes of your objects from within it's methods.

Additionally, you might find these two articles interesting to read, since they explain a bit about OOP techniques for JS and some common mistakes:

Let me know if you have more questions.

于 2013-02-28T14:23:34.400 回答
1

JavaScript doesn't look up variables in an object, because it's not a classical inheritance language. It's more based on Scheme, with its lexical scope.

Your examples show 2 things:

  • this refers to the object being instantiated
  • a is just a variable

In the internalMethod, you're still in the constructor function. So you have access to the variables defined in the constructor (function scope and lexical scope). However, once you get out of the constructor, a is not reachable anymore.

this.b means that on the instantiated object, you attach the property b. It's basically the equivalent of this:

function Foo() {}
var foo = {
    b: ''
};
foo.constructor.prototype = Foo.prototype;

Basically. Instantiating with new does a little bit more.

So if you want to access the instantiated object, you have to use this. If you just want to use the power of lexical scope, you can also play with simple variables and closures.

For example, this constructor will instantiate a new object every time it's called, and there is no this:

function Foo() {
    var a = 1;
    var b = 2;
    return {
        internalMethod: function() {
            return a;
        },

        incB: function() {
            return ++b;
        }
    };
}

var foo = Foo();
foo.internalMethod(); // 1
foo.incB(); // 3
foo.incB(); // 4

var bar = new Foo();
bar.incB(); // 3

This pattern is called the "module pattern".

If you find limiting the use of an object, you can return an object by using a immediately-executed function (and thus having another scope to play with):

function Foo() {
    var a = 1;
    var b = 2;
    return function() {
        var c = 3;
        return {
            a: a,
            c: c
        };
    }();
}
于 2013-02-28T14:23:49.683 回答
1

I seem to always need it in every reference to anything in my class. It just seems to result in an inordinate amount of typing in order to write much that is useful in JS

You're not doing it wrong. In my comment I referenced this answer where I try to explain every stage of writing a constructor, after reading it you'll understand why it's not wrong to use this, however, JavaScript does provide another statement, with that you could use to reduce how much you need to type after a property is initialised.

function MyConstructor() {
    this.a = 1; // initialise properties with `this`
    this.b = 2;
    this.c = 3;
    this.d = 4;
}
MyConstructor.prototype = {};
MyConstructor.prototype.foobar = function() {
    var c = 5, e = null;
    with (this) { // `with` now means you can do `a` instead of `this.a`
        var d = 6;
        console.log(a, b, c, d, e);
        a = 0 - a; // in `this`, property gets set to property
        b = -2; // in `this`
        c = -c; // in `this`, var'd outside `with`, property gets set to property
        d = -d; // in `this`, var'd inside `with`, property gets set to var
        e = 100; // not in `this`, var'd, var gets set (not var'd => global set)
    }
};
x = new MyConstructor(); // new instance
console.log(x.a, x.b, x.c, x.d, x.e);
//  1  2  3  4 undefined -- undefined as no property `e`
x.foobar();
//  1  2  3  6      null -- 6 from var, null from `var e = null`
console.log(x.a, x.b, x.c, x.d, x.e);
// -1 -2 -3 -6 undefined -- undefined as still no property `e`
于 2013-02-28T15:39:33.413 回答
0

I suggest reading about the Javascript module pattern to learn about public / local attributes and scope.

var declares a variable that will be accessible in the current function and the inner ones. Any method / attribute attached to an object is said "public" as it can be accessed from anywhere. Local variables, 'trapped' in closures, are used to emulate private members.

Attaching public methods to the prototype is the best memory-efficient approach to reuse those methods on different instances. You could use closures as well in those prototype methods.

One thing to note: as closures emulate 'private' variables but not 'protected' ones, it makes inheritance quite tricky when you need it.

于 2013-02-28T14:22:04.557 回答