3

我想知道在闭包中使用函数构造函数时是否存在任何内存或性能问题?

这是一个粗略的示例,我知道有很多不同且更好的方法来编写它,我只是想提供一个示例,每个构造函数现在都可以访问闭包中的变量(farmer、lastToSpeak 和动物)。

// usage: myFarm = new Farm(["Cow","Goat"],"Old MacDonald");

function Farm(animalList, farmer){
    var animals = [],
        lastToSpeak = "";

    function Cow(){
        this.speak = function(){
            alert("I'm a Cow and I belong to "+farmer);
        }
        lastToSpeak = "A Cow";
    }
    function Sheep(){
        this.speak = function(){
            alert("I'm a Sheep and I belong to "+farmer);
        }
        lastToSpeak = "A Sheep";
    }
    function Goat(){
        this.speak = function(){
            alert("I'm a Goat and I belong to "+farmer);
        }
        lastToSpeak = "A Goat";
    }

    for(var i = 0; i < animalList.length; i++){
        switch(animalList[i]){
            case "Cow":
                animals.push(new Cow());
                break;
            case "Sheep":
                animals.push(new Sheep());
                break;
            case "Goat":
                animals.push(new Goat());
                break;
        }
    }

    this.allSpeak = function(){
        for(var i = 0; i < animals.length; i++){
            animals[i].speak();
        }
    }
}
myFarm = new Farm(["Cow","Goat"],"Old MacDonald");
myFarm.allSpeak();

我猜在这个例子中它没有什么区别,但是如果牛、绵羊和山羊更大更复杂,并且我们正在创建许多农场,这种方法会有什么缺点吗?

每次创建农场时都会将一组新的构造函数存储到内存中吗?

更新

因此,我对 Kemal Dağ 所说的以及 Bergi 的评论感到满意。

如果我按照 Bergi 的建议更改代码以使用原型并在农场中传递,这似乎是更好的方法吗?

function Farm(animalList, farmer){
    var animals = [],
        lastToSpeak = "";

    this.farmer = farmer;

    for(var i = 0; i < animalList.length; i++){
        switch(animalList[i]){
            case "Cow":
                animals.push(new this.Cow(this));
                break;
            case "Sheep":
                animals.push(new this.Sheep(this));
                break;
            case "Goat":
                animals.push(new this.Goat(this));
                break;
        }
    }

    this.allSpeak = function(){
        for(var i = 0; i < animals.length; i++){
            animals[i].speak();
        }
    }
}
Farm.prototype.Goat = function(farm){
    this.speak = function(){
        alert("I'm a Goat and I belong to "+farm.farmer);
    }
    farm.lastToSpeak = "A Goat";
}
Farm.prototype.Cow = function(farm){
    this.speak = function(){
        alert("I'm a Cow and I belong to "+farm.farmer);
    }
    farm.lastToSpeak = "A Cow";
}
Farm.prototype.Sheep = function(farm){
    this.speak = function(){
        alert("I'm a Sheep and I belong to "+farm.farmer);
    }
    farm.lastToSpeak = "A Sheep";
}

myFarm = new Farm(["Cow","Goat"],"Old MacDonald");
myFarm.allSpeak();

更新

我整理了一个小提琴,而不是在此处为问题添加另一个版本。我已经完全分离了我的动物构造函数并将 speakAll() 移到原型中。我想我真的在寻找一种解决方案,它允许我在我的实例之间共享变量而不向全局范围添加任何东西。我最终决定将一个对象传递给每个实例而不是构造函数,这意味着我不必在构造函数上公开它们。多谢你们。

4

2 回答 2

3

在 javascript 中,每个函数本身都是一个对象,因此您的问题的答案是肯定的。每次创建 Farm 对象时,您也会创建其所有私有方法,它们是:Cow、Sheep、Goat。

为了防止这种情况,请在 Farm 对象的原型中创建函数。这可以防止为这些函数重新分配内存,但它们会立即成为公共函数。看:

Farm.prototype.Goat = function(farmer){
        this.speak = function(){
            alert("I'm a Goat and I belong to "+farmer);
        }
        this.lastToSpeak = "A Goat";
}

您必须决定私人功能对您是否至关重要?然后原型方法要好得多。

但请记住,在 javascript 中使用原型有点棘手,所以我不得不修改代码的某些方面,你可以在这里看到一个工作示例。

在该示例中,您会发现this.lastToSpeak分配从根本上是错误的。由于您使用在 Farm 对象原型上创建的函数作为 Object 构造函数本身。然后this变成对 Goat 对象而不是 Farm 对象的引用,因此,如果您想引用父级 Farm 对象,您可能会将对父级的引用传递给 Goat 构造函数。请记住,原型继承与标准类继承完全不同,但您几乎可以模拟您想要的任何行为。

于 2013-09-12T10:43:26.413 回答
2

每次创建农场时都会将一组新的构造函数存储到内存中吗?

是的。但是,如果他们真的需要访问闭包变量(如lastToSpeak),这可能是合理的,就像特权方法一样。拥有私有构造函数可能有点奇怪,但可能是必需的。

当您处理一组相似(但范围不同)的构造函数时,为它们提供一个通用原型对象可能是一种性能优化。如果原型方法也不需要访问闭包变量,则在Farm构造函数之外使用静态对象并将其分配给每个特权构造函数。

我更改了代码以使用原型,这似乎是更好的方法吗?

不是这样的。构造函数不应该是实例方法,将它们放在原型对象上很奇怪。Farm最好将它们作为命名空间放在函数对象上。

这是一个公开一切,崇拜原型的例子:

function Farm(animalList, farmer){
    this.farmer = farmer;
    this.lastToSpeak = "";
    this.animals = [];

    for(var i = 0; i < animalList.length; i++){
        var name = animalList[i];
        if (Farm.hasOwnProperty(name))
            this.animals.push(new Farm[name](this));
    }
}
Farm.prototype.allSpeak = function(){
    for(var i = 0; i < this.animals.length; i++){
        this.animals[i].speak();
    }
};

function Animal(farm) {
    this.farm = farm;
    farm.lastToSpeak = "A "+this.type;
}
Animal.prototype.speak = function(){
    alert("I'm a "+this.type+" and I belong to "+this.farm.farmer);
};
Animal.create = function(name) {
    Farm[name] = function() {
        Animal.apply(this, arguments);
    };
    Farm[name].prototype = Object.create(Animal.prototype);
    Farm[name].prototype.type = name;
};
Animal.create("Goat");
Animal.create("Sheep");
Animal.create("Cow");

myFarm = new Farm(["Cow","Goat"],"Old MacDonald");
myFarm.allSpeak();
于 2013-09-12T11:02:06.070 回答