簡體   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