Javascript 之 作用域和闭包
执行上下文
1、在一段 <script>...</script> 标签里面会有一个全局执行上下文
变量声明、函数声明提前
2、在一个函数 function fn(arguments){...} 内部会有一个函数执行上下文
变量声明、函数声明、 this 、 arguments 提前
函数声明: function fn () {...}
函数表达式: var fn = function () {...}
this
this 要在执行时才能确认值,定义时无法确认 js是单线程解释性语言(另一种是编译性语言),定义时不会报错,执行时才会报错
var o = {
name: 'A',
fn: function () {
console.log(this.name);
} }
o.fn(); // this === o
o.fn.call({name: 'B'}); // this === {name: 'B'}
var fn1 = o.fn;
f1(); // this === window
作为构造函数执行 this指向新创建的空对象
作为对象属性执行 this指向对象
作为普通函数执行 this指向window
call() 、 apply() 、 bind()
function fn (name, age) {
alert(name);
console.log(this);
}
fn.call({x: 100}, 'joffe'); // this === {x: 100}
fn.apply({x: 100}, ['joffe', 21]); // this === {x: 100}
var fn = function (name, age) {
alert(name);
console.log(this);
}.bind({x: 200});
fn('joffe', 21); // this === {x: 200}
作用域
js没有块级作用域,只有全局作用域、函数作用域(函数内部变量,外部无法访问)
针对函数作用域
自由变量是指在当前作用域中没有定义的变量
在函数执行时,如果一个变量在当前函数作用域没有找到,就会去函数的父级作用域寻找(函数哪里定义,哪里就是函数的父级作用域,跟在哪里执行函数没有关系)
var a = 100;
function f1 () {
var b = 200;
function f2 () {
var c = 300;
console.log(a); // a 是自由变量
console.log(b); // b 是自由变量
console.log(c);
}
f2();
}
f1();
一个变量通过父级作用域一层层往上找,直到找到变量定义的作用域,这叫作用域链
实例: 创建十个 a 标签,点击弹出序列号
var i;
for (i=0; i<10; i++) {
(function (i) {
var a = document.createElement('a');
a.innerHTML = i + '<br />';
a.addEventListener('click', function (e) {
e.preventDefault();
alert(i); // i 是一个自由变量,需要到函数父级作用域寻找
})
document.body.appendChild(a);
})(i);
}
闭包
1、函数作为返回值
function fn () {
var a = 100;
// 返回一个函数(函数作为返回值)
return function () {
console.log(a); // a 是一个自由变量,父级作用域寻找
}
}
var f1 = fn();
var a = 200;
f1(); // 100
2、函数作为参数传递
function f1 () {
var a = 100;
return function (){
console.log(a);
}
}
var f2 = f1();
function f3 (fn){
var a = 200;
fn();
}
f3(f2); // 100
实际开发中闭包的应用 主要用于封装变量,收敛权限
//防止在 isFirstLoad 函数外修改 _list 的值
function isFirstLoad () {
var _list = [];
return function (num) {
if(_list.indexOf(num) >= 0) {
return false;
} else {
_list.push(num);
return true;
}
}
}
var firstLoad = isFirstLoad();
firstLoad(10); // true
firstLoad(10); // false
firstLoad(20); // true
firstLoad(20); // false