Javascript:函数中的变量赋值

Javascript:函数中的变量赋值,javascript,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 =

考虑以下两个方面:

块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
,而是为当前函数的作用域重新分配变量。它是有效的,但它是正确的吗


编辑:为了澄清,变量将在整个函数中广泛使用。一般的共识似乎是,对于这一点,通过局部作用域进行重新分配是可行的。

第一种方法相对来说更有效,因为在第二种方法中,您制作了变量的一个副本,因此所有变量都有一个额外的语句,并且会占用更多的内存空间(代码和变量).

视情况而定。如果只使用一次该值,那么增加存储和检索该值的开销是没有意义的。另一方面,如果在函数范围内多次引用该值,则只获取一次是有意义的。

f: function() {
    a = this.a;
    b = this.b;
    c = this.c;
    alert(a);
    alert(b);
    alert(c);
}
不仅全局赋值和查找效率较低,而且您正在污染全局范围,因为
a=this.a
正在分配给全局
a

编辑:

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

两者之间存在操作顺序差异

var a = this.a, b = this.b;
alert(a); alert(b);
哪一个(得到a,得到b,一个toString,一个toString)和

哪一个(得到a,a toString,得到b,b toString)和

可能有一个很好的理由选择一种操作顺序而不是另一种,但从效率角度看,第二种可能更好


由于操作顺序的不同,当成员只有一次使用时,您不应该依赖于保留语义的JavaScript小型化器来优化第一到第二次。如果在函数中只访问一次属性,则第一次访问速度更快。如果您多次访问它,那么使用修改后的版本作为第二个代码块会更快

将第二个版本更改为将
a
b
c
声明为
f()
的本地变量将避免多次扫描范围链和遍历
——同样,如果您需要多次访问这些属性,也可以这样做。

视频摘要:

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


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

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

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

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

function () {
  //use the values as they are
  alert( this.a );
  alert( this.b );
  alert( this.c );
}
但是,如果您正在执行计算,并且需要临时缓存结果以供重用,则应使用局部变量。确保它们不会污染全局范围(
窗口
对象);使用
var
使变量仅在本地持久化

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

  alert( this.a * foo );
  alert( this.b / foo );
  alert( this.c + foo );
}
编辑以添加:

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

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

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

这不会像预期的那样发出警报
1
2
3

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 );
  });
}
此版本将:

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


2) 是的,复制值可以节省键入时间,而且执行速度更快,因为访问对象属性并不是一个真正便宜的操作。JavaScript不实现数组;它们总是散列。

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

var obj = {
    a: 1,
    b: 2,
    c: 3,
    f: function () {
        for (var property in this) {
            if (property != "f")
                alert(property + "=" + this[property]);
        }
    }
};
obj.f();
我想您的意思是将var放在匿名函数中每个变量a、b和c的前面

根据尼古拉斯·扎卡斯(Nicholas Zakas)的书,他提到了标识符解析(查找您正在使用的变量名)所涉及的成本。他指出,局部变量的成本最低,而全局变量的成本最低
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 );
  });
}
var obj = {
    a: 1,
    b: 2,
    c: 3,
    f: function () {
        for (var property in this) {
            if (property != "f")
                alert(property + "=" + this[property]);
        }
    }
};
obj.f();
$(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");
});
(function () {
    var i = 10;
    (function () {
        (function() {
            console.log(i); // i is 10
        })();
    })();
})();
var i = 1;
function test() {
  console.log(i); // undefined
  var i = 10;
}
test();
var i = 1;
function test() {
    var i;
    console.log(i); // undefined
    i = 10;
}
test();
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);
    }
}