2011-07-12 92 views
4

我有這個類在那裏我有一個私人財產和公共方法進行訪問:強制上下文

Person = function() { 
    this.Name = "asd"; 

    var _public = new Object(); 
    _public.Name = function (value) { 
     if (value == undefined) { //Get 
      return this.Name 
     } else { 
      this.Name = value; //Set 
     } 
    }; 

    return _public; 
}; 

我想強制上下文中_public.Name接入的this.Name

我知道關閉的技巧,但我想看看我是否可以強制上下文。

,我發現了一種技術,做到這一點,擴展目標函數:

Function.prototype.setScope = function (scope) { 
    var f = this; 

    return function() { 
     f().apply(scope); 
    } 
} 

而且我的課就變成了:

Person = function() { 
    this.Name = "asd"; 

    var _public = new Object(); 
    _public.Name = function (value) { 
     if (value == undefined) { 
      return this.Name 
     } else { 
      this.Name = value; 
     } 
    }.setScope(this); 

    return _public; 
}; 

這樣我就可以正確地強制上下文,但我無法通過value和但不能返回this.Name

+0

所以新的Person()返回一個封閉的綁定到這個_public.Name函數。這是一個相當複雜的創建類的方法,它不是返回一個對象,而是返回一個函數。這可能不是你想要做的。但是很難從所提供的代碼示例中猜出您想實現的目標。 –

+0

@Jean:*「這是一種相當複雜的方式來創建一個類,而不是返回一個對象,返回一個函數。」*它不返回一個函數對象,只是一個對象。該對象具有分配給其「名稱」屬性的功能。儘管如此,我認爲這不是我該怎麼做的。 –

+0

^^你是對的,它確實會返回一個對象,但這是一個相當複雜的方式,用於做一些其他方面微不足道的事情。 –

回答

8

f().apply(scope); 

只是

f.apply(scope); 

(無()f。)你想使用apply函數的函數f對象上,而不是通話功能f和訪問apply在其回報價值上。

要還傳遞您在setScope函數接收參數,補充一點:

f.apply(scope, arguments); 

arguments是一個隱含參數所有功能,這是傳遞給函數的實際參數的僞陣列在運行時。 apply接受任何類似數組的東西作爲其第二個參數來指定調用基礎函數時使用的參數。

我想也有它返回的返回值:

return f.apply(scope, arguments); 

所以setScope變爲:

Function.prototype.setScope = function (scope) { 
    var f = this; 

    return function() { 
     return f.apply(scope, arguments); 
    } 
} 

Live example

注意平時的名字此功能,名稱它在新的ECMAScript5 standard中,是bind(第15.3.4.5節; ECMAScript5的bind也可以讓你curry參數,這不是由此實現完成的)。 setScope是一個特別不幸的名字,因爲它沒有設置範圍,它設置了上下文

說了這麼多,沒有理由在您的Person構造函數中需要setScope。你可以這樣做:

Person = function() { 
    var self = this; 

    this.Name = "asd"; 

    var _public = new Object(); 
    _public.Name = function (value) { 
     if (value == undefined) { 
      return self.Name; 
     } else { 
      self.Name = value; 
     } 
    }; 

    return _public; 
}; 

Live example

但使用bind(又名setScope)可以在地方,你不想在你正在做它在上下文中的新的閉包是有用的。


題外話:你指定Person將打破某些東西的人可能希望的工作方式,如:

var p = new Person(); 
alert(p instanceof Person); // Expect "true", but in your case will be "false" 

...因爲你要替換的對象new爲你創建,但是從構造函數中返回一個不同的對象(它覆蓋了默認值)。

而不是創建一個新的對象並返回,在你的構造,允許你構造的對象由new爲對象(因此Person關係保持不變),但你仍然可以得到真正的私有變量和使用存取:

function Person() { 
    // Private variable 
    var name = "asd"; 

    // Accessor function 
    this.Name = function(value) { 
     if (typeof value === "undefined") { 
      return name; 
     } 
     name = value; 
    }; 
} 

Live example

正如你所看到的,這是大大簡化,它保留了instanceof關係。請注意,我們並沒有在Name範圍內限定我們對name的引用,因此我們在構造函數調用中使用局部變量,其中創建了關閉它的Name函數。

我也冒昧給了構造函數名稱,因爲I'm not a fan of anonymous functions。我應該給訪問者一個名稱,以及:

function Person() { 
    // Private variable 
    var name = "asd"; 

    // Accessor function 
    this.Name = Person_Name; 
    function Person_Name(value) { 
     if (typeof value === "undefined") { 
      return name; 
     } 
     name = value; 
    } 
} 

題外話2:在JavaScript代碼中壓倒一切的慣例是,在功能名稱中使用首字母大寫的構造函數(如Person),而不是其他類型的功能(如Name)。當然,你可以自由地做任何你喜歡的事情,但是我認爲我會提到這個慣例,因爲它讓其他人更容易閱讀你的代碼。


值得注意:這些技術的所有導致每一個Person對象具有存取功能的它自己副本。如果將會有很多這些對象,那可能是一個內存問題。如果只有幾個,那很好。

+0

對不起,我忘了在最後一次測試後刪除 –

+1

非常感謝您的詳盡解釋和您的時間 –

1

首先,我認爲正確的方法是使用「閉包」方法,因爲語法更容易理解,更易於理解,而且用Javascript編寫的大多數面向對象代碼都是這樣編寫的。另外要注意的是,在您的方法中,可以通過訪問Person.Name(而不是(new Person()).Name)從外部訪問「私人」成員。

這就是說,你似乎想要類似Prototype.JS's bind method,它允許你綁定一個函數引用作爲一個方法調用一個特定的對象,並且也正確地傳遞所有參數(包括允許預加載的參數)。

看看的prototype.js源的完整實現,但是一個簡單的實現這個語義的可能是這樣的:

Function.prototype.bind = function(context) { 
    var callee = this; 
    var args = Array.prototype.slice.call(arguments,1); 
    return function() { 
     var newargs = args.concat(Array.prototype.slice.call(arguments,0)); 
     return callee.apply(context, newargs); 
    }; 
};    
1

這是很難理解你想要達到的目的。但是,如果我猜,你要創建一個Person類的名稱的方法來獲取/設置這個人的名字,這是我的建議:我有一個小寫的第一定義的名稱功能

function Person() { 
    this._name = undefined; // not required but is better than assigning a fake name 

    return this; 
} 

Person.prototype.name = function(_name) { 
    if (_name === undefined) return this._name; // get 
    return this._name = _name; // set 
} 

注信。這是JavaScript中的標準做法,只有構造函數通常是大寫的。要使用此類,請執行以下操作:

person = new Person(); 

person.name("Ermes Enea Colella"); 

alert(person.name); // displays "Ermes Enea Colella" 

沒有必要使用此方法綁定任何上下文,因此您可能正在尋找其他內容。如果你能澄清你的需求,我會很樂意編輯我的答案。

我希望這會有所幫助。