Javascript:我是否需要为对象中的每个变量放置this.var?

Cha*_*ed0 35 javascript scope this

在C++中,我最熟悉的语言,通常是一个声明这样的对象:

class foo
{
public:
    int bar;
    int getBar() { return bar; }
}
Run Code Online (Sandbox Code Playgroud)

调用getBar()工作正常(忽略bar可能未初始化的事实).内部的变量bargetBar()类的范围内foo,所以我不需要说,this->bar除非我真的需要说清楚我指的是' bar而不是比如参数.

现在,我正试图在Javascript中开始使用OOP.所以,我查找如何定义类并尝试相同的事情:

function foo()
{
     this.bar = 0;
     this.getBar = function() { return bar; }
}
Run Code Online (Sandbox Code Playgroud)

它给了我bar is undefined.更改barthis.bar解决问题,但为每个变量执行此操作会使我的代码变得非常混乱.这对每个变量都是必要的吗?由于我找不到与此有关的任何问题,这让我觉得我做的事情从根本上是错误的.


编辑:是的,所以,从评论我得到的是,this.bar一个对象的属性,引用不同于bar局部变量的东西.有人可以说,为什么这就是范围和对象,以及是否有另一种方法来定义一个没有必要的对象?

Ber*_*rgi 33

JavaScript有没有基于类的对象模型.它使用了更强大的原型继承,它可以模仿类,但不适合它.一切都是对象,对象[可以]从其他对象继承.

构造函数只是一个为新创建的对象分配属性的函数.可以通过关键字(函数的本地new关键字)引用对象(通过带关键字的调用创建).this

方法也只是一个对象调用的函数 - 再次this指向对象.至少在使用成员运算符(点,括号)调用该函数作为对象的属性时.这会给新手带来很多混淆,因为如果你传递那个函数(例如,传递给事件监听器),它就会与访问它的对象"分离".

现在遗产在哪里?"类"的实例继承自同一原型对象.方法被定义为该对象上的函数属性(而不是每个实例的一个函数),您调用它们的实例只是继承该属性.

例:

function Foo() {
    this.bar = "foo"; // creating a property on the instance
}
Foo.prototype.foo = 0; // of course you also can define other values to inherit
Foo.prototype.getBar = function() {
    // quite useless
    return this.bar;
}

var foo = new Foo; // creates an object which inherits from Foo.prototype,
                   // applies the Foo constructor on it and assigns it to the var
foo.getBar(); // "foo" - the inherited function is applied on the object and
              // returns its "bar" property
foo.bar; // "foo" - we could have done this easier.
foo[foo.bar]; // 0 - access the "foo" property, which is inherited
foo.foo = 1;  // and now overwrite it by creating an own property of foo
foo[foo.getBar()]; // 1 - gets the overwritten property value. Notice that
(new Foo).foo;     // is still 0
Run Code Online (Sandbox Code Playgroud)

所以,我们只使用该对象的属性并对它感到满意.但是所有这些都是"公开的",可以被覆盖/更改/删除!如果这与你无关,那你很幸运.您可以通过在其名称前加下划线来表示属性的"私有性",但这只是对其他开发人员的提示,可能不会被遵守(特别是在出错时).

因此,聪明的头脑已经找到了一个使用构造函数作为闭包的解决方案,允许创建私有"属性".每次执行javascript函数都会为局部变量创建一个新的变量环境,一旦执行完成,就可能会收集垃圾.在该范围内声明的每个函数也可以访问这些变量,只要可以调用这些函数(例如,通过事件监听器),环境就必须保持不变.因此,通过从构造函数中导出本地定义的函数,可以使用只能由这些函数访问的局部变量来保留该变量环境.

让我们看看它的实际效果:

function Foo() {
    var bar = "foo"; // a local variable
    this.getBar = function getter() {
        return bar; // accesses the local variable
    }; // the assignment to a property makes it available to outside
}

var foo = new Foo; // an object with one method, inheriting from a [currently] empty prototype
foo.getBar(); // "foo" - receives us the value of the "bar" variable in the constructor
Run Code Online (Sandbox Code Playgroud)

此getter函数在构造函数中定义,现在称为" 特权方法",因为它可以访问"私有"(本地)"属性"(变量).价值bar永远不会改变.当然,您也可以为它声明一个setter函数,并且可以添加一些验证等.

请注意,原型对象上的方法无法访问构造函数的局部变量,但是它们可能使用特权方法.我们加一个:

Foo.prototype.getFooBar = function() {
    return this.getBar() + "bar"; // access the "getBar" function on "this" instance
}
// the inheritance is dynamic, so we can use it on our existing foo object
foo.getFooBar(); // "foobar" - concatenated the "bar" value with a custom suffix
Run Code Online (Sandbox Code Playgroud)

所以,你可以结合两种方法.请注意,特权方法需要更多内存,因为您创建了具有不同范围链的不同函数对象(但代码相同).如果要创建大量实例,则应仅在原型上定义方法.

当你将一个"类"的继承设置为另一个时,它会变得更加复杂 - 基本上你必须让子原型对象从父类继承,并在子实例上应用父构造函数来创建"私有属性" ".看看正确的javascript继承,继承原型中的私有变量,定义JAVASCRIPT模块模式中的私有字段成员和继承以及如何在JS Revealing原型模式中实现继承?


Min*_*hev 7

明确表示this.foo你对所foo引用的当前对象的属性感兴趣(正如你已经理解的那样)this.因此,如果您使用:this.foo = 'bar';您将设置equals foo引用的当前对象的属性.thisbar

thisJavaScript中的关键字并不总是像C++中那样.在这里,我可以给你一个例子:

function Person(name) {
   this.name = name;
   console.log(this); //Developer {language: "js", name: "foo"} if called by Developer
}

function Developer(name, language) {
   this.language = language;
   Person.call(this, name);
}

var dev = new Developer('foo', 'js');
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,我们使用函数Person的上下文调用函数,Developer因此this引用了将由其创建的对象Developer.正如您可能从console.log结果中看到的那样this来自Developer.使用方法的第一个参数,call我们指定用于调用函数的上下文.

如果您不使用this,您创建的属性将是一个局部变量.您可能知道JavaScript具有功能范围,因此变量将是本地的,仅对于声明它的函数可见(当然还有它在父元素内声明的所有子函数).这是一个例子:

function foo() {
    var bar = 'foobar';
    this.getBar = function () {
        return bar;
    }
}

var f = new foo();
console.log(f.getBar());  //'foobar'
Run Code Online (Sandbox Code Playgroud)

使用var关键字时也是如此.这意味着,你定义bar为局部变量,如果你忘记var不幸bar将成为全球性的.

function foo() {
    bar = 'foobar';
    this.getBar = function () {
        return bar;
    }
}

var f = new foo();
console.log(window.bar);  //'foobar'
Run Code Online (Sandbox Code Playgroud)

确切地说,本地范围可以帮助您实现隐私和封装,这是OOP的最大好处之一.

现实世界的例子:

function ShoppingCart() {
    var items = [];

    this.getPrice = function () {
       var total = 0;
       for (var i = 0; i < items.length; i += 1) {
          total += items[i].price;
       }
       return total;
    }

    this.addItem = function (item) {
        items.push(item);
    }

    this.checkOut = function () {
        var serializedItems = JSON.strigify(items);
        //send request to the server...
    }
}

var cart = new ShoppingCart();
cart.addItem({ price: 10, type: 'T-shirt' });
cart.addItem({ price: 20, type: 'Pants' });
console.log(cart.getPrice()); //30
Run Code Online (Sandbox Code Playgroud)

JavaScript范围的另一个好处是模块模式.在模块模式中,您可以使用JavaScript的本地功能范围模拟隐私.使用此方法,您可以拥有私有属性和方法.这是一个例子:

var module = (function {

    var privateProperty = 42;

    function privateMethod() {
        console.log('I\'m private');
    }
    return {

       publicMethod: function () {
           console.log('I\'m public!');
           console.log('I\'ll call a private method!');
           privateMethod();
       },

       publicProperty: 1.68,

       getPrivateProperty: function () {
           return privateProperty;
       },

       usePublicProperty: function () {
           console.log('I\'ll get a public property...' + this.publicProperty);
       }

    }
}());

module.privateMethod(); //TypeError
module.publicProperty(); //1.68
module.usePublicProperty(); //I'll get a public property...1.68
module.getPrivateProperty(); //42
module.publicMethod(); 
/*
 * I'm public!
 * I'll call a private method!
 * I'm private
 */
Run Code Online (Sandbox Code Playgroud)

有一个奇怪的语法,无父对象包装匿名函数但暂时忘记它(它只是在初始化后执行函数).功能可以从使用示例中看出,但优点主要在于提供一个简单的公共接口,而不会使您了解所有实现细节.有关模式的更详细说明,您可以看到我上面提到的链接.


我希望通过this:-)信息帮助您了解JavaScript的一些基本主题.