[英]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.