【问题标题】:Why javascript function has instance per call?为什么javascript函数每次调用都有实例?
【发布时间】:2016-07-14 02:57:21
【问题描述】:

javascript介绍说: 当我有如下代码时:

var Person=function(name){ 
     this.name=name; 
     this.sayHello=function(){return 'Hello '+name;} 
} 

每当我实例化一个“Person”时,内存中都会有一个“sayHello”函数的副本。为了减少这种内存消耗,我可以更改如下代码:

var Person=(function(){ 
     var sayHello=function(){return 'Hello '+name} 
     return function(name){ 
         this.name=name 
         this.sayHello=sayHello 
     } 
})() 

这样,sayHello()就不会有多个副本了

我的问题是:

  1. 对于第一种类型的代码,除了浪费更多内存还有什么好处?
  2. 我们应该以第二种方式编写代码,还是 javascript 应该避免每个实例一个函数一个副本?

非常感谢。

【问题讨论】:

  • 或选项 3,Person.prototype.sayHello = function() { ... }。您的函数的第一个版本有点奇怪,因为它只返回name原始 值,而不是this.name 的当前值。您的函数的第二个版本实际上不起作用,因为它尝试访问的变量 name 与返回的函数中的 name 参数不同。
  • @nnnnnn:实际上这就是为什么第二个版本根本不起作用的原因,它会产生window.name 的值(如果你在没有@ 的情况下调用Person,它仍然可能有一个值987654330@).
  • @Bergi - 是的,我只是在编辑我的评论以提及这一点。尽管您在没有new 的情况下调用Person() 的额外点非常重要,我没想到要提及这一点。
  • 您应该只从构造函数内部将实例方法分配给this,当该方法需要访问作用域为构造函数的变量/函数时。 所有其他实例方法都应该分配给原型。
  • “内存中函数的副本”可能不是很昂贵。是的,它比不这样做的解决方案更昂贵,但不是很多。该函数的代码是不可变的,因此它可以被所有实例共享。如果您要制作 1,000,000 个 Person 实例,那么这很重要,但如果您要制作 10 或 100 个实例,则可能无关紧要。

标签: javascript function object instance


【解决方案1】:

您目睹的行为是两件事的结果:

  1. 函数作为一等对象。这意味着函数的处理方式与字符串、数字、数组等相同。

  2. 如何在函数中处理局部变量。每次调用函数时都会创建局部变量(通常在堆栈上)。这允许递归调用函数。

这种行为存在于许多具有匿名函数的不同语言中,例如 Go、Perl、Lisp 等。

上述两条规则意味着每次调用函数时,都会创建内部函数并将其分配给变量。

这样做有什么好处?

从语言的角度来看,这样做的主要优点是行为的一致性。这意味着函数确实被视为一等对象,就像数字、字符串等一样。始终如一地对待它意味着尝试使用匿名函数的人不会对这种行为感到惊讶。

人们如何使用此功能?

有时您会发现自己编写了几个看起来相似的不同函数:

function double (x) {return x * 2};
function quadruple (x) {return x * 4};

能够将相似的函数“家族”归类并以某种方式编写一次,这不是很好吗?

嗯,在像 C 这样的语言中,您可以使用宏系统基本上剪切和粘贴您键入的文本以生成几个不同的代码。

在具有一流函数的语言中,您可以编写一个函数来生成函数:

function makeMultiplier (factor) {
    return function (x) { return x * factor }
}

所以现在你可以这样做了:

var double = makeMultiplier(2);
var quadruple = makeMultiplier(4);

现在显然要让makeMultiplier() 函数必须返回两个不同的函数。它不能只修改单个函数以在每次调用时执行不同的操作。否则,double()quadruple() 函数都将在第二次调用 makeMultiplier() 后乘以 4。


实现细节

可以创建一个系统,其中内部函数的主体只编译一次,而差异由闭包捕获。所以所有函数只占用一次 RAM,但一个函数的不同版本可能占用不止一个闭包。这可能是大多数 js 引擎的实现方式,但我真的不知道。如果是这样,那么内部函数 do 每次定义它们时都会占用额外的 RAM,但不会占用太多(通常是一个堆栈帧 - 因此每个函数定义占用与函数调用相同的空间)。

但从程序员的角度来看,内部函数必须看起来像每次调用都创建它们一样工作,因为这是您期望它们工作的方式。

【讨论】:

    【解决方案2】:

    对于第一种类型的代码,除了浪费更多内存还有什么好处?

    我唯一一次使用这样的东西是为了快速测试一个匿名对象。这方面的一个例子是在某种 factory 实现中:

    getWidget = function(){
        return {
            foo: 'bar',
            test: function(){ ... }
        }
    }
    

    老实说,在我确认我的初始测试后,这将很快被正确的编码约定所取代。

    我们应该以第二种方式编写代码,还是 javascript 应该避免每个实例一个函数一个副本?

    我会说不。不要写这样的代码。它不必要地令人费解,而且据我所知似乎不起作用。我建议只在函数原型上创建方法:

    var Person = function(name){
        this.name = name;
    }
    
    Person.prototype.sayHello = function(){
        return 'Hello ' + this.name;
    }
    

    优点 - 易于阅读 - 易于管理 - this 在调用方法的上下文中被正确定义

    缺点 - 编码工作的名义增加 - 根据对该问题的评论,您将无法访问仅限于构造函数的变量

    【讨论】:

      【解决方案3】:

      对于第一种类型的代码,除了浪费更多内存还有什么好处?

      这可用于访问控制(如在某些其他语言中的private)。

      考虑:

      var Person=function(name){ 
         this.sayHello = function(){return 'Hello '+name;}
      };
      

      name只能被sayHello访问,这是一个不透明的Function。

      其他代码可以替换this.sayHello,但不能让这个sayHello实例使用另一个name

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-10
        • 2017-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-29
        • 2018-06-13
        相关资源
        最近更新 更多