https://developers.google.com/speed/articles/optimizing-javascript

作者: Gregory Baker, Software Engineer on GMail & Erik Arvidsson, Software Engineer on Google Chrome

不要在constructor中定义类方法,应该使用原型。

下面的代码会在每次构造baz.Bar实例时,创建一个新的function实例;随着foo创建的同时还创建了一个闭包:

baz.Bar = function() {
  // constructor body
  this.foo = function() {
    // method body
  };
}

推荐下面的方式

baz.Bar = function() {
  // constructor body
};

baz.Bar.prototype.foo = function() {
  // method body
};

无论创建多少baz.Bar实例,只有一个foo function被创建,同时避免了闭包。

在prototype上申明、初始化数值型的实例变量的初始值

数值型:比如:number, Boolean, null, undefined, or string
引用型:略

避免了构造时,每次都调用无用的初始化代码。(与构造函数参数相关的实例变量除外)

举个例子:

foo.Bar = function() {
  this.prop1_ = 4;
  this.prop2_ = true;
  this.prop3_ = [];
  this.prop4_ = 'blah';
};

可以改为

foo.Bar = function() {
  this.prop3_ = [];
};

foo.Bar.prototype.prop1_ = 4;

foo.Bar.prototype.prop2_ = true;

foo.Bar.prototype.prop4_ = 'blah';

回避闭包带来的缺陷

闭包是JavaScript语言强大且有用的特性;然而同时也存在着许多缺陷:

  • 内存泄露是闭包最常见的缺点。
  • 创建闭包比创建 inner function 更慢,而inner function又慢于static function

举例:

function setupAlertTimeout() {
  var msg = 'Message to alert';
  window.setTimeout(function() { alert(msg); }, 100);
}

慢于下面代码:

function setupAlertTimeout() {
  window.setTimeout(function() {
    var msg = 'Message to alert';
    alert(msg);
  }, 100);
}

而上述代码又慢于下面代码:

function alertMsg() {
  var msg = 'Message to alert';
  alert(msg);
}

function setupAlertTimeout() {
  window.setTimeout(alertMsg, 100);
}
  • 闭包增加了语义链的层级,浏览器在resolves属性时,每一层的语义链都会被检查 :
var a = 'a';

function createFunctionWithClosure() {
  var b = 'b';
  return function () {
    var c = 'c';
    a;
    b;
    c;
  };
}

var f = createFunctionWithClosure();
f();

当调用f时,referencing 效率:

a <  b < c

参考 IE+JScript Performance Recommendations Part 3: JavaScript Code inefficiencies  了解 IE 下的闭包.

避免使用 with

避免使用 with,它不仅影响效率,还修改了scope chain,导致查找变量的开销变得更加昂贵。

避免浏览器内存泄露

内存泄露在 web 应用中太常见了,而且会引起严重的性能问题。
最常见的内存泄露是JavaScript引擎和浏览器C++ DOM对象(比如IE COM 基础组件,Firefox XPCOM 基础组件)循环引用

Use an event system for attaching event handlers

一种最常见的循环引用模式:

[ DOM element --> event handler --> closure scope --> DOM element]

参考 MSDN blog post.

要想避免这种情况,可以使用 Google doctypeDojo, or JQuery等成熟的事件处绑定库。

另外在 IE 中使用inline event handlers 会造成另外一种泄露,不同于普通的循环引用,泄露的是一种内部零时匿名script对象。

详情可参考 Understanding and Solving Internet Explorer Leak Patterns “DOM Insertion Order Leak Model” 章节。
具体例子 JavaScript Kit tutorial.

避免 expando properties

Expando properties are properties added to DOM nodes with JavaScript, where those properties are not part of the object’s DOM specification:

通过dot访问一个不存在的DOM属性时,javascript会自动创建一个Expando properties。
这种行为通常导致循环引用。

[ DOM element --> via expando--> intermediary object --> DOM element ]
// First set up the script scope to element reference
myGlobalObject =
document.getElementById("LeakedDiv");

// Next set up the element to script scope reference
document.getElementById("LeakedDiv").expandoProperty =
myGlobalObject;

最佳实践是不用使用。如果非要用这种特性不可,仅仅访问 primitive类型

var div = document.getElementsByTagName('div')[0];
div.someProperty = true;

或者将expando property置为null。

document.getElementById("LeakedDiv").expandoProperty =
null;

参考  Understanding and Solving Internet Explorer Leak Patterns"Circular References" 章节。