执行上下文
范围
一段<script>
或者一个函数
全局(一段<script>
)执行顺序
变量定义
函数声明
函数执行顺序
变量定义
函数声明
设定this
传入参数
consloe.log(a); // undefined
var a = 100;
fn("Tim Chen"); // Tim Chen 20
function fn(name){
age = 20;
console.log(name,age);
var age;
}
第一个例子里执行顺序
var a = undefined;
console.log(a);
a = 20 ;
a还没有赋值,便已经打印了。所以最终结果是undefined。
第二个例子里执行顺序
function fn(name){
var age = undefined;
age = 20;
console.log(name,age);
};
fn("Tim Chen");
函数内部先声明变量,传入参数,然后对变量进行赋值,打印。
函数已经声明了,所以调用是可以执行的。
this
this要在执行时才能确定值,定义时无法确定。
var a = {
name: "A",
fn: function(){
console.log(this.name);
}
}
a.fn(); // this === a
a.fn.call({name: "B"}); // this === {name : "B"}
var fn1 = a.fn;
fn1(); // this === window
构造函数使用
function Foo(name){
this.name = name;
}
var f = new Foo("Tim Chen");
对象属性使用
var a = {
name: "A",
fn: function(){
console.log(this.name);
}
}
a.fn();
普通函数使用
此时打印出来的结果是window
function fn(){
consoloe.log(this);
}
fn();
call/apply/bind使用
call()
此时打印出来的this === {x: 100}
function fn(name){
alert(name);
consoloe.log(this);
}
fn.call({x:100},"Tim Chen");
bind()
bind()只能是函数表达式用
var fn = function(name,age){
alert(name);
console.log(this);
}.bind({y:200});
fn("Tim Chen","22");
作用域
es5及以前,无块级作用域。
只有函数和全局作用域。
无块级作用域怎么理解?
所谓的块级作用域,就是说{}括号里自成一体,括号外访问不了括号内的变量、参数。
if(true){
var str = "成功读取";
}
console.log(str); // 成功读取
上面的代码,从{}外面依然可以访问到里面的str变量。
函数和全局作用域
var a =100;
function fn(){
var a = 200;
console.log("fn",a); //打印出来是函数内的变量a
}
console.log("global",a); //打印出来是全局变量a
fn();
作用域链
如果函数调用了,当前作用域没有定义的变量(自由变量),它会去父级作用域找。父级也没有,就继续网上找。
这就叫作用域链。
注意,这个父级作用域指的是函数在定义时的父级,而不是执行时的父级。
var a = 100;
function fn (){
var b = 200;
console.log(a); //
}
fn();
闭包
function F1(){
var a = 100;
//返回值是一个函数
return function(){
console.log(a);
}
}
//fn1 得到一个函数
var fn1 = F1();
var a = 200;
fn1();
fn1执行的时候,其实就是执行console.log(a)。
上面的作用域链提到过,作用域调用自由变量的时候,会去它定义时的父级作用域寻找。在这个例子里,父级就是 F1函数。
所以最终执行的结果是100。
使用场景
函数作为返回值(上面的例子)
函数作为参数传递
请看代码
function F1(){
var a = 100;
return function(){
console.log(a);
}
}
var fn1 = F1();
function F2(fn){
var a = 200;
fn();
}
F2(fn1);
还是那句话,自由变量会去它定义时的父级作用域寻找。
而不是调用时的作用域!!!
所以最终执行的结果是100。
闭包在实际开发中的应用
封装变量,限制权限。
function isFirstLoad(){
var _list = [];
return function (id){
if(_list.indexOf(id)>=0){
return false;
}else{
_list.push(id);
return true;
}
}
}
//调用
var firstLoad = isFirstLoad();
firstLoad(10); //true
firstLoad(10); //false
firstLoad(20); //true
例题讲解
1. 变量提升的理解(执行上下文)
变量定义
函数声明
2. 创建10个标签,点击弹出相应序号
错误写法:
var i , a;
for(i = 0; i < 10; i++){
a.document.createElement("a");
a.innerHTML = i +"<br />";
a.addEventListener("click",function(e){
e.preventDefault();
alert(i); //这里的i是自由变量
})
document.body.appendChild(a);
}
在上面的代码中,因为不知道什么时候才发生click事件,早就循环完毕,此时i已经是10了。
正确写法
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);
}
在for循环里面,包裹着一个子调用函数,把i传进去作为参数。
每次循环,i的值都不一样。最后达到预期效果。
3. 如何理解作用域
自由变量
作用域链,即自由变量的查找
闭包的两个场景