繁体   English   中英

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

[英]Javascript: Do I need to put this.var for every variable in an object?

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

class foo
{
public:
    int bar;
    int getBar() { return bar; }
}

调用getBar()工作正常(忽略bar可能未初始化的事实)。 getBar()的变量bar在类foo的范围内,所以我不需要说this->bar除非我真的需要明确我指的是类的bar而不是说,一个参数。

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

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

它给了我bar is undefined bar更改为this.bar解决了这个问题,但是对每个变量都这样做会使我的代码混乱很多。 这对每个变量都是必要的吗? 由于我找不到与此相关的任何问题,这让我觉得我在做一些根本错误的事情。


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

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

所以,我们只使用了那个对象的属性并且对它很满意。 但它们都是“公开的”,可以被覆盖/更改/删除! 如果这对你来说并不重要,那么你很幸运。 您可以通过在属性名称前加上下划线来表示属性的“私有性”,但这只是对其他开发人员的提示,可能不会被遵守(尤其是在错误时)。

因此,聪明的头脑找到了一个解决方案,它使用构造函数作为闭包,允许创建私有“属性”。 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

这个在构造函数中定义的 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

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

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

明确地说this.foo意味着(正如您所理解的那样)您对this引用的当前对象的属性foo感兴趣。 所以如果你使用: this.foo = 'bar'; 您将设置this引用的当前对象的属性foo等于bar

JavaScript 中的this关键字并不总是与 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');

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

如果您不使用this ,那么您创建的属性将是一个局部变量。 您可能知道 JavaScript 具有函数作用域,因此变量将是局部的,仅对声明它的函数可见(当然,它是在父函数内部声明的所有子函数)。 下面是一个例子:

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

var f = new foo();
console.log(f.getBar());  //'foobar'

使用var关键字时确实如此。 这意味着如果您忘记了var不幸的是bar将成为全局变量,那么您将bar定义为局部变量。

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

var f = new foo();
console.log(window.bar);  //'foobar'

正是本地范围可以帮助您实现隐私和封装,这是 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

JavaScript 作用域优势的另一个例子是Module Pattern 在模块模式中,您可以使用 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
 */

无父函数包装匿名函数有一些奇怪的语法,但暂时忘记它(它只是在初始化后执行函数)。 功能可以从使用示例中看出,但好处主要在于提供一个简单的公共接口,该接口不会让您参与所有实现细节。 有关该模式的更详细说明,您可以查看我上面放置的链接。


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

function Foo() {
  this.bar = 0;
  this.getBar = function () { return this.bar };
}

当你用new关键字调用上面的函数时 - 像这样......

var foo = new Foo();

... - 发生了一些事情:

1)创建一个对象
2) 使用this关键字引用该对象来执行该函数。
3) 返回那个对象。

foo ,然后,成为这个对象:

{
    bar: 0,
    getBar: function () { return this.bar; }
};

那么,为什么不这样做:

var foo = {
    bar: 0,
    getBar: function () { return this.bar; }
};

你会的,如果它只是一个简单的对象。

但是使用构造函数创建对象(这就是它的调用方式)在创建多个“相同”对象方面给了我们很大的优势。

请看,在 javascript 中,所有函数都使用原型属性 [一个对象] 创建,并且使用该函数创建的所有对象(通过使用 new 关键字调用它)都链接到该原型对象。 这就是它如此酷的原因——您可以在原型对象中存储所有常用方法(和属性,如果您愿意),并节省大量内存。 这是它的工作原理:

function Foo( bar, bob ) {
   this.bar = bar;
   this.bob = bob;
}

Foo.prototype.calculate = function () {
  // 'this' points not to the 'prototype' object 
  // as you could've expect, but to the objects
  // created by calling Foo with the new keyword.
  // This is what makes it work.
  return this.bar - this.bob;  
};

var foo1 = new Foo(9, 5);
var foo2 = new Foo(13, 3);
var result1 = foo1.calculate();
var result2 = foo2.calculate();

console.log(result1); //logs 4
console.log(result2); //logs 10

而已!

为了更接近 JavaScript 中的 OOP,您可能需要查看模块设计模式(例如,这里描述的)。

基于闭包效果,此模式允许在您的对象中模拟私有属性。

使用“私有”属性,您可以通过其标识符直接引用它们(即,没有构造函数中的this关键字)。

但无论如何,JS 中的闭包和设计模式 - 一个高级主题。 所以,熟悉基础知识(也在前面提到的书中解释过)。

在 javascript 中, this总是指函数的所有者对象。 例如,如果您在页面中定义函数foo() ,则所有者是 javascript 对象windows 或者,如果您在 html 元素<body>上定义foo() ,则所有者是 html 元素主体; 同样,如果您定义元素<a> onclick 函数,则所有者是锚点。

在您的情况下,您在开始时将属性bar分配给“所有者”对象并尝试返回局部变量bar

由于您从未定义过任何本地变量bar ,因此它给您作为 bar 未定义。

理想情况下,您的代码应该将变量定义为var bar; 如果你想返回零值。

就像对象(变量或函数)的公共访问修饰符,而var是私有访问修饰符

例子

var x = {}; 
x.hello = function(){
    var k = 'Hello World'; 
   this.m = 'Hello JavaScript'; 
}

var t = new x.hello(); 
console.log(t.k); //undefined
console.log(t.m); //Hello JavaScript

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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