自动执行函数与对象中的闭包
Closures in auto executing functions vs objects
假设我有以下内容:
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/
在第一个中,您只能创建一次对象,而在第二个中,您可以根据需要创建任意数量的对象。 即第一个实际上是单例。
请注意,第二个关闭是不行的。每次实例化它时,您都在重新创建函数并浪费大量内存。原型对象旨在解决这个问题,您可以在函数范围之外创建函数一次,并且不会创建意外闭包。
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 的
唯一的区别是foo
将是一个通用Object
,而foo2_obj
在检查其类型时将标识为foo2
(即 foo2_obj.constructor == foo2
将是true
,而foo
上的等价物是foo.constructor == Object
(。
当然,foo
和 foo2
之间有一个重要的区别 - foo
是一个对象,而foo2
是一个函数(旨在用作构造函数(。 因此,创建尽可能多的foo2
实例(foo2_obj
就是其中之一(是微不足道的,而创建foo
的"实例"的想法并没有真正的意义 - 你能做的最好的事情是复制(这比调用构造函数更困难(。
由于复制/创建实例的区别,第二种方法允许使用原型链进行真正的OO编程,而第一种方法使这些事情变得更加困难(并且不明智(。
[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();
哪个更快,看对象文字与新操作
[2]程序模式:前者没有程序模式,但后者将有利于原型继承形式,如果现在我们要添加一个名为"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 的方式,当创建 Foo1 实例时,它logBar
函数实例的生成时间.object-literal-vs-new-operation
我认为在我个人对这两种类型
的看法中1- 单例
2- 对象
假设我们有一个页面使用对象(第二个(的javascript,并且有许多使用单例(第一(的实用程序,并且工作正常。
但是有一天我们需要一个新页面通过 AJAX 调用第一个页面,这个新页面使用 Object (Second( 的 javascript 并且使用单例具有相同的实用程序,但我们在 utils 单例中添加了一些新功能。
事实证明,新页面中的 utils 单例被覆盖为第一页中加载的 utils 单例,因此当新页面执行其中一些新函数时不存在,产生错误......
我认为这就是我的观点,当您遇到这种情况时,单例会被覆盖,并且在这样的情况下找到错误很难......,与具有唯一实例的对象不同
干杯。
主要区别实际上在于foo
是一个对象,而foo2
是一个函数。
这意味着您将无法创建另一个对象,例如实际上没有foo
自身的对象foo
,除非您复制/粘贴其代码。
另一方面,您可以创建另一个foo2
对象并对其进行操作,同时将foo2_obj
用于其他目的。
简而言之,foo
是一个实例,而foo2
可以被视为一个类(即使它只是一个构造对象的函数(。
这取决于你想在你的程序中做什么,但我肯定建议使用第二种形式,它允许通过创建其他实例来重用你的代码。
foo
和 foo2_obj
它们是相同的。在这两种情况下,您都有一个函数,该函数创建一个新对象,引用闭包范围内的变量并返回该对象。
你有 4 件事
- 匿名函数,是"foos"的工厂
- 从匿名工厂创建的对象 foo
- foo2 是"foo2_objs"的名称工厂
- 从 FOO2 工厂创建的对象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。 这意味着您不必一直重新创建这些函数。
来说,如果你正在创建 10 个 foo
和 foo2
的实例,foo
的getBar
函数将在内存中存在 10 次,而foo2
的函数将只存在一次。
此外,现代浏览器,如带有 V8 编译器的 chrome,它将 js 编译为机器代码......在这种情况下,foo2
将被转换为本机类对象,并且速度快 20 倍(当您在循环中创建 1000 个实例时(
当只需要该类/模块的一个实例时,我通常使用简单对象方法。我遵循的结构是,
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(){
};
};
如您所见,当您有很多这种情况时,局部 temp 变量并不漂亮。
在另一种方法中,您始终在模块范围内的任何地方引用self
,
var myInstance = function(){
var self = {};
self.doSomething = function(){
$.someAsyncCall(function(){
self.callAfterAsyncCall();
});
}
self.callAfterAsyncCall = function(){
};
return self;
}();
我不确定它对你是否重要,但只是认为值得一提。
- 为什么 Google 闭包编译器会重命名对象的属性名称
- JSHint 错误“对象不支持此属性或方法”用于 javascript 闭包
- Javascript对象/闭包
- 使用闭包编译器编写更好的面向对象JavaScript完整示例代码
- 对象的闭包编译器问题
- JavaScript中带有构造函数的对象和闭包之间的区别
- 如何在 Google 闭包编译器中指定对象类型的@param @return
- Google 闭包编译器中的“全局此对象的危险使用”警告
- 这个 getter-setter 闭包是否有充分的理由以这种方式在其内部对象中声明它是私有的
- 测试对象是否是 Google 闭包类框架中接口的实现
- 如果闭包的行为类似于对象,它们是对象吗?(好像不像)
- JavaScript闭包:在对象中动态存储组失败
- 如何强制 Google 闭包编译器重命名方法,即使使用对象也是如此
- 仅在闭包中定义的模拟/存根对象
- 对闭包内对象的赋值的行为类似于指针
- 从 JavaScript 闭包中访问对象的值
- JQuery 通过闭包绑定到对象方法
- Javascript中对象和闭包的有趣和奇怪的行为
- Javascript 闭包(使用参数名称在同一函数中定义对象)
- 闭包对象创建:这两种方法的优点/缺点是什么