作用域
作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。其实每一个 javascript 都可以表示为一个对象,也可以说成是 Function 对象的一个实例。
-
全局作用域
在函数外层定义的变量具有全局作用域。
var a = 123; function Fn() { console.log(a); }
末定义直接赋值的变量自动声明为拥有全局作用域
function Fn() { var a = 123; b = 456; // 隐式全局变量 } Fn(); console.log(b); // 456 console.log(a); // a is not defined
所有 window 对象的属性拥有全局作用域(详见 《window 对象》一章)
function Fn() { window.c = 1; } Fn(); console.log(c); //1
用变量声明符定义的变量是不能被删除的,没有变量声明符的变量是可以被删除
var a = 123; c = 456; delete a; delete c; console.log(a); // 123 console.log(c); // c is not defined
-
局部作用域
局部作用域一般只在固定的代码片段内可访问到
a = 100; fn(a); function fn(b) { // 函数作用域 b = 200; } console.log(a); //100 这里的 a 是全局变量 console.log(b); //b is not defined
for (var i = 0; i < 10; i++) { //ES 5 中是没有块级作用域的,所以 i 可以被外界访问 } console.log(i); // 10 / (function (){// 自执行函数 for (var i = 0; i < 10; i++) { // 可以用函数作用域来限制作用域,模拟一个块级作用域 } }(); console.log(i); // undefined // for (let i = 0; i < 10; i++) { // es6 中增加的 变量声明方式 let 解决了块级作用域的问题 } console.log(i); // 报错 undefined
作用域链
如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。知道原型链主要是用于排查我们需要的变量究竟在哪一个作用域中存在,经过层层筛选,会出现多大的性能问题。一个原则是 尽量将 全局变量局部化。
当一个函数定义后其执行环境与作用域链就已经确定了,不会因为执行位置改变而改变
var x = 101;
function Tn() {
console.log(x);
}
function show_1() {
var x = 202;
(function() {
Tn();
// 这里的输出结果是 101
})();
}
show_1();
function show_2() {
var x = 303;
(function() {
console.log(x);
// 这里的输出结果是 303
})();
}
show_2();
var a = 10;
function fn_1() {
var b = 20;
function fn_2() {
console.log(a + b);
}
return fn_2;
}
var b = 30;
var fn = fn_1();
fn(); //30
在局部作用域中使用全局作用域变量
var name = "全局";
function test() {
var name = "局部";
console.log(window.name);
}
test(); // 全局
var name = "全局";
function test() {
var name = "局部";
with (window) {
// with 子句 这种模式在 严格模式下是不被允许的
console.log(name);
}
}
test();
this
当前执行代码的环境对象
在全局执行环境中 this 指向全局对象
console.log(this === window); //true
如果没有指定 this 的绑定,默认情况下,全局对象绑定 this 上
var a = 10;
(function() {
var a = 20;
console.log(a); //20
console.log(this.a); //10 相当于 window.a
})();
但是在严格模式下(strict mode),全局对象将无法使用默认绑定
function fn() {
"use strict";
var a = 20;
console.log(a); //20
console.log(this); //undefined
}
fn();
但是如果将函数作为对象的属性或方法调用
var a = 10;
function fn() {
"use strict";
var a = 20;
console.log(a); //20
console.log(this.a); //10
}
this.fn();
函数的调用是在某个对象上触发的,那么此时的 this 就指代该对象。
var a = 101;
var obj = {
a: 202,
fn: fn
};
function fn() {
console.log(this.a);
}
obj.fn(); //202
this.fn(); //101
如果将对象中某一属性或函数赋值给一个变量,那么相当于将这个属性或函数单独拿出来,并不涉及到对象的其他属性了。
var a = 2;
var obj = {
a: 3,
foo: foo
};
function foo() {
console.log(this.a);
}
var bar = obj.foo;
bar(); //2
obj.foo(); //3
因为 this 的指向会根据上下文不同而不同,我们也可以自定义 this 的指向。通过这两个方法 call(…)或 apply(…)来实现
var a = 101;
var obj_1 = {
a: 202
};
var obj_2 = {
a: 303
};
function foo() {
console.log(this.a);
}
foo.call(obj_1); //202
foo.call(obj_2); //303
var a = 101;
var obj_1 = {
a: 202
};
var obj_2 = {
a: 303
};
function foo() {
console.log(this.a);
}
var bar = function() {
foo.call(obj_1);
console.log(this.a);
};
bar.call(obj_2);
// 202
// 303