一、执行上下文
- 范围:一段<script>或者一个函数
- 全局:变量定义、函数声明
- 函数:变量定义、函数声明、this、arguments
console.log(a) //undefined
var a = 100
fn('zhangsan')
function fn(name){
age = 20
console.log(name, age) //'zhangsan' 20
var age
}
//实际过程中执行的顺序是:
var a
function fn(name){
//函数
var age
age = 20
console.log(name, age) //'zhangsan' 20
}
console.log(a) //undefined
a = 100
fn('zhangsan')
1、函数声明与函数表达式的区别:
全局:
//函数表达式,在代码执行之前,会把函数的声明放到最前面
fn()
function fn() {
//函数声明
}
//上述代码的实际执行的代码
function fn() {
//函数声明
}
fn()
//函数表达式,在代码执行之前不会将它提前,因为代码还没有执行到fn1赋值为函数的代码,此时fn1就为undefined,所以会报错。函数表达式的执行和普通变量的执行是一样的。
fn1()
var fn1 = function() {
//函数表达式
}
//上述代码的实际执行的代码
var fn1
fn1()
fn1 = function(){
}
注意:代码在执行时,this和arguments已经确定。
二、this
this要在代码执行时才能确认值,定义时无法确定。this的几种情况:
- 作为构造函数执行
function Foo(name) {
this.name = name //this === f
}
var f = new Foo('zhangsan')
- 作为对象属性执行
var obj = {
name: 'A',
peintName: function(){
console.log(this.name) //this === obj
}
}
obj.printName()
- 作为普通函数执行
function fn() {
console.log(this) //this === window
}
fn()
- call apply bind
function fn1(name, age) {
console.log(name)
console.log(this)
}
fn1.call({x:100}, 'zhangsan', 20)
fn1.apply({x:100},['zhangsan', 20])
var fn2 = function(name, age) {
console.log(name)
console.log(this)
}.bind({y:200})
fn2('zhangsan', 20)
//bind必须是函数表达式
三、作用域
- js没有块级作用域
if(true) {
var name = 'zhangsan'
}
console.log(name) //'zhangsan'
- 只有函数和全局作用域
var a = 100
function fn() {
var a = 200
console.log('fn', a) //'fn' 200
}
console.log('global',a) //'global' 100
fn()
四、作用域链
什么是自由变量?
var a = 100
function fn() {
var b = 200
console.log(a) //当前作用域没有定义的变量,即自由变量,这里的a就是自由变量,因为在函数作用域里面没有声明
console.log(b)
}
fn()
//注意,函数的父级作用域是定义时的作用域,不是执行时的作用域
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()
//F1的父级作用域是全局作用域
//F2的父级作用域是F1
//a在F2没有,就去父级作用域F1里面找,没有就去F1的父级作用域去找
//b在F2没有,就去父级作用域F1找
什么是作用域链:一个自由变量,一直不断的去父级作用域找,就像这里的a。
五、闭包
闭包的使用场景:
- 函数作为返回值
function F1() {
var a = 100
//返回一个函数(函数作为返回值)
return function() {
console.log(a)
}
}
var f1 = F1()
var a = 200
f1() //100
- 函数作为参数传递
function F1() {
var a = 100
//返回一个函数(函数作为返回值)
return function() {
console.log(a)
}
}
var f1 = F1()
function F2(fn) {
var a = 200
fn()
}
F2(f1) //100
六、题目解答:
- 说一下变量提升的理解
解答 :https://www.jianshu.com/p/2f4659dfaed3
- 说明this几种不同的使用场景
解答:作为构造函数执行、作为对象属性执行、作为普通函数执行、call aapply bind
- 创建10个<a>标签,点击的时候弹出对应的序号
解答:
//这是错误的写法,因为我们点击不知道是什么时间,等我们点击的时候,for循环早就执行结束了,然后i就一直是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)
}
//这是正确写法,这里把每个i都存在函数里面,这里创建了是个函数,每个函数里
面有一个i
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)
}
- 如何理解作用域
解答:自由变量、作用域链、闭包的场景
- 实际开发中闭包的应用
解答:
//主要用于封装变量,收敛权限
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