趁热打铁,今天简单谈一谈作用域和闭包,老规矩请看面试题:
1.说一下对变量提升的理解
2.说明this几种不同的使用场景
3.创建10个<a>标签,点击时弹出对应的序号
4.如何理解作用域
5.实际开发中闭包的使用
知识点:执行上下文 this 作用域 作用域链 闭包
1.执行上下文
范围:一段<script>或一个函数
一段<script>:变量定义 函数声明 (会先被读取)
函数:变量定义 函数声明 this arguments(会先被读取),注意函数声明(function abc(){})和函数表达式(var abc = function (){})的区别
例1: console.log(a) var a = 100 // undefined
例2: a = 100 console.log(a) var a // 100
例3: fn('zhangsan') // zhangsan 20
function fn(name) {
age = 20
console.log(name,age)
var age
}
2. this
this要在执行时才能确认值,定义时无法确认,具体如下:
this指向:a 构造函数中执行 this指向空对象,给this赋值
b 作为对象属性赋值 指向这个对象
c 普通函数中执行 指向window
d call apply bind中使用 fn.call({x:100},'abc');fn函数中的this就是传入的{x:100}
3 作用域
3.1 JS中没有块级作用域 if (true) { var a = 100}; console.log(a) // 100 仍然能打印出a的值
3.2 函数作用域、全局作用域(函数作用域是封闭的,不会受到外边变量影响)
var a = 200;
function fn() {
var a = 100;
console.log(a)
}
console.log(a) // 200
fn(); // 100
3.3 作用域链
var a = 100;
function fn() {
// 当前作用域没有定义的变量,即自由变量,去父级作用域查找
console.log(a)
}
fn(); // 100
父级作用域:函数被定义的作用域,不是被执行时的作用域
上图中以console.log(a)为例讲一下。在F2函数体中没有a这个变量,那么去它的父级作用域查找,F2是在F1函数体中被定义的(注意哦,是被定义的作用域不是执行的作用域),其中只定义了b变量,所以继续向上找,F1是在全局被定义的,全局定义了变量a,所以a打印出来是100;
3.4 闭包的使用场景
a 函数作为返回值
function F1() {
var a = 100;
return function () {
console.log(a)
}
}
var f1 = F1();
var a = 200;
f1(); // 100 自由变量去父级作用域查找 全局的a=200无用
b 函数作为参数传递
function F1() {
var a = 100;
return function () {
console.log(a)
}
}
var f1 = F1();
function F2(fn) {
var a = 200;
fn();
}
F2(f1); // 打印出来的 还是100 原因同上 自由变量去父级作用域查找 a=200无用
解答:
1.说一下对变量提升的理解
这问的就是执行上下文,变量声明的东西,注意函数声明和函数表达式的区别
2.说明this几种不同的使用场景
a 构造函数中执行 this指向空对象,给this赋值
b 作为对象属性赋值 指向这个对象
c 普通函数中执行 指向window
d call apply bind中使用 fn.call({x:100},'abc');fn函数中的this就是传入的{x:100}
3.创建10个<a>标签,点击时弹出对应的序号
这道题考的是闭包,像下边这样的写法是错的,alert出来的都是9,因为当你点击的时候,alert(i)这个i变量是自由变量,会去父级作用域查找,父级作用域的i已经是9了。
这样的写法才是正确的,alert的i是自由变量,会去父级作用域查找
4.如何理解作用域
a 自由变量
b 作用域链,即自由变量的查找
c 闭包的两个场景
5.实际开发中闭包的使用
主要用于 封装变量 收敛权限;用在循环中,如问题3;
下期预告:异步 单线程 敬请期待