Javascript:函数中的变量赋值

时间:2011-08-15 17:54:52

标签: javascript

考虑以下两个方块:

阻止A

obj = {
    a: 1,
    b: 2,
    c: 3,
    f: function() {
        alert(this.a);
        alert(this.b);
        alert(this.c);
    }
}

B座

obj = {
    a: 1,
    b: 2,
    c: 3,
    f: function() {
        var a = this.a;
        var b = this.b;
        var c = this.c;
        alert(a);
        alert(b);
        alert(c);
    }
}

一种方式比另一方更正确/有效吗?当然,这是一个简洁的例子 - 在我的代码中有更多的变量,我想要做的是通过不为每个函数中的this.varName重新分配当前函数范围的变量来节省时间。它有效,但它是否正确?

编辑:为了澄清,变量将在整个函数中广泛使用。普遍的共识似乎是,为此,通过地方范围进行重新分配是可行的方法。

12 个答案:

答案 0 :(得分:7)

取决于。如果您只想使用该值一次,则添加存储的开销然后检索该值是没有意义的。另一方面,如果在函数范围内多次引用该值,则仅获取一次它是有意义的。

答案 1 :(得分:4)

f: function() {
    a = this.a;
    b = this.b;
    c = this.c;
    alert(a);
    alert(b);
    alert(c);
}

不仅全局分配和查找效率较低,而且您正在污染全局范围,因为a = this.a正在分配给全局a

编辑:

我们假设this.athis.b导致getter触发,alert(a)调用toString方法调用值a

之间存在操作顺序差异
var a = this.a, b = this.b;
alert(a); alert(b);

确实(得到a,得到b,a toString,b toString)和

alert(this.a); alert(this.b);

做(获取a,toString,获取b,b toString)和

可能有充分的理由偏好一个操作顺序到另一个,但效率方面,第二个可能更好。

由于操作顺序不同,当只有一个成员使用时,你不应该依赖一个保留语义的JavaScript缩小器来优化第一个到第二个。

答案 2 :(得分:3)

一切都取决于。如果您只在函数中访问该属性一次,则第一个更快。如果您多次访问它,则将修改后的版本用作第二个代码块会更快。

更改第二个版本以将abc声明为f()的本地变量将避免对范围链进行多次扫描并遍历{{1 - 再次,这是你需要多次访问这些属性。

答案 3 :(得分:3)

Nicholas Zakas mentions this as a way to speed up your JavaScript.来自视频摘要:

  

与全局变量类似,可以通过创建局部变量来保存对象属性和多次引用的数组项来提高性能。另外,请记住,更深层次的对象属性和数组项查找(例如,obj.name1.name2.name3)更慢。

答案 4 :(得分:1)

第一种方式,如果相对更高效,因为在第二种方式,你正在制作变量的副本,因此将占用所有变量和更多内存空间的一个额外语句(由代码和变量) )。

答案 5 :(得分:1)

您在示例中遗漏了一些关键内容。我只关注f函数,假设其余代码是相同的:

如果您只是访问存储在对象上的值,那么就没有理由存储临时变量,它只会使工作变得无聊:

function () {
  //use the values as they are
  alert( this.a );
  alert( this.b );
  alert( this.c );
}

但是,如果您正在执行计算并需要临时缓存结果以供重用,那么使用局部变量。确保它们不会污染全局范围(window对象);使用var使变量仅在本地持久存在。

function () {
  var foo;
  foo = this.a / this.b + this.c;

  alert( this.a * foo );
  alert( this.b / foo );
  alert( this.c + foo );
}

编辑添加:

引用了两种不同类型的变量。附加到对象的变量(使用this.varnamethis['varname']访问)和仅存在于本地范围内的变量(使用var varname声明并使用varname访问)。

附加到对象的任何变量都可以公开访问,并且应该用于显示跨函数调用的数据或持久性。函数内声明的任何变量只能在函数的上下文中访问,因此对函数是私有的。它们不会在调用之间保留值,但是它们可用于跨子函数调用保留数据。

Block A Block B 之间, Block A 是与对象数据交互的首选方法,但在大多数情况下是函数执行更多一系列操作,通常涉及更复杂的行为。如果函数包含需要this.athis.bthis.c值的回调,则需要使用别名来传递数据,因为this会在上下文之间发生变化

这不会像预期的那样提醒123

f:function ()
{
  $(foo).click(function g(){
    //`this` does not refer to the object `f` belongs to, but the element being clicked on
    //and therefor is not likely to work as expected
    alert( this.a );
    alert( this.b );
    alert( this.c );
  });
}

此版本

f:function()
{
  var a,b,c;
  a = this.a;
  b = this.b;
  c = this.c;
  $(foo).click(function g(){
        alert( a );
        alert( b );
        alert( c );
  });
}

答案 6 :(得分:0)

如果您可以稍微更改对象结构,请尝试将所需的属性放在容器(z)中,如下所示:

obj = {
    z: {
        a: 1,
        b: 2,
        c: 3
    },
    f: function() {
        var z = this.z;
        for(var prop in z) {
            alert(z[prop]);
        }
    }
}

答案 7 :(得分:0)

1)您应该使用var来声明变量,否则它们将是全局变量。

2)是的,复制值可以节省您输入的时间并且执行速度更快,因为访问对象属性并不是一个非常便宜的操作。 JavaScript没有实现数组;他们总是哈希。

答案 8 :(得分:0)

var obj = {
    a: 1,
    b: 2,
    c: 3,
    f: function () {
        for (var property in this) {
            if (property != "f")
                alert(property + "=" + this[property]);
        }
    }
};
obj.f();

答案 9 :(得分:0)

我希望你的意思是将var放在Block B中每个变量声明的前面。否则你正在做一些你可能不打算做的事情。您可以在全局对象的属性a,b,c上设置值,而不是将值设置为您要分配给属性f的匿名函数所独有的变量。

我希望您的意思是将var放在匿名函数中的每个变量a,b和c的前面。

根据Nicholas Zakas撰写的书High Performance Javascript,他提到了标识符解析所涉及的成本(查找您正在使用的变量名称)。他指出局部变量是最便宜的,而全局变量是最昂贵的。

因此,在没有var的情况下给出的示例(块B)中,您正在做的事情很昂贵。如果你有var就位,你正在做的是识别标识符的最佳选择。

如果您对JS的表现感兴趣,我建议您购买Nicholas' book

答案 10 :(得分:0)

如果唐纳德·克努特说:“我们应该忘记效率低下,大约97%的时间说:过早优化是万恶之源”,他们已经死了,他会在他的坟墓里翻身。对于我自己,我开始感觉像this

注意,男孩和女孩。效率无所谓!它只是没有!

让我们忘记像@Pheonix那样彻头彻尾的疯狂 - 它拼写为“凤凰”BTW! - 实际上担心局部变量占用的空间,一旦函数结束就会被回收以便大声喊叫,只关注原始问题。请考虑以下代码:

$(function() {
    var b;
    var d = { b : 1 }
    var n = 100000;

    var now = function() {
       return new Date().getTime();
    };
    var doTime = function(f) {
        var t = now();
        f();
        return now() - t;
    };

    var tm1 = doTime(function() {
       for (var i=0; i<n; i++) {
           b = 1;
       }
    });   

    var tm2 = doTime(function() {
       for (var i=0; i<n; i++) {
           b = d.b;
       }
    });
    $('body').empty().html("<table><tr><tr><td>Time without</td><td>"
      + tm1 + "ms</td><tr>"
      + "<tr><tr><td>Time with</td><td>"+ tm2 + "ms</td><tr>"
      + "<tr><tr><td>Total diff</td><td>"+ (tm2 - tm1) + "ms</td><tr>"
      + "<tr><tr><td>Avg. diff</td><td>"+ ((1000000.0 * (tm2 - tm1)) / n)
      + "ns</td><tr>"
    + "</table");
});

在我可怜的五岁笔记本电脑上,它说像这样的对象取消引用需要90 纳秒。这意味着,你可以做到一千一百万次一秒。你要去做一千一百万次吗?如果没有,不要花一秒钟担心它。

众所周知,优化正确的代码比纠正优化的代码更容易。好好编写代码,然后对其进行基准测试。如果它太慢,找出瓶颈(我保证你不会在本地值中隐藏对象解引用的结果),并消除它们。

我们现在让您回到定期安排的节目中。

答案 11 :(得分:0)

原样。不可以。因为您没有使用var声明变量,所以您在全局范围而不是本地范围内声明这些变量。

如果您反复访问变量,则绝对值得在本地范围内缓存副本。这是因为如果变量在直接范围内不可用,则函数和对象具有一系列范围来搜索。在下面的示例中,最内层函数必须首先搜索其本地范围,然后搜索其父函数的范围,然后最终在最外层函数的范围内查找i。如果此范围中不存在i,那么它也必须搜索全局范围。

(function () {
    var i = 10;
    (function () {
        (function() {
            console.log(i); // i is 10
        })();
    })();
})();

在本地范围内缓存变量的问题在于,有时代码可能变得难以阅读,并且几乎在任何情况下可读性都超过了效率(计算机在处理低效代码方面要比使用读取代码的人更好。写得很糟糕。)


在函数顶部声明变量

如果您真的关心效率/正确性,那么在函数开头声明所有变量。这允许javascript在创建函数的本地范围时非常有效。函数内部的声明效率很低,因为javascript必须首先检查本地作用域中是否有可用空间,并在需要时调整作用域的大小。请注意,即使您没有在函数顶部声明变量,一些编译器也会为您执行此操作,因为它是ECMA规范的一部分 - 这可能会导致一些问题)。 source

例如

var i = 1;
function test() {
  console.log(i); // undefined
  var i = 10;
}
test();

基本上,javascript引擎已将您的代码编译为:

var i = 1;
function test() {
    var i;
    console.log(i); // undefined
    i = 10;
}
test();

修复了您的代码版本(如果您要使用a,b或c进行进一步操作)。

var obj = {
    a: 1,
    b: 2,
    c: 3,
    f: function() {
        var a, b, c;
        a = this.a;
        b = this.b;
        c = this.c;
        alert(a);
        alert(b);
        alert(c);
    }
}