1. 函数做用域和声明提早javascript
① javascript没有块级做用域,取而代之的使用了函数做用域,即变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。java
function test(o) {
var i = 0; // i 在函数体内均是有定义的
if (typeof o == "object") {
var j = 0; // j在函数体内是有定义的,不单单是在这个代码段内
for(var k = 0; k < 10; k++) { // k在函数体内是有定义的,不单单是在循环内
console.log(k);
}
console.log(k);
}
console.log(j);
}
② 声明提早:javascript函数里声明的全部变量(但不涉及赋值)都被“提早”至函数体的顶部,看一下以下代码:浏览器
var scope = "global";
function f() {
console.log(scope); // 输出"undefined",而不是"global"
var scope = "local"; // 变量在这里赋初始值,但变量自己在函数体内任何地方均是有定义的
console.log(scope); // 输出"local"
}
//以上代码等价于
function f() {
var scope:
console.log(scope);
scope = "local";
console.log(scope);
}
2. 做为属性的变量函数
当声明一个javascript全局变量时,其实是定义了全局对象的一个属性。当使用var声明一个变量时,建立的这个属性是不可配置的,也就是说这个变量没法经过delete运算符删除。this
var truevar = 1; // 声明一个不可删除的全局变量
fakevar = 2; // 建立全局对象的一个可删除的属性
this.fakevar2 = 3; // 同上
delete truevar; // => false: 变量并无被删除
delete fakevar; // => true: 变量被删除
delete this.fakevar2; // => true: 变量被删除
javascript全局变量是全局对象的属性,这是在ECMAScript规范中强制规定的。对于局部变量则没有如此规定,但咱们能够想象获得,局部变量当作跟函数调用相关的某个对象的属性。ECMAScript 3规范称该对象为“调用对象” (call object),ECMAScript 5规范称为“声明上下文对象”。javascript能够容许使用this关键字来引用全局对象,却没有方法能够引用局部变量中存放的对象。这种存放局部变量的对象的特有性质,是一种对咱们不可见的内部实现。即在局部变量的函数做用域以外是没法直接访问到该变量的,除非经过在函数做用域的对外方法获取到(相似类的私有属性跟公有方法)。spa
3. 做用域链对象
每一段javascript代码(全局代码或函数)都有一个与之关联的做用域链。这个做用域链是一个对象列表或者链表,这组对象定义了这段代码“做用域中”的变量。当javascript须要查找变量x的值的时候(这个过程称做“变量解析”),它会从链中的第一个对象开始查找,若是这个对象有一个名为x的属性,则会直接使用这个属性的值,若是第一个对象依然没有名为x的属性,则会继续查找下一个对象,以此类推。若是做用域链上没有任何一个对象含有属性x,那么就认为这段代码的做用域链上不存在x,并最终抛出一个引用错误异常。blog
在javascript的最顶层代码中,做用域链由一个全局对象组成,在这个做用域下声明的变量x都是全局对象(window)的一个属性,而且能够经过window.x或者this.x访问该变量。在不包含嵌套的函数体内,做用域链上有两个对象,第一个是定义函数参数和局部变量的对象(这个对象就是咱们不可见的调用对象),第二个是全局对象。理解对象链的建立规则是很是重要的。当定义一个函数时,它实际上保存一个做用域链(此时做用域链中并无值)。当调用这个函数时,它建立一个新的对象(调用对象)来存储它的局部变量,并将这个对象添加至保存的那个做用域链上,同时建立一个新的更长的表示函数调用做用域的“链”(其中包括全局对象)。ip
4. 对于全局对象与调用对象的理解(本人看法,不必定正确)作用域
其实咱们能够把全局对象想象成一个最顶级的调用对象,本来调用对象的内部实现是不可见的,即咱们不能够直接访问到调用对象。但全局对象是个例外,它惟一与调用对象的区别无外乎ECMAScript对其强制规定可见(即一个对外可见的调用对象)。全局代码中的执行环境至关于一个最顶级的函数体内部,页面加载时,浏览器默认执行这个顶级函数而已。