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

Closures in auto executing functions vs objects

本文关键字:闭包 对象 执行 函数      更新时间:2023-09-26

假设我有以下内容:

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(。

当然,foofoo2 之间有一个重要的区别 - 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可以被视为一个类(即使它只是一个构造对象的函数(。

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

foofoo2_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 个 foofoo2 的实例,foogetBar函数将在内存中存在 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;
}();

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