变量与作用域

1664 字
8 分钟
变量与作用域

变量#

  • 字面量
    字面量(常量),都是一些不可改变的值
    比如 :1 2 3 4 5
    字面量都是可以直接使用,但是我们一般都不会直接使用字面量
  • 变量
    变量可以用来保存字面量,而且变量的值是可以任意改变的
    变量更加方便我们使用,所以在开发中都是通过变量去保存一个字面量,而很少直接使用字面量,可以通过变量对字面量进行描述

传递参数#

  • 所有函数的参数都是按值传递的,在按值传递参数时,值会被复制到一个局部变量中(也就是arguments对象中的一个槽位) function setName(obj) { obj.name = “aaa” obj = new Object() obj.name = “bbb” } var person = new Object() setName(person) console.log(person.name) // aaa 如果是按照引用传递的话,person将会指向name为”bbb”的对象,而不是任然指向”aaa”的对象。 当obj在函数内部重写时,它变成了一个指向本地对象的指针,而那个本地对象在函数执行结束时就被销毁了。

执行上下文与作用域#

变量提升与函数提升#

  • 变量声明提升:通过var定义(声明)的变量, 在定义语句之前就可以访问到,但值: undefined

  • 函数声明提升:通过function声明的函数, 在之前就可以直接调用,但 值: 函数定义(对象)

    //变量提升
    console.log(a1) //可以访问, 但值是undefined
    var a1 = 3
    //函数提升
    a2() // 可以直接调用,输出 a2()
    function a2() {
    console.log('a2()')
    }
    a3() //报错
    var a3 = function(){
    console.log('a3')
    }

遇到函数表达式 var a3 = function(){}时,首先会将var a3上升到函数体顶部,然而此时的 a3 的值为 undefined,所以执行a3()报错。

  • 注:
    • 当遇到函数和变量同名且都会被提升的情况,函数声明优先级比较高(实际上是变量先提升到全局作用域中,但值为undefined,然后函数提升并且给函数直接赋值),因此变量声明会被函数声明所覆盖,但是可以重新赋值。

      console.log(a) //输出:function a(){ console.log('函数') }
      function a() {
      console.log("函数")
      } //
      var a = "变量"
      console.log(a) //输出:'变量'

function 声明的优先级比 var 声明高,也就意味着当两个同名变量同时被 function 和 var 声明时,function 声明会覆盖 var 声明。

这代码等效于:

function a() {
console.log("函数");
}
var a
console.log(a) //输出:function a(){ console.log('函数') }
a = "变量" //赋值
console.log(a) //输出:'变量'

执行上下文#

全局执行上下文#

* 在执行全局代码前将window确定为全局执行上下文
* 对全局数据进行预处理
* var定义的全局变量==>undefined, 添加为window的属性
* function声明的全局函数==>赋值(fun), 添加为window的方法
* this==>赋值(window)
* 开始执行全局代码

函数执行上下文#

* 在调用函数, 准备执行函数体之前, 创建对应的函数执行上下文对象
* 对局部数据进行预处理
* 形参变量==>赋值(实参)==>添加为执行上下文的属性
* arguments==>赋值(实参列表), 添加为执行上下文的属性
* var定义的局部变量==>undefined, 添加为执行上下文的属性
* function声明的函数 ==>赋值(fun), 添加为执行上下文的方法
* this==>赋值(调用函数的对象)
* 开始执行函数体代码

执行上下文栈#

JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循后进先出的原则。

  • JavaScript 执行在单线程上,所有的代码都是排队执行,遵循后进先出的原则。
  • 一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。
  • 每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收。
  • 浏览器的 JS 执行引擎总是访问栈顶的执行上下文。
  • 全局上下文只有唯一的一个,它在浏览器关闭时出栈。
  • var color = "blue"
    function changeColor() {
    var anotherColor = "red"
    function swapColors() {
    var tempColor = anotherColor
    anotherColor = color
    color = tempColor
    }
    swapColors()
    }
    changeColor()
    • 当上述代码在浏览器中加载时,JavaScript 引擎会创建一个全局执行上下文并且将它推入当前的执行栈
    • 调用 changeColor 函数时,此时 changeColor 函数内部代码还未执行,js 执行引擎立即创建一个 changeColor 的执行上下文,然后把这执行上下文压入到执行栈中。
    • 执行 changeColor 函数过程中,调用 swapColors 函数,同样地,swapColors 函数执行之前也创建了一个 swapColors 的执行上下文,并压入到执行栈中。
    • swapColors 函数执行完成,swapColors 函数的执行上下文出栈,并且被销毁。
    • changeColor 函数执行完成,changeColor 函数的执行上下文出栈,并且被销毁。

作用域#

  • 我们可以这样理解:作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
  • 在创建函数会创建函数作用域,函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。而外部作用域无法使用内部作用域的变量
  • 在ES6中引入了let关键字,用来在任意块中声明变量,在块中的变量无法在块作用域在进行引用 var foo = true; if (foo) { { // <— 显式的快 let bar = foo * 2; bar = something( bar ); console.log( bar ); } } console.log( bar ); // ReferenceError

作用域链#

  • 作用域链是由多个上下级关系的作用域形成的链,他的方向是从下向上的(从内向外),查找变量时就是沿着作用域链来查找的 var a = 2; function fn1() { var b = 3; function fn2() { var c = 4; console.log(c); console.log(b); console.log(a); console.log(d); }

    fn2();
    }
    fn1(); //4, 3, 2, ReferenceError

    上面例子中的作用域链就是fn2函数作用域—>fn1函数作用域—>全局作用域

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

变量与作用域
https://sukksa.github.io/posts/bian-liang-yu-zuo-yong-yu/
作者
Sukksa
发布于
2021-10-16
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Sukksa
记录前端学习与日常
公告
博客刚刚上线,内容还在慢慢补齐。欢迎随便逛逛~
分类
标签
最新动态
站点统计
文章
69
分类
16
标签
17
总字数
178,517
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录