简体   繁体   English

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

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

In C++, the language I'm most comfortable with, usually one declares an object like this:在 C++ 中,我最熟悉的语言通常是这样声明一个对象:

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

Calling getBar() works fine (ignoring the fact that bar might be uninitialized).调用getBar()工作正常(忽略bar可能未初始化的事实)。 The variable bar within getBar() is in the scope of class foo , so I don't need to say this->bar unless I really need to make it clear that I'm referring to the class' bar instead of, say, a parameter. getBar()的变量bar在类foo的范围内,所以我不需要说this->bar除非我真的需要明确我指的是类的bar而不是说,一个参数。

Now, I'm trying to get started with OOP in Javascript.现在,我正在尝试在 Javascript 中开始使用 OOP。 So, I look up how to define classes and try the same sort of thing:所以,我查找如何定义类并尝试相同的事情:

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

And it gives me bar is undefined .它给了我bar is undefined Changing the bar to this.bar fixes the issue, but doing that for every variable clutters up my code quite a bit.bar更改为this.bar解决了这个问题,但是对每个变量都这样做会使我的代码混乱很多。 Is this necessary for every variable?这对每个变量都是必要的吗? Since I can't find any questions relating to this, it makes me feel like I'm doing something fundamentally wrong.由于我找不到与此相关的任何问题,这让我觉得我在做一些根本错误的事情。


EDIT: Right, so, from the comments what I'm getting is that this.bar , a property of an object, references something different than bar , a local variable.编辑:是的,所以,从我得到的评论来看, this.bar是一个对象的属性,引用了不同于bar的局部变量。 Can someone say why exactly this is, in terms of scoping and objects, and if there's another way to define an object where this isn't necessary?有人可以说为什么会这样,就范围和对象而言,以及是否有另一种方法来定义不需要的对象?

JavaScript has no classes class-based object model. JavaScript有没有基于类的对象模型。 It uses the mightier prototypical inheritance, which can mimic classes, but is not suited well for it.它使用更强大的原型继承,它可以模仿类,但并不适合它。 Everything is an object, and objects [can] inherit from other objects.一切都是对象,对象[可以]从其他对象继承。

A constructor is just a function that assigns properties to newly created objects.构造函数只是一个为新创建的对象分配属性的函数。 The object (created by a call with the new keyword ) can be referenced trough the this keyword (which is local to the function).对象(通过使用new关键字调用创建)可以通过this关键字(对于函数来说是本地的)来引用。

A method also is just a function which is called on an object - again with this pointing to the object.方法也只是一个对象调用的函数 - 同样this指向对象。 At least when that function is invoked as a property of the object, using a member operator (dot, brackets).至少当该函数作为对象的属性被调用时,使用成员运算符(点、括号)。 This causes lots of confusion to newbies, because if you pass around that function (eg to an event listener) it is "detached" from the object it was accessed on.这会给新手带来很多困惑,因为如果您传递该函数(例如传递给事件侦听器),它会与访问它的对象“分离”。

Now where is the inheritance?现在传承在哪里? Instances of a "class" inherit from the same prototype object. “类”的实例继承自同一个原型对象。 Methods are defined as function properties on that object (instead of one function for each instance), the instance on which you call them just inherits that property.方法被定义为该对象的函数属性(而不是每个实例的一个函数),您调用它们的实例只是继承了该属性。

Example:例子:

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

So, we did only use properties of that object and are happy with it.所以,我们只使用了那个对象的属性并且对它很满意。 But all of them are "public", and can be overwritten/changed/deleted!但它们都是“公开的”,可以被覆盖/更改/删除! If that doesn't matter you, you're lucky.如果这对你来说并不重要,那么你很幸运。 You can indicate "privateness" of properties by prefixing their names with underscores, but that's only a hint to other developers and may not be obeyed (especially in error).您可以通过在属性名称前加上下划线来表示属性的“私有性”,但这只是对其他开发人员的提示,可能不会被遵守(尤其是在错误时)。

So, clever minds have found a solution that uses the constructor function as a closure, allowing the creating of private "attributes".因此,聪明的头脑找到了一个解决方案,它使用构造函数作为闭包,允许创建私有“属性”。 Every execution of a javascript function creates a new variable environment for local variables, which may get garbage collected once the execution has finished. javascript 函数的每次执行都会为局部变量创建一个新的变量环境,一旦执行完成,它可能会被垃圾收集。 Every function that is declared inside that scope also has access to these variables, and as long as those functions could be called (eg by an event listener) the environment must persist.在该范围内声明的每个函数也可以访问这些变量,只要可以调用这些函数(例如,由事件侦听器),环境就必须持续存在。 So, by exporting locally defined functions from your constructor you preserve that variable environment with local variables that can only be accessed by these functions.因此,通过从构造函数导出本地定义的函数,您可以使用只能由这些函数访问的局部变量来保留该变量环境。

Let's see it in action:让我们看看它的实际效果:

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

This getter function, which is defined inside the constructor, is now called a " privileged method" as it has access to the "private" (local) "attributes" (variables).这个在构造函数中定义的 getter 函数现在被称为“特权方法”,因为它可以访问“私有”(本地)“属性”(变量)。 The value of bar will never change. bar的值永远不会改变。 You also could declare a setter function for it, of course, and with that you might add some validation etc.当然,您也可以为它声明一个 setter 函数,然后您可以添加一些验证等。

Notice that the methods on the prototype object do not have access to the local variables of the constructor, yet they might use the privileged methods.请注意,原型对象上的方法无权访问构造函数的局部变量,但它们可能会使用特权方法。 Let's add one:让我们添加一个:

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

So, you can combine both approaches.所以,你可以结合这两种方法。 Notice that the privileged methods need more memory, as you create distinct function objects with different scope chains (yet the same code).请注意,特权方法需要更多内存,因为您创建具有不同作用域链(但代码相同)的不同函数对象。 If you are going to create incredibly huge amounts of instances, you should define methods only on the prototype.如果你要创建大量的实例,你应该只在原型上定义方法。

It gets even a little more complicated when you are setting up inheritance from one "class" to another - basically you have to make the child prototype object inherit from the parent one, and apply the parent constructor on child instances to create the "private attributes".当您设置从一个“类”到另一个“类”的继承时,它会变得更加复杂 - 基本上您必须使子原型对象从父对象继承,并在子实例上应用父构造函数以创建“私有属性”。 Have a look at Correct javascript inheritance , Private variables in inherited prototypes , Define Private field Members and Inheritance in JAVASCRIPT module pattern and How to implement inheritance in JS Revealing prototype pattern?看看正确的 javascript 继承继承原型中的私有变量,在 JAVASCRIPT 模块模式中定义私有字段成员和继承以及如何在 JS 揭示原型模式中实现继承?

Explicitly saying this.foo means (as you've understood well) that you're interested about the property foo of the current object referenced by this .明确地说this.foo意味着(正如您所理解的那样)您对this引用的当前对象的属性foo感兴趣。 So if you use: this.foo = 'bar';所以如果你使用: this.foo = 'bar'; you're going to set the property foo of the current object referenced by this equals to bar .您将设置this引用的当前对象的属性foo等于bar

The this keyword in JavaScript doesn't always mean the same thing like in C++. JavaScript 中的this关键字并不总是与 C++ 中的相同。 Here I can give you an example:这里我可以给你举个例子:

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');

In the example above we're calling the function Person with the context of the function Developer so this is referencing to the object which will be created by Developer .在上面的示例中,我们使用函数Developer的上下文调用函数Person ,因此this引用了将由Developer创建的对象。 As you might see from the console.log result this is comes from Developer .正如您从console.log结果中看到的, this是来自Developer With the first argument of the method call we specify the context with which the function will be called.使用方法call的第一个参数,我们指定call函数的上下文。

If you don't use this simply the property you've created will be a local variable.如果您不使用this ,那么您创建的属性将是一个局部变量。 As you might know JavaScript have functional scope so that's why the variable will be local, visible only for the function where it's declared (and of course all it's child functions which are declared inside the parent).您可能知道 JavaScript 具有函数作用域,因此变量将是局部的,仅对声明它的函数可见(当然,它是在父函数内部声明的所有子函数)。 Here is an example:下面是一个例子:

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

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

This is true when you use the var keyword.使用var关键字时确实如此。 This means that you're defining bar as local variable if you forget var unfortunately bar will became global.这意味着如果您忘记了var不幸的是bar将成为全局变量,那么您将bar定义为局部变量。

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

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

Exactly the local scope can help you to achieve privacy and encapsulation which are one of the greatest benefits of OOP.正是本地范围可以帮助您实现隐私和封装,这是 OOP 的最大好处之一。

Real world example:现实世界的例子:

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

One more example of the benefits of the JavaScript scope is the Module Pattern . JavaScript 作用域优势的另一个例子是Module Pattern In Module Pattern you can simulate privacy using the local functional scope of JavaScript.在模块模式中,您可以使用 JavaScript 的本地功能范围来模拟隐私。 With this approach you can have both private properties and methods.通过这种方法,您可以同时拥有私有属性和方法。 Here is an example:下面是一个例子:

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
 */

There's a little strange syntax with the parentless wrapping the anonymous functions but forget it for the moment (it's just executing the function after it's being initialized).无父函数包装匿名函数有一些奇怪的语法,但暂时忘记它(它只是在初始化后执行函数)。 The functionality can be saw from the example of usage but the benefits are connected mainly of providing a simple public interface which does not engages you with all implementation details.功能可以从使用示例中看出,但好处主要在于提供一个简单的公共接口,该接口不会让您参与所有实现细节。 For more detailed explanation of the pattern you can see the link I've put above.有关该模式的更详细说明,您可以查看我上面放置的链接。


I hope that with this :-) information I helped you to understand few basic topics of JavaScript.我希望通过this :-) 信息,我可以帮助您了解 JavaScript 的一些基本主题。

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

When you call the function above with the new keyword - like this...当你用new关键字调用上面的函数时 - 像这样......

var foo = new Foo();

... - a few things happen: ... - 发生了一些事情:

1) an object is created 1)创建一个对象
2) the function is executed with the this keyword referencing that object. 2) 使用this关键字引用该对象来执行该函数。
3) that object is returned. 3) 返回那个对象。

foo , then, becomes this object: foo ,然后,成为这个对象:

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

Why not, then, just do this:那么,为什么不这样做:

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

You would, if it's just that one simple object.你会的,如果它只是一个简单的对象。

But creating an object with a constructor (that's how it's called) gives us a big advantage in creating multiple of the "same" objects.但是使用构造函数创建对象(这就是它的调用方式)在创建多个“相同”对象方面给了我们很大的优势。

See, in javascript, all functions are created with a prototype property [an object], and all objects created with that function (by calling it with the new keyword) are linked to that prototype object.请看,在 javascript 中,所有函数都使用原型属性 [一个对象] 创建,并且使用该函数创建的所有对象(通过使用 new 关键字调用它)都链接到该原型对象。 This is why it's so cool - you can store all common methods (and properties, if you wanted to) in the prototype object, and save a lot of memory.这就是它如此酷的原因——您可以在原型对象中存储所有常用方法(和属性,如果您愿意),并节省大量内存。 This is how it works:这是它的工作原理:

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

That's it!而已!

To get closer to OOP in JavaScript, you might want to take a look into a Module design pattern (for instance, describedhere ).为了更接近 JavaScript 中的 OOP,您可能需要查看模块设计模式(例如,这里描述的)。

Based on the closure effect, this pattern allows emulating private properties in your objects.基于闭包效果,此模式允许在您的对象中模拟私有属性。

With 'private' properties you can reference them directly by its identifier (ie, no this keyword as in constructors).使用“私有”属性,您可以通过其标识符直接引用它们(即,没有构造函数中的this关键字)。

But anyway, closures and design patterns in JS - an advanced topic.但无论如何,JS 中的闭包和设计模式 - 一个高级主题。 So, get familiar with basics (also explained in the book mentioned before).所以,熟悉基础知识(也在前面提到的书中解释过)。

In javascript this always refers to the owner object of the function.在 javascript 中, this总是指函数的所有者对象。 For example, if you define your function foo() in a page, then owner is the javascript object windows ;例如,如果您在页面中定义函数foo() ,则所有者是 javascript 对象windows or if you define the foo() on html element <body> , then the owner is the html element body;或者,如果您在 html 元素<body>上定义foo() ,则所有者是 html 元素主体; and likewise if you define the function onclick of element <a> , then the owner is the anchor.同样,如果您定义元素<a> onclick 函数,则所有者是锚点。

In your case, you are assigning a property bar to the 'owner' object at the begining and trying to return the local variable bar .在您的情况下,您在开始时将属性bar分配给“所有者”对象并尝试返回局部变量bar

Since you never defined any local varialbe bar , it is giving you as bar is undefined.由于您从未定义过任何本地变量bar ,因此它给您作为 bar 未定义。

Ideally your code should have defined the variable as var bar;理想情况下,您的代码应该将变量定义为var bar; if you want to return the value zero.如果你想返回零值。

this is like a public access modifier of objects(variables or functions), while var is the private access modifier就像对象(变量或函数)的公共访问修饰符,而var是私有访问修饰符

Example例子

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