簡體   English   中英

Javascript函數范圍和查找父對象的屬性

[英]Javascript function scope and finding properties of a parent object

我想要實現的是創建一些在JavaScript中操作對象屬性的函數,這是一個相當典型的事情。

問題在於,為了靈活性和可擴展性,我不希望函數提前知道對象。 據我所知,這正是Javascript應該非常好用的東西,但是沒有花費大量時間來處理函數式語言,我還沒有掌握如何做到這一點。

所以我可能會遇到這樣的情況:

function Owner()
{
     var self=this;
     self.size = 100;
     self.writers = [];
}

function Writers()
{
    var Alice= function()
    {
        var that=this;
        that.name="alice";
        that.operate = function()
        {
            console.log( "Alice says: "+self.size );   
        }
    }
    this.alice=new Alice();
}

var owner = new Owner();
var writers = new Writers();
owner.writers.push( writers.alice );
owner.writers[0].operate();

現在這回歸Alice Says: undefined哪個好,哪個好,但不完全是我想要的 - 我顯然希望看到Alice Says: 100 在一個經典語言中,我可能不得不將Alice函數交給所有者(在偽C#中):

public class Owner()
{
    private List<Writer> writers;



    public Owner()
    {
        this.size=100;
        this.writers= List<Writer>();
    }

    public int Size{ get; set; }

    public void AddWriter( Writer writer )
    {
         writer.Owner = this;
         this.writers.Add(writer);     
    }

}

public class Writer
{
   private Owner owner;
   private string name;

   public void Writer( string name )
   {
      this.name=name;
   }

   public void operate()
   {
        Console.WriteLine( "{0} says: {1}", name, owner.Size );
    }
}

據我所知,這不是非常Javascripty-似乎應該有一個更好 - 或至少更慣用 - 的方式。

就我正在使用的代碼的實際目標而言(基本上是這樣,但更復雜),目標是能夠添加任意數量的“Writer”類,這些類可以對“Owner”實例執行操作他們是誰。 這基本上是Decorator模式的一個實現,我可以想到很多方法我可以實現這個,但正如我所說,我正在尋找理解Javascript的功能/原型本質如何幫助我在這種情況下如此我可以更好地處理語言。

我對通過實例傳遞函數的方式也不是很滿意 - 我覺得我應該像五彩紙屑那樣拋出函數,所以關於如何與變量范圍交互的任何提示都會非常有用。

如果事實證明我正在嘗試做的事情是向后傾斜並且有更好的方法來實現這種類型的目標,那也沒關系。 我正試圖在Javascript中使用優秀的語言並證明它很棘手,但我學到了很多東西。

編輯:我的目標是有一個基本類型,我不必知道我何時設計它可能必須執行的操作的完整列表,以及我不必為每個不同的可用組合創建新的子類操作,確保我的操作與我的核心類型分離。

所以,如果我有一個Animal類型然后我的操作是EatRunGrow等,我理想情況下會有一個myAnimal.can("Eat")類型調用(我意識到這有點像hasOwnProperty但我是假設這些操作屬於特定的基類型,它會告訴我該操作是否可用,然后是myAnimal.actions.Eat("leaves")如果它是可訪問的。 當調用Eat動作時,它會影響父myAnimal對象的屬性。 myAnimal的生命周期中,它可能會在任何時候添加或刪除,但只要檢查它can做一些應該足以滿足我的需要的東西。

實現它的一種方法是使用javascript閉包

function Owner() {
    var $self = {};
    $self.size = 150;
    $self.writers = [];

    function Writers() {

        var Alice = function () {

            var that = this;
            that.name = "alice";
            that.operate = function () {
                console.log("Alice says: " + $self.size);
                console.log($self);
            }
        }
        this.alice = new Alice();
    }

    var getO = function () {
        var writers = new Writers();
        $self.writers.push(writers.alice);
        $self.writers[0].operate();
        $self.writers[0].operate();
    }
    return getO
}

var o = Owner();
o();

這是小提琴

有一個閉包基本上創建了一個環境,其中“內部外部”函數和變量可以從已返回的函數的“內部內部”訪問:

這些是“內外”功能和變量:

    var $self = {};
    $self.size = 150;
    $self.writers = [];

    function Writers() {

        var Alice = function () {

            var that = this;
            that.name = "alice";
            that.operate = function () {
                console.log("Alice says: " + $self.size);
                console.log($self);
            }
        }
        this.alice = new Alice();
    }

里面的getO是'內在':

    var getO = function () {
         //Here are the functions you want to call:

        var writers = new Writers();
        $self.writers.push(writers.alice);
        $self.writers[0].operate();
        $self.writers[0].operate();
     }

返回getO,我們創建了一個閉包!

從現在開始,當你打電話給realowner(); 你會喜歡叫getO(); getO()所有內容都能夠訪問“內部 - 外部”變量和函數(例如$self )。

在這一行:

console.log( "Alice says: "+self.size ); 

self不是你想要的,因為你對self的定義不在范圍內,因此你得到了self的全局定義,這是一個window ,不會做你想要的。

您的設計中的問題是Writers對象對Owner對象一無所知,因此不能有報告Owner數組大小的方法。

你已經使你的代碼變得比它需要的那些復雜得多that並且對於selfself的不必要的定義that並且不清楚為什么Writers對象應該知道它的容器。 如果你希望如此,那么你應該將它的容器傳遞給Writers對象的構造函數,它可以存儲對它的容器的引用。

如果你更好地描述了你真正試圖解決的問題,我們可能會提出一個更簡單,更正確的面向對象的建議。


在Javascript中,您仍然需要記住封裝到對象中的數據的概念,以及那些具有可以對可訪問的數據和屬性進行操作的方法的對象。 如果要對對象中的數據進行操作(如在.size屬性中),則必須具有對包含該屬性的對象的引用。

對象(如Owner對象)的引用可以來自幾個地方:

  1. 它可以作為實例數據存儲在某個其他對象中,因此可以根據需要從其他對象方法中獲得(例如,在每個Writers對象中存儲對所有者的引用)。
  2. 它可以通過范圍獲得(例如,在當前函數中定義或嵌套在其中的任何父函數)。 在Owner的構造函數范圍內定義Writers對象。 這將使您可以訪問Owner實例,但也會使Writers對象僅在Owners方法內可用(不公開)。
  3. 它可以作為參數傳遞給想要使用它的方法/函數。 您可以將相應的所有者作為參數傳遞給.operate(curOwner)方法。
  4. 它可以全局存儲,因此可以在任何地方使用。 如果一次只有一個所有者(例如,它是單一設計模式),您可以讓所有者全局存儲並全局訪問。 這不太靈活(將來某個時候不能使用多個所有者用於其他目的),但可以更簡單。

您的代碼中缺少的是從Writers對象的.operate()方法到達相應的Owner對象的任何方式。 它根本不在.operate()代碼中。 如果您希望它在那里可用,則必須更改代碼的結構以至少制作上述訪問方法之一,或者創建一種新方法來訪問Owner實例。


根據您最新的編輯來描述問題,這里有更多信息。 您可以利用javascript松散類型的一種方法是,您可以擁有一個對象數組,這些對象可以是您想要的任何類型。 只要它們都有一組通用的方法,如.eat() .grow() .eat() .grow() .run() ,你就可以對對象進行相同的處理(在它們上面調用這些方法),即使它們是完全不同的類型對象 您不必創建公共基類,然后從中進行子類化。 這不是非常javascripty,不是必需的。 只需創建三個不同的對象,為每個對象提供所需的方法集,將它們放入數組中,然后根據需要調用方法。 如果一個對象在調用它之前可能不是都有相同的方法,你甚至可以測試一個對象是否有一個特定的方法(雖然這是一個不太完美的OO設計 - 雖然有時比其他設計更實用)。

例如,你可以這樣:

// define the cat object
function cat(name) {
   this.name = name;
}

cat.prototype = {
    eat: function(howMuch) {
        // implementation of eat here
    },
    run: function(howFar) {
        // implementation of run here
    },
    grow: function(years) {
        // implementation of grow here
    }
};

// define the dog object
function dog(name) {
    this.name = name;
}

dog.prototype = {
    eat: function(howMuch) {
        // implementation of eat here
    },
    run: function(howFar) {
        // implementation of run here
    },
    grow: function(years) {
        // implementation of grow here
    }
};

然后你可以有一系列的貓和狗:

var animals = [];
animals.push(new cat("fluffy"));
animals.push(new dog("bowser"));
animals.push(new cat("purr"));

然后,您可以擁有對animals陣列內容進行操作的代碼,而不考慮它是什么類型的動物。 所以,要使每只動物跑30英尺,你可以這樣做:

for (var i = 0; i < animals.length; i++) {
    animals[i].run(30);
}

現在,您可以創建一個具有默認構造函數的基類來保存名稱,並且定義不執行任何抽象方法,包括吃,運行和增長(使用C ++或C#樣式),但這在JS中通常不是必需的,因為您不需要只要它們都有一些您知道如何調用的通用方法,就必須知道要使用它的對象類型。

我將你的偽C#代碼翻譯成了javascript。
在翻譯時,我將一些命名約定修改為常見的js標准。 (僅針對類的主要大寫,私人成員的下划線)。 我使用屬性和閉包來更接近你的類描述。

我還修改了一些簽名:
- 我猜是名字和大小只讀。
- 作家必須了解其所有者。
- 所有者可能希望讓所有作家都能操作。 (只是一個猜測)。

希望這會引導您實現目標。

小提琴在這里: http//jsfiddle.net/gamealchemist/zPu8C/3/

function Owner() {
    var _writers = [];

    Object.defineProperty(this, 'size', {
        get: function () {
            return _writers.length
        },
        enumerable: true
    });

    this.addWriter = function (writer) {
        _writers.push(writer);
    }

    this.operateAll = function () {
        _writers.forEach(function (w) {
            w.operate();
        });
    };
}

function Writer(owner, name) {
    var _owner = owner;
    var _name = name;

    Object.defineProperty(this, 'name', {
        get: function () {
            return _name;
        },
        enumerable: true
    });

    this.operate = function () {
        console.log(this.name + " sees " + _owner.size + " people");
    }

    _owner.addWriter(this);
}

var wonderlandOwner = new Owner();

var alice = new Writer(wonderlandOwner, 'Alice');
var rabbit = new Writer(wonderlandOwner, 'Rabbit');
var madHatter = new Writer(wonderlandOwner, 'Mad Hatter');

wonderlandOwner.operateAll();

// outuput is :
// Alice sees 3 people 
// Rabbit sees 3 people 
// Mad Hatter sees 3 people 

madHatter.operate();

// output is :
// Mad Hatter sees 3 people

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM