1. 动态类型
JavaScript是一种松散类型的语言。换句话说,不必提前声明保存在变量中的数据是什么类型。Java(跟JavaScript完全不一样)等其他语言都要求提前声明变量类型,比如int、float、boolean 或String:
// 在Java中声明变量
int number = 5;
float value = 12.3467;
boolean active = true;
String text = "Crystal clear";
而JavaScript则会根据赋给变量的数据,自动推断其类型。(注意,’’或”” 表示字符串。我个人喜欢双引号,但有人喜欢单引号。)
// 在JavaScript中声明变量
var number = 5;
var value = 12.3467;
var active = true;
var text = "Crystal clear";
好讨厌哪,那么多var!不过也有方便的地方:不用知道保存什么数据,就可以声明和命名变量。甚至随意改变保存的数据类型,JavaScript也不会怪你:
var value = 100;
value = 99.9999;
value = false;
value = "This can’t possibly work.";
value = "Argh, it does work! No errorzzzz!";
这里要提醒大家的是,如果你不小心在一个数值变量里保存了一个字符串,后来代码就出现了一些奇怪的问题,希望你自己能好好反省一下。如果你不确定某个变量中保存着什么类型的数据,可以使用typeof操作符:
typeof 67; // 返回"number"
var myName = "Scott";
typeof myName; // 返回"string"
myName = true;
typeof myName; // 返回"boolean"
2. 变量提升
在我们印象里,浏览器会从上到下依次执行JavaScript代码。但有时候也不一定!比如,下面这些代码,你觉得变量i什么时候有定义?
var numLoops = 100;
for (var i = 0; i < numLoops; i++) {
console.log(i);
}
在其他很多语言中,i会到for循环开始时才被声明,这符合我们的预期。但由于存在一种叫做变量提升的机制,JavaScript中的变量声明会被提升到函数上下文的顶部。对于前面的例子来说,i实际上在for循环开始之前就有了定义。下面的代码跟上面的代码是等价的:
var numLoops = 100;
var i;
for (i = 0; i < numLoops; i++) {
console.log(i);
}
如果变量名字有冲突,变量提升可能就会带来问题。因为你本以为某个变量到后面才会有定义,不成想它早在代码一开始执行时就有定义了。
3. 函数级作用域
在编程领域,变量作用域这个概念用于区分在哪个环境下可以访问哪些变量。一般来说,在任何地方都能访问任何值是不正确的。因为这样发生冲突的可能性太高,或者不知道在哪儿意外改了某个值,都会让你急得疯掉。
很多语言都有块级作用域,也就是在当前“块”中,变量才有定义。这里的“块”,通常就是花括号。在块级作用域下,前面例子中的变量i将只存在于for循环中。如果想在循环外部读取或修改i的值,都会失败。这样其实挺好,因为你知道循环中的变量不会跟循环外部的其他变量有冲突。
但是,JavaScript中的变量只能限制在函数中,即在函数(而不是任何块)中声明的变量只能在函数内部访问。
假如你使用过其他语言,那么对这一点必须格外注意。关键要记住:要想封闭某个值,就得把它们放到函数里。
4. 全局命名空间
说到变量冲突,请帮我个忙:打开任意网页,调出JavaScript控制台,输入window,然后单击那个灰色三角形,看看里面都有什么。
我知道你就像进入了迷宫一样,而这正是所谓的“全局命名空间”,真是“百闻不如一见”
window在浏览器中是一个顶级对象,包含所有JavaScript中能直接访问到的对象。你看到的所有这些对象和值都包含在全局作用域中。换句话说,如果你每次都在全局作用域中声明新变量,那这个变量就会被加到window对象下面。正像一些JavaScript界敢于仗义直言的人所说:“你这是在污染全局命名空间。”(奇怪的是,很多在论坛里义正词严说这种话的人,在现实当中都很平易近人。)
比如,我们在控制台中输入:var zebras = “amazing”。
然后,再输入window,点开灰色小三角,一直向下滚动,滚动到最底部,就会看到zebras。
(让人不解的是,在JavaScript中不使用标准的var也可以定义新变量。因此,只要输入zebras = “amazing” 也会得到与前面相同的结果。)
给window增加几个值有什么大问题吗?刚开始的时候,啥事儿也没有。但随着你的项目越来越复杂,特别是在需要联合使用非D3的JavaScript代码时(如jQuery、Facebook 的“Like”按钮,或谷歌的分析跟踪代码),说不定就会发生命名冲突。比如,你在自己的项目里使用了变量zebras,而zebraTracker.js恰好也使用了一个同名变量!结果肯定是一团糟。至少,一些不太规范的数据,很有可能导致你的代
码发生异常或错误。
解决这个问题有两个简单的办法(说明一下,不到后面你不用担心这一点)。
只在函数里面声明变量。虽然有时候也不是绝对可行,但函数级作用域可以防止其本地变量跟其他变量发生冲突。只声明一个全局对象,然后把你本来想作为全局变量的值都作为这个对象的属性。比如:
var Vis = {}; // 声明空的全局对象
Vis.zebras = "still pretty amazing";
Vis.monkeys = "too funny LOL";
Vis.fish = "you know, not bad";
这样,所有变量就都被“关在笼子里了”(在这里就是全局对象Vis里),因此不会再污染全局命名空间。当然,不一定非得叫Vis,叫什么随你便——不过叫Menagerie(动物园)输入起来就太麻烦了。无论如何,如果再有冲突发生,那肯定是其他脚本里也恰好有一个全局对象,而且也起了个相同的名字(Vis)。但这种事儿发生的概率就低得多了。
摘自《数据可视化实战:使用D3设计交互式图表》