在之前的介绍中,我们已经知道 Javascript 没有块级作用,只有函数级作用域。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
function test() { // a scopepcZHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 10; i++) { // not a scopepcZHTML5中文学习网 - HTML5先行者学习网
// countpcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
console.log(i); // 10pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网Javascript 中也没有显示的命名空间,这就意味着一切都定义在全局作用域中。每一次引用一个变量时,Javascript 会往上遍历整个全局作用域直到找到该变量。如果遍历完整个全局作用域仍然没有找到该变量,则抛出一个 ReferenceError 错误。pcZHTML5中文学习网 - HTML5先行者学习网
请输入图片描述pcZHTML5中文学习网 - HTML5先行者学习网
隐式全局变量pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// script ApcZHTML5中文学习网 - HTML5先行者学习网
foo = '42';pcZHTML5中文学习网 - HTML5先行者学习网
// script BpcZHTML5中文学习网 - HTML5先行者学习网
var foo = '42'pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网上面的两个例子产生不一样的效果。第一个将在全局作用域中定义变量 foo,而第二个则在当前作用域定义变量 foo。pcZHTML5中文学习网 - HTML5先行者学习网
我们一定要注意,如果不使用关键字 var 将会带来意想不到的影响。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// global scopepcZHTML5中文学习网 - HTML5先行者学习网
var foo = 42;pcZHTML5中文学习网 - HTML5先行者学习网
function test() {pcZHTML5中文学习网 - HTML5先行者学习网
// local scopepcZHTML5中文学习网 - HTML5先行者学习网
foo = 21;pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
test();pcZHTML5中文学习网 - HTML5先行者学习网
foo; // 21pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网由于在函数 test 内没用 var 来定义变量 foo,所以将覆盖函数外部的全局变量 foo。尽管看上去不是什么大问题,但是如果有成千上万行代码时,这将是个难以追踪的 bug。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// global scopepcZHTML5中文学习网 - HTML5先行者学习网
var items = [/* some list */];pcZHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 10; i++) {pcZHTML5中文学习网 - HTML5先行者学习网
subLoop();pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
function subLoop() {pcZHTML5中文学习网 - HTML5先行者学习网
// scope of subLooppcZHTML5中文学习网 - HTML5先行者学习网
for(i = 0; i < 10; i++) { // missing var statementpcZHTML5中文学习网 - HTML5先行者学习网
// do amazing stuff!pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网上例中,外部的循环将会在执行第一次的时候就停止,这是因为 subloop 函数内部的变量 i 将会覆盖外部的全局变量 i。我们只需要在函数内部加上一个 var 就可以避免这个错误,所以我们在定义变量时一定不要忘记加上关键字 var。除非我们确实希望对外部的全局变量造成影响。pcZHTML5中文学习网 - HTML5先行者学习网
局部变量pcZHTML5中文学习网 - HTML5先行者学习网
Javascript 中局部变量只可以通过两个方式产生,一是通过关键字 var 来声明,一是作为函数的形参。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// global scopepcZHTML5中文学习网 - HTML5先行者学习网
var foo = 1;pcZHTML5中文学习网 - HTML5先行者学习网
var bar = 2;pcZHTML5中文学习网 - HTML5先行者学习网
var i = 2;pcZHTML5中文学习网 - HTML5先行者学习网
function test(i) {pcZHTML5中文学习网 - HTML5先行者学习网
// local scope of the function testpcZHTML5中文学习网 - HTML5先行者学习网
i = 5;pcZHTML5中文学习网 - HTML5先行者学习网
var foo = 3;pcZHTML5中文学习网 - HTML5先行者学习网
bar = 4;pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
test(10);pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网此时,函数 test 内部的变量 i 和 foo 是局部变量,而 bar 则会覆盖外部的全局变量 bar。pcZHTML5中文学习网 - HTML5先行者学习网
提升(Hoisting)pcZHTML5中文学习网 - HTML5先行者学习网
Javascript 将会提升变量声明,这就意味着 var 表达式和函数声明都将被提升到作用域的顶部。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
bar();pcZHTML5中文学习网 - HTML5先行者学习网
var bar = function() {};pcZHTML5中文学习网 - HTML5先行者学习网
var someValue = 42;pcZHTML5中文学习网 - HTML5先行者学习网
test();pcZHTML5中文学习网 - HTML5先行者学习网
function test(data) {pcZHTML5中文学习网 - HTML5先行者学习网
if (false) {pcZHTML5中文学习网 - HTML5先行者学习网
goo = 1;pcZHTML5中文学习网 - HTML5先行者学习网
} else {pcZHTML5中文学习网 - HTML5先行者学习网
var goo = 2;pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
for(var i = 0; i < 100; i++) {pcZHTML5中文学习网 - HTML5先行者学习网
var e = data[i];pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网上面的代码在运行之前, var 表达式和函数 test 的声明都将提升至顶部,因此程序将正常运行并不会报错。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// var statements got moved herepcZHTML5中文学习网 - HTML5先行者学习网
var bar, someValue; // default to 'undefined'pcZHTML5中文学习网 - HTML5先行者学习网
// the function declaration got moved up toopcZHTML5中文学习网 - HTML5先行者学习网
function test(data) {pcZHTML5中文学习网 - HTML5先行者学习网
var goo, i, e; // missing block scope moves these herepcZHTML5中文学习网 - HTML5先行者学习网
if (false) {pcZHTML5中文学习网 - HTML5先行者学习网
goo = 1;pcZHTML5中文学习网 - HTML5先行者学习网
} else {pcZHTML5中文学习网 - HTML5先行者学习网
goo = 2;pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
for(i = 0; i < 100; i++) {pcZHTML5中文学习网 - HTML5先行者学习网
e = data[i];pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
bar(); // fails with a TypeError since bar is still 'undefined'pcZHTML5中文学习网 - HTML5先行者学习网
someValue = 42; // assignments are not affected by hoistingpcZHTML5中文学习网 - HTML5先行者学习网
bar = function() {};pcZHTML5中文学习网 - HTML5先行者学习网
test();pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网由于 Javascript 没有块级作用域,这不仅将提升 var 表达式,同时也会使得 if 结构变得不够直观。pcZHTML5中文学习网 - HTML5先行者学习网
在上例中,尽管看上去 if 在对全局变量 goo 进行操作,实际上,由于变量 goo 被提升,所以修改的是局部变量。pcZHTML5中文学习网 - HTML5先行者学习网
如果没有对提升规则有所了解,你可能会认为下面的代码将会抛出 ReferenceError 错误。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// check whether SomeImportantThing has been initializedpcZHTML5中文学习网 - HTML5先行者学习网
if (!SomeImportantThing) {pcZHTML5中文学习网 - HTML5先行者学习网
var SomeImportantThing = {};pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网当然上面的代码是没有错误的,因为在代码在运行前,var 表达式已经被提升到顶部。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
var SomeImportantThing;pcZHTML5中文学习网 - HTML5先行者学习网
// other code might initialize SomeImportantThing here, or notpcZHTML5中文学习网 - HTML5先行者学习网
// make sure it's therepcZHTML5中文学习网 - HTML5先行者学习网
if (!SomeImportantThing) {pcZHTML5中文学习网 - HTML5先行者学习网
SomeImportantThing = {};pcZHTML5中文学习网 - HTML5先行者学习网
}pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网这里要推荐下 @nightire 凡哥的博文 《理解 JavaScript(二)》,里面对提升的讲解非常透彻。pcZHTML5中文学习网 - HTML5先行者学习网
名称解析顺序pcZHTML5中文学习网 - HTML5先行者学习网
当尝试在一个函数作用域内访问一个 foo 变量时,Javascript 将会按照下面的顺序查找:pcZHTML5中文学习网 - HTML5先行者学习网
当前作用域内是否有 var foo 的定义。pcZHTML5中文学习网 - HTML5先行者学习网
函数形参中是否有 foo 变量。pcZHTML5中文学习网 - HTML5先行者学习网
函数自身的名称是否为 foo。pcZHTML5中文学习网 - HTML5先行者学习网
跳到外层定义域,再从第一部开始查找起。pcZHTML5中文学习网 - HTML5先行者学习网
命名空间pcZHTML5中文学习网 - HTML5先行者学习网
一个最常见的问题就是命名冲突,这是因为 Javascript 只有一个全局作用域所带来的。但这个问题可以通过匿名的外部函数解决。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
(function() {pcZHTML5中文学习网 - HTML5先行者学习网
// a self contained "namespace"pcZHTML5中文学习网 - HTML5先行者学习网
window.foo = function() {pcZHTML5中文学习网 - HTML5先行者学习网
// an exposed closurepcZHTML5中文学习网 - HTML5先行者学习网
};pcZHTML5中文学习网 - HTML5先行者学习网
})(); // execute the function immediatelypcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网上例中的匿名函数被认为是表达式,所以它们会被执行。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
( // evaluate the function inside the parenthesespcZHTML5中文学习网 - HTML5先行者学习网
function() {}pcZHTML5中文学习网 - HTML5先行者学习网
) // and return the function objectpcZHTML5中文学习网 - HTML5先行者学习网
() // call the result of the evaluationpcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网当然我们也可以用其他方式来调用函数表达式,不同的结构,但是同样的效果。pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网
// A few other styles for directly invoking the pcZHTML5中文学习网 - HTML5先行者学习网
!function(){}()pcZHTML5中文学习网 - HTML5先行者学习网
+function(){}()pcZHTML5中文学习网 - HTML5先行者学习网
(function(){}());pcZHTML5中文学习网 - HTML5先行者学习网
// and so on...pcZHTML5中文学习网 - HTML5先行者学习网
pcZHTML5中文学习网 - HTML5先行者学习网总结pcZHTML5中文学习网 - HTML5先行者学习网
建议大家使用匿名的外部函数来将代码封装到空间内,这样不仅可以解决命名空间的冲突,同时也有利于程序的模块化。pcZHTML5中文学习网 - HTML5先行者学习网
此外,使用全局变量不是一个好习惯,这将带来高成本的维护代价而且容易产生错误。pcZHTML5中文学习网 - HTML5先行者学习网
命名空间同类型、函数、变量、模板等都属于实体(entity)。pcZHTML5中文学习网 - HTML5先行者学习网
实体的主要的共性是,可以具有名称。(此外,标签也可以具有名称,但它不是实体。)pcZHTML5中文学习网 - HTML5先行者学习网
而命名空间作用域是作用域中的一类统称,和块作用域、类作用域、函数原型作用域、函数作用域(仅对标签有效)并列。命名空间内声明的名称在命名空间作用域中。全局名称被认为在隐含的全局命名空间作用域中。pcZHTML5中文学习网 - HTML5先行者学习网
命名空间作用确实就是作用域,但是,他又不同于简单的作用域,你可以分多次在多处声明同一个命名空间,但是里面的内容不能重定义,他们最终都会合成一个命名空间,就像std,到处宏定义pcZHTML5中文学习网 - HTML5先行者学习网