Javascript - 在闭包中使用函数构造函数是一个坏主意吗?

时间:2013-09-12 10:37:13

标签: javascript performance memory

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

这是一个粗略的例子,我知道有很多不同的和更好的方法来写这个,我只想提供一个例子,每个构造函数现在可以访问闭包中的变量(农民,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();

我在这个例子中猜测它没什么区别,但如果牛,羊和山羊更大更复杂,而且我们创造了很多农场,那么这种方法有什么不利之处吗?

每次创建服务器场时,是否会将一组新的构造函数存储到内存中?

更新

所以我很高兴KemalDağ所说的话以及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();

更新

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

2 个答案:

答案 0 :(得分: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中有点棘手,所以我不得不修改代码的某些方面,你可以看到一个有效的例子here.

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

答案 1 :(得分: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();