[翻译]看图学Javascript Part I

要想成为一个出色的Javascript开发者,其中一个秘密就是真正的去理解这门语言。这篇文章将会用一些简单易懂的图表来解释Javasript的一些基本要素。

无处不在的引用(References Everywhere)

Javascript里的变量简单来说就是一个引用了内存里某个值的标签。这个值可以是字符串,数字或者布尔值这些基本元素,也可以是对象或者函数。

局部变量(Local Variables)

在下面的例子里,我们会在顶级作用域里创建四个局部变量然后把它们指向一些基本类型的值:
// Let's create some local variables in the top scope
var name = "Tim Caswell";
var age = 28;
var isProgrammer = true;
var likesJavaScript = true;
// Test to see if the two variables reference the same value
isProgrammer === likesJavaScript;
输出
=> true


注意到两个布尔型变量指向了内存里的同一个值。这是因为基本元素是不可变的(immutable),解析器可以进行优化,对这些值的引用分享同一个实例。 在这段代码中,我们用 === 来检验两个引用是不是指向同一个值,结果返回是true。 在外层的代码处于最外层的闭包作用域(The outer box represents the outermost closure scope.),这些变量是顶级的局部变量,不要跟global/window对象的属性相混淆。

对象与原型链(Objects and Prototype Chains)

对象就是一个集合,包含了更多的对新对象与原型的引用。唯一特别的地方是当你试图访问一个在父对象里而不在局部对象里的属性时所用到的原型链。
// Create a parent object
var tim = {
  name: "Tim Caswell",
  age: 28,
  isProgrammer: true,
  likesJavaScript: true
}
// Create a child object
var jack = Object.create(tim);
// Override some properties locally
jack.name = "Jack Caswell";
jack.age = 4;
// Look up stuff through the prototype chain
jack.likesJavaScript;
输出
=> true
对象

这里我们有一个具有四个属性的对象,被变量tim所引用。同时我们创建了一个由第一个对象继承而来的新对象,被变量jack所引用。然后我们重写这个局部对象中的两个属性。 当我们要访问jack.likesJavaScript的时候,我们先会查找jack所引用的对象,然后在其中查找likesJavaScript这个属性。对象里没有这个属性,于是我们在父对象里进行查找并找到了它,并知道它引用了值true。

全局对象(The Global Object)

曾经有疑问为什么像jslint这些工具老是告诉你不要忘记用var定义变量,好吧,让我告诉你如果你忘记了的话。
var name = "Tim Caswell";
var age = 28;
var isProgrammer = true;
// Oops we forgot a var
likesJavaScript = true;


注意到likesJavaScript现在是全局对象的一个属性而不是外层闭包里的一个变量。当你把多段代码混合在一起就可能会出现问题,而事情你确实会经常这么做。 总是记得加上var使你的变量的作用域处于当前的闭包和子闭包里。你当然会很乐意遵循这个这么简单的规则。 如果你必须把东西放到全局对象上,你可以在浏览器上使用window.woo或者在node.js使用global.goo。

函数与闭包(Functions and Closures)

Javascript不仅仅是一系列的数据结构链。它包含了可执行和调用的代码,这种代码被称作函数。这些函数会创建链式作用域和闭包。

观察闭包(Visualizing Closures)

函数可以看作是特别的对象,对象包含了可执行的代码和属性。每个函数在定义时都会赋予一个特别的[scope]属性表明它正处于哪个环境。如果一个函数是在另外一个函数里被返回,那么对旧环境的引用就会被新的函数关闭在“闭包”里。 在这个例子里我们会创建一个简单的工厂方法来产生一个闭包,并返回一个函数。
function makeClosure(name) {
  return function () {
    return name;
  };
}
var description1 = makeClosure("Cloe the Closure");
var description2 = makeClosure("Albert the Awesome");
console.log(description1());
console.log(description2());
输出
Cloe the Closure
Albert the Awesome


当我们调用description1()的时候,VM就会查找他引用的函数然后执行它。而这个函数会在闭包的作用域里去找一个叫做name的局部变量。这是个不错的工厂方法,每个产生的函数都有自己存放局部变量的空间。

想深入了解闭包和它的用法,可以看why use closure这篇文章。

共享函数与this(Shared Functions and this)

有时为了性能上的考虑,或者你纯粹喜欢这样,Javascript提供了this这个关键字让你在不同的作用域里重用函数对象,这取决于你是怎样调用它的。

这里我们创建了一些对象,这些对象共享了同一个函数。这个函数将会在内部引用this,向你展示它在不同的调用里会有哪些不同的表现。
var Lane = {
  name: "Lane the Lambda",
  description: function () {
    return this.name;
  }
};
var description = Lane.description;
var Fred = {
  description: Lane.description,
  name: "Fred the Functor"
};
// Call the function from four different scopes
console.log(Lane.description());
console.log(Fred.description());
console.log(description());
console.log(description.call({
  name: "Zed the Zetabyte"
}));
输出
Lane the Lambda
Fred the Functor
undefined
Zed the Zetabyte


在这种图表里,我们看到就算Fred.description被设为Lane.description,它事实上只是引用那个函数。因此三个引用都是引用相同的匿名函数。因此我尽量不会用构造函数原型这个“方法”去调用函数,因为这样意味着会把这个函数绑定到构造函数以及他的“类”上(This is why I try to not call functions on constructor prototypes "methods", because that implies some sort of binding of the function to the constructor and it's "class")。(想知道更多关于this的动态性质请看what is this

总结

使用图表去表现这些数据结构是件很有趣的事情。我希望这个能帮助像我们一样通过视觉学习(visual learner)的人更好的掌握Javascript的语法。我具有前端设计/开发以及后台开发的经验,我希望我独特的视觉能给那些从事设计以及正在深入学习Javascript这个美妙的语言的人带来帮助。

(所有图表都是graphviz的点文件(dot file),可以在这里查看)

原文:http://howtonode.org/object-graphs

同时发表在:http://www.icyfire.me/2011/10/object-graphs-1/

Translated by icyfire @ 2011-09-02 updated @ 2011-10-28
posted @ 2011-11-01 10:02  icyfire  阅读(241)  评论(0编辑  收藏  举报