自动执行函数与对象的闭包

时间:2011-11-22 18:21:28

标签: javascript object closures

我们说我有以下内容:

var foo = (function(){
    var bar = 0;
    return {
       getBar: function(){
           return bar;
       },
       addOne: function(){
           bar++;
       },
       addRandom: function(rand){
           bar += rand;
       }
    }
})();

我有以下内容:

var foo2 = function(){
    var bar = 0;
    this.getBar = function(){
           return bar;
       };
    this.addOne = function(){
           bar++;
       };
    this.addRandom = function(rand){
           bar += rand;
       }
};

执行new函数的唯一区别是什么?

alert(foo.getBar()); //0
foo.addOne();
foo.addRandom(32);
alert(foo.getBar()); //33

var foo2_obj = new foo2;
alert(foo2_obj.getBar());//0
foo2_obj.addOne();
foo2_obj.addRandom(32);
alert(foo2_obj.getBar());//33

他们两个都完全相同。

那么从长远来看有什么不同?

另一个人不能做什么呢?

以上的小提琴演示:http://jsfiddle.net/maniator/YtBpe/

7 个答案:

答案 0 :(得分:14)

在第一个中你只能创建一次对象,而第二个你可以创建任意数量的对象。 I.E.第一个实际上是一个单身人士。

请注意,第二个闭包不适用。每次实例化它都会重新创建函数并浪费大量内存。原型对象旨在解决这个问题,您可以在函数范围之外创建函数,并且不会创建意外的闭包。

function foo2(){
    this._bar = 0;
}

foo2.prototype = {

    constructor: foo2,

    getBar: function(){
        return this._bar;
    },

    addOne: function(){
        this._bar++;
    },

    addRandom:function(rand){
        this._bar += rand;
    }

};

然后:

var a = new foo2, b = new foo2, c = new foo2;

创建三个具有自己的_bar但共享相同功能的实例。

jsperf

你可以将所有这些与“PHP”进行“比较”,有些代码甚至不会运行,但原则上它是“等效的”:


var foo = (function(){
    var bar = 0;
    return {
       getBar: function(){
           return bar;
       },
       addOne: function(){
           bar++;
       },
       addRandom: function(rand){
           bar += rand;
       }
    }
})();

与PHP大致“等同”:

$foo = new stdClass;

$foo->bar = 0;

$foo->getBar = function(){
    return $this->bar;
};

$foo->addOne = function(){
    $this->bar++;
}

$foo->addRandom = function($rand){
    $this->bar += $rand;
}

var foo2 = function(){
    var bar = 0;
    this.getBar = function(){
        return bar;
    };
    this.addOne = function(){
        bar++;
    };
    this.addRandom = function(rand){
        bar += rand;
    }
};

在PHP中大致“等同于”:

Class foo2 {


    public function __construct(){
    $bar = 0;

        $this->getBar = function(){
            return $bar;
        };
        $this->addOne = function(){
            $bar++;
        };
        $this->addRandom = function($rand){
            $bar += rand;
        };


    }

}

function foo2(){
    this._bar = 0;
}

foo2.prototype = {

    constructor: foo2,

    getBar: function(){
        return this._bar;
    },

    addOne: function(){
        this._bar++;
    },

    addRandom:function(rand){
        this._bar += rand;
    }

};

在PHP中大致“等同于”:

Class foo2 {

    public $_bar;

    public function __construct(){
        $this->_bar = 0;    
    }

    public function getBar(){
        return $this->_bar;    
    }

    public function addOne(){
        $this->_bar++
    }

    public function addRandom($rand){
        $this->_bar += $rand;
    }

}

...并且是上面三个例子中唯一接近OOP的那个


答案 1 :(得分:4)

唯一的区别是foo将是通用Object,而foo2_obj在检查其类型时会识别为foo2(即foo2_obj.constructor == foo2将是true,而foo上的等价物是foo.constructor == Object)。

当然,foofoo2之间存在重要区别 - foo是一个对象,而foo2是一个函数(用作构造函数)。因此,制作尽可能多的foo2(其中foo2_obj为1)的实例是微不足道的,而创建foo的“实例”的想法并不真正有意义 - 你能做的最好的就是副本(这比调用构造函数更难)。

由于复制/创建实例的区别,第二种方法允许使用原型链进行真正的OO编程,而第一种方法使得这些事情很多更难(并且是不明智的)。

答案 2 :(得分:1)

[1]首先,但不重要:效率

function Foo1() {
    var bar = 0;
    return {
        getBar: function () {
            return bar;
        }
    }
}
var o = Foo1();
o.getBar();


function Foo2() {
    var bar = 0;
    this.getBar = function () {
        return bar;
    }
}
var o = new Foo2();
o.getBar();

哪个更快?,看object-literal-vs-new-operate

[2]程序模式:前者没有程序模式,但后者将受益于prototypal inheritance形式。如果现在我们要添加一个名为“logBar”的方法,

前者:

1:扩展每个 Foo1实例:

o.logBar = function () {
    console.log(this.getBar());
}
o.logBar();

糟糕的方式!

2:找到Foo1定义的位置并添加:

function Foo1() {
    var bar = 0;
    return {
        getBar: function () {
            return bar;
        },
        logBar:function () {
            console.log(this.getBar());
        }
    }
}
var o = Foo1();
o.logBar = o.logBar();

如果你想增加更多的方法时间,你想回去做吗?

后者:

Foo2.prototype.logBar = function () {
    console.log(this.getBar());
}

var o = Foo2();
o.logBar = o.logBar();

这样可以正常工作。

[3]回到效率: 在Foo1的方式中,它产生logBar函数实例在创建Foo1实例时的时间。object-literal-vs-new-operate

答案 3 :(得分:0)

foofoo2_obj他们是一样的。在这两种情况下,您都有一个创建新对象的函数,在闭包范围内引用变量并返回该对象。

你有4件事

  • 匿名函数,是“foos”的工厂
  • 从匿名工厂创建的对象foo
  • foo2,是“foo2_objs”
  • 的名称工厂
  • 从foo2 factory
  • 创建的对象foo2_obj

如果您不触及new

,使用<Function>.prototype和从函数返回函数文字之间的确切差异是可以忽略的

您可能想比较

var foo2 = function(){
    var bar = 0;
    this.getBar = function(){
           return bar;
       };
    this.addOne = function(){
           bar++;
       };
    this.addRandom = function(rand){
           bar += rand;
       };
};

var Foo = {
  addOne: function () { this.bar++; },
  addRandom: function (x) { this.bar+=x; }
};

var foo3 = function () {
  return Object.create(Foo, { bar: { value: 0 } });
}

foo3使用原型OO。这意味着您不必一直重新创建这些功能。

答案 4 :(得分:0)

我认为在我个人看来这两种类型中 1-单身人士 2-对象

让我们说我们有一个页面让他们的javascript使用Object(Second),和 有许多使用单例的工具(第一),并且工作正常。

但是有一天我们需要一个通过AJAX调用第一页的新页面,这个新页面使用Object(Second)使用javascript并使用singleton具有相同的util,但是我们在utils单例中添加了一些新函数。 / p>

事实证明,新页面中的utils单例重写了第一页中加载的utils单例,所以当新页面执行时,其中一些新函数不存在,生成错误......

我认为这是我的观点,当你有这个场景时,单身人士会被覆盖,并且在这样的情况下发现错误很难......很难...,与具有唯一实例的对象不同

干杯。

答案 5 :(得分:0)

简单来说,如果您要创建10个foofoo2的实例,则getBar的{​​{1}}函数将在内存中存在10次,而{{1}的函数将存在10次只会一次。

此外,现代浏览器如Chrome和V8编译器,它将js编译为机器代码......在这种情况下,foo将被转换为本机类对象,它的速度提高了20倍(当你创建时)说一个循环中的1000个实例)


当只需要一个类/模块的实例时,我通常使用简单对象方法。我遵循的结构是,

foo2

这与foo2方法非常相似,但我觉得这个结构更方便。


我通常遇到的另一个不同之处(实际使用中)是我在课堂上有var myInstance = function(){ var self = {}; self.bar = null; self.gerBar = function(){ return self.bar } return self; }(); 个函数或foo时,

callback

正如你可以看到当你有很多这些情况时,本地临时变量并不漂亮。

在另一种方法中,您始终是模块范围内的timeouts引用,

var foo2 = function(){
    this.doSomething = function(){
        var temp = this;
        $.someAsyncCall(function(){
           // 'this' in current scope is the inline function itself, not the class instance
           // so have to store the instance ref in a local var in outer scope and then use that to get the class instance
           temp.callAfterAsyncCall();
        });
    };
    this.callAfterAsyncCall = function(){
    };
};

我不确定它对你来说是否重要,但只是值得一提。

答案 6 :(得分:0)

主要区别在于foo 是一个对象,而foo2是一个函数。

这意味着您将无法创建另一个实际上不是foo的{​​{1}}对象,除非您复制/粘贴其代码。 / p>

另一方面,您可以创建另一个foo对象并在将foo2用于其他目的时对其进行操作。

简而言之,foo2_obj是一个实例,而foo 可以被视为一个类(即使它只是一个构建一个函数的函数)对象)。

这取决于你想在程序中做什么,但我肯定建议使用第二种形式,它允许通过创建其他实例来重用你的代码。