 作用域、执行上下文和this
作用域、执行上下文和this
  # 前言
作用域和this一直都是js里令人头疼的问题,个人总结了一下自己对作用域以及this指向的一些理解。
# 作用域
# 什么是作用域?
作用域本身指的就是某个
变量所能被访问的区域,也就是你在某处定义了一个变量,那么有这么一个区域内,是可以访问这个变量的。出了这个区域。你就无法访问他了。对于这种现象,就可以理解为作用域而作用域从概念上来区分有两种,一是词法作用域,二是动态作用域。
# 词法作用域和动态作用域
词法作用域指的是变量在何处定义,他的作用域就在哪里。而动态作用域则是变量在何处被调用,则他的作用域就在何处。在js中采用的就是词法作用域
# 全局作用域,函数作用域,块级作用域
上面介绍了两种概念上的作用域,接下来介绍一下js中的三种作用域。
- 全局作用域
全局作用域,顾名思义,就是定义在全局的变量。在代码的任何地方都可以访问全局作用域内的变量。至于是为什么,这里留个疑问。
- 块级作用域
js本身是没有
块级作用域的,也就是我们定义在{}里的变量都会被提升到全局。而在es6引入了let关键字后,就有了块级作用域下面看一段代码示例
if(true){
    var a = 1
}
console.log(a)// 1
if(true){
    let a = 1
}
console.log(a) //a is not defined
2
3
4
5
6
7
8
9
在没有let之前,用var声明的变量会被提升到全局,所以我们在花括号外面也能访问到a。而在使用了let之后,就会形成块级作用域,也就是变量只能再花括号内访问了,所以第二段代码会报错,提示a为定义
- 函数作用域
最有意思的东西来了,
函数作用域,他是一个能牵扯出很多东西的作用域。许许多多复杂的概念都是在他里头出现的,比如闭包,作用域链,执行上下文等等。 那么他到底是个什么东西呢,其实可以把他理解成函数体内的区域,和块级作用域相似。都是花括号包裹的这个区域,不过在函数作用域内会发生许许多多奇怪的事情 例如,在函数内访问别的变量,就会牵扯出闭包,作用域链的概念,再比如在函数体内能访问哪些变量。这就又涉及到了函数的执行上下文的概念。下面就让我来一一解答一下
# 作用域链,执行上下文
- 执行上下文
先从
执行上下文开始讲起,执行上下文又分为全局执行上下文和函数执行上下文。可以把他理解成一个对象。这个对象上包含了当前可访问的活动变量,this等一些列东西。 js自身维护了一个执行上下文栈,没当一个函数被调用时,就会创建一个函数的执行上下文,将他压入栈内。这个执行上下文包含了他父级上下文的变量。栈的最底部就是全局执行上下文。 当我们在一个函数体内访问一个变量时,首先js会在当前函数执行上下文内查找是否有这个变量,没有的话就会去父级执行上下文内查找,一直到全局执行上下文为止。这也就是上文中为什么任何地方都可以访问到全局变量了。因为全局变量就被放在全局执行上下文里头
- 作用域链
根据执行上下文中变量查找的概念,是不是感觉很像原型链的查找,没错这种查找就可以称为是
作用域链。
# this
this到底是是个什么东西?
在js中this一般分为三种
- 在构造函数中,this指向当前被创建的对象
- 在函数调用中,this指向调用函数的那个对象
- 在call,bind,apply中,this指向传入的那个对象
- 在箭头函数里,指向父级执行上下文里的this
在不同的情况下,this会有不同的含义,下面通过几段代码来更深层次的理解一下
// 在函数中
let obj = {
    hi:'嗨喽',
    sayHi:function (){
        console.log(this.hi)
    }
}
obj.sayHi() // 嗨喽
// 在构造函数中
let obj2 = new function (){
    this.name = 111
    console.log(this) //{name:111}
}
// 在箭头函数中,因为父级是全局执行上下文,里面并没有hi这个变量,所以输出undefined
let obj = {
    hi:'嗨喽',
    sayHi:()=>{
        console.log(this.hi) //undefined
    }
}
// 在call,bind,apply中,this会变成传入的那个对象
let o = {hi:'我是o的hi属性'}
obj.sayHi.call(o) //'我是o的hi属性'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25