2010-05-06 48 views
23

时间一般不超过原型的功能,我见过的类定义之外声明原型的功能,如:设置JavaScript对象类声明

function Container(param) { 
    this.member = param; 
} 
Container.prototype.stamp = function (string) { 
    return this.member + string; 
} 

var container1 = new Container('A'); 
alert(container1.member); 
alert(container1.stamp('X')); 

此代码将产生两个警报与价值观“A”和“AX” 。

我想定义类定义的原型函数INSIDE。做这样的事情有什么不妥吗?

function Container(param) { 
    this.member = param; 
    if (!Container.prototype.stamp) { 
     Container.prototype.stamp = function() { 
      return this.member + string; 
     } 
    } 
} 

我试图让我可以访问类中的私有变量。但我发现,如果我的原型函数引用私人变种,私营var值始终是当函数原型最初创建,而不是在对象实例的值被使用的值:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    if (!Container.prototype.stamp) { 
     Container.prototype.stamp = function(string) { 
      return privateVar + this.member + string; 
     } 
    } 
} 
var container1 = new Container('A'); 
var container2 = new Container('B'); 
alert(container1.stamp('X')); 
alert(container2.stamp('X')); 

此代码会生成两个值为“AAX”和“ABX”的警报。我希望输出将是“AAX”和“BBX”。我很好奇为什么这不起作用,如果有其他的模式我可以使用。

编辑:请注意,我完全理解,对于这个简单的例子,最好只使用像this.stamp = function() {}这样的闭包,而不要使用原型。我就是这么做的。但我使用的原型,以了解更多关于它的实验和想知道的几件事情:

  • 当它是有意义的使用原型的功能,而不是关闭?我只需要使用它们来扩展现有的对象,如Date。我读过closures are faster
  • 如果我需要使用原型函数出于某种原因,是否可以在类的内部定义它,比如在我的示例中,还是应该在外部定义?
  • 我想了解为什么每个实例的privateVar值不能被原型函数访问,只有第一个实例的值。
+0

瓶盖再删除线... – Dormilich 2010-05-06 22:09:14

+2

阅读上封(http://www.jibbering.com/faq/faq_notes/closures.html)的详细原因,你的代码的行为它的方式。 – outis 2010-05-06 22:09:28

回答

21

何时使用原型函数而不是闭包?

嗯,这是最轻便的路要走,让我们假设你有某些构造的prototype的方法,与您共创1000个对象实例,所有这些对象将在其原型链的方法,和所有其中只会涉及一个功能对象

如果您在构造函数中初始化该方法,例如(this.method = function() {};),所有1000个对象实例都将有一个函数对象作为自己的属性。

如果我需要使用原型函数出于某种原因,它是“OK”来定义它内部的类,就像在我的例子中,还是应该在外面定义?

定义构造函数原型的成员本身并没有什么意义,我会更详细地解释它,以及为什么代码不起作用。

我想了解为什么每个实例的privateVar值不能被原型函数访问,只有第一个实例的值。

让我们来看看你的代码:

var Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    if (!Container.prototype.stamp) { // <-- executed on the first call only 
     Container.prototype.stamp = function(string) { 
      return privateVar + this.member + string; 
     } 
    } 
} 

你的代码的行为,关键的一点是,Container.prototype.stamp功能是在第一个方法调用创建

当前您创建一个函数对象,它将当前的封闭范围存储在名为[[Scope]]的内部属性中。

当您调用该函数时,通过使用var或函数声明在其中声明的标识符(变量),此范围稍后会增大。

[[Scope]]的属性列表形成范围链,并且当访问的标识符(例如您privateVar变量),被检查的那些对象。

由于您的函数是在第一个方法调用(new Container('A'))上创建的,因此privateVar被绑定到第一个函数调用的作用域,并且无论您如何调用该方法,它都将保持绑定状态。

看看这个answer,第一部分是关于with声明,但在第二部分中,我将讨论范围链如何适用于函数。

+0

@CMS:很好的解释,非常感谢!所以如果我的原型函数只访问'this'而不是私人变量,那么它会正常工作,即使它被定义在类内部,对吧?有没有这样做的缺点,而不是像我通常看到的那样在类之后声明原型函数? – Tauren 2010-05-07 01:38:39

+1

@Tauren,是的,有一个缺点,例如在代码中会出现内存泄漏,在构造函数中的第一次调用中声明的所有变量都不会被垃圾回收,因为如你所知,封闭范围即使在构造函数结束其执行之后(创建闭包),Container.prototype.stamp函数的创建位置仍然可以访问。出于这个原因,一些图书馆像[Google's Closure](http://code.google.com/closure/library/)避免了* private *成员的关闭,他们只是简单地遵守命名约定。 'OBJ .__ privateMember'。 – CMS 2010-05-07 02:12:24

+0

非常有意义,谢谢! – Tauren 2010-05-07 05:16:36

1

你需要把功能上的每一个具体的实例,而不是原型的,像这样的:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 

    this.stamp = function(string) { 
     return privateVar + this.member + string; 
    } 
} 
+0

@Slaks:我已经修改了这个问题,以便更清楚我究竟在问什么。 – Tauren 2010-05-06 22:22:16

1

为了得到你想要的行为,你需要在每个单独的对象分配不同stamp()功能与独特关闭:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    this.stamp = function(string) { 
     return privateVar + this.member + string; 
    } 
} 

当您在原型创建一个单一功能的每个对象使用相同的功能,与功能收盘在第一个集装箱的privateVar

通过每次调用构造函数时分配this.stamp = ...,每个对象都将获得自己的stamp()函数。这是必要的,因为每个stamp()需要关闭一个不同的privateVar变量。

+0

@约翰:谢谢你的回答。我想我的问题还不够清楚,所以我修改了它。请看看编辑。 – Tauren 2010-05-06 22:23:33

0

这是因为privateVar不是该对象的私有成员,而是图章封闭的一部分。您可以通过创建始终函数得到的效果:

Container = function(param) { 
    this.member = param; 
    var privateVar = param; 
    this.stamp = function(string) { 
     return privateVar + this.member + string; 
    } 
} 

的功能是建立时的privateVar的值设置,所以你必须在每次创建它。

编辑:修改不设置原型。

+0

@凯茜:谢谢,我同意封闭是要走的路。请参阅我编辑的问题,因为我添加了更多关于我正在尝试提问的详细信息。 – Tauren 2010-05-06 22:21:26

+0

@Tauren变量只被设置为第一个privateVar的原因是该函数只创建一次,所以闭包只创建一次。 – 2010-05-06 22:59:17

+0

感谢您的澄清,我明白现在发生了什么。 – Tauren 2010-05-07 01:30:12

11

对不起,复活一个老问题,但我想补充一点,就是我最近发现其他地方在这里SO (寻找链接,将编辑/添加它,一旦我找到它) found it

我个人喜欢下面的方法,因为我可以直观地将我的所有原型和'实例'定义与函数定义一起分组,同时避免多次评估它们。它还提供了一个机会来关闭你的原型方法,这对于创建由不同原型方法共享的“私有”变量非常有用。

var MyObject = (function() { 
    // Note that this variable can be closured with the 'instance' and prototype methods below 
    var outerScope = function(){}; 

    // This function will ultimately be the "constructor" for your object 
    function MyObject() { 
     var privateVariable = 1; // both of these private vars are really closures specific to each instance 
     var privateFunction = function(){}; 
     this.PublicProtectedFunction = function(){ }; 
    } 

    // "Static" like properties/functions, not specific to each instance but not a prototype either 
    MyObject.Count = 0; 

    // Prototype declarations 
    MyObject.prototype.someFunction = function() { }; 
    MyObject.prototype.someValue = 1; 

    return MyObject; 
})(); 

// note we do automatic evalution of this function, which means the 'instance' and prototype definitions 
// will only be evaluated/defined once. Now, everytime we do the following, we get a new instance 
// as defined by the 'function MyObject' definition inside 

var test = new MyObject(); 
+0

这是一个非常酷的想法,感谢分享它! – Tauren 2010-11-20 12:59:24

+0

这正是我需要的,非常感谢你! – seahorsepip 2016-01-13 15:55:05