船长的航行日记
首页
  • 原生能力

    • JavaScript
    • Node
  • 框架能力

    • Vue
    • React
  • 学习笔记

    • 《TypeScript》学习笔记
    • 《JavaScript高程4》学习笔记
  • HTML
  • CSS
  • 最近在读
  • 奇思妙想
  • 读书收获
历史足迹
飞鸽传书
GitHub (opens new window)

captain

心之所向,海的彼岸
首页
  • 原生能力

    • JavaScript
    • Node
  • 框架能力

    • Vue
    • React
  • 学习笔记

    • 《TypeScript》学习笔记
    • 《JavaScript高程4》学习笔记
  • HTML
  • CSS
  • 最近在读
  • 奇思妙想
  • 读书收获
历史足迹
飞鸽传书
GitHub (opens new window)
  • JavaScript的历史回顾
  • HTML中的JavaScript
  • 语言基础
  • 变量、作用域、内存
    • 《JavaScript高程4》学习笔记
    masongsong
    2021-06-17
    时间 4分钟
    阅读量 0

    变量、作用域、内存

    # 变量

    需要注意的是:

    • 基本类型的值是按值访问(by value),
    • 引用类型是按引用访问(by refernce)。
    • 函数内的参数是按值传递

    举个例子:

    let num1 = 5
    let num2 = num1
    console.log(num2) // 5
    
    1
    2
    3

    上述代码发生的事情是:

    • 首先在内存空栈中,开辟一个新的空间用于存储num2变量
    • 然后将num1的值复制一份给num2

    由于 num1 和 num2 是 按值传递 的,所以修改其中一个的值,并不会对影响另外一个值。

    let num1 = 5
    let num2 = num1
    num2 = 10
    console.log(num2) // 10
    console.log(num1) // 5
    
    1
    2
    3
    4
    5

    而对于引用类型来说,则是 按照引用 传递的,举个例子:

    const person = { name: 'Jack' }
    const person1 = person
    
    console.log(person1.name) // Jack
    
    1
    2
    3
    4

    上述代码发生的事情是:

    • 将 person1 指向 person 引用的内存地址。 也就是,现在 person 和 person1 指向同一个地址,修改其中一个的值,都会修改另外一个对象的值,因为此时他们都指向的是同一个内存地址。
    const person = { name: 'Jack' }
    const person1 = person
    
    person1.name = 'Lucy'
    console.log(person.name) // Lucy
    console.log(person1.name) // Lucy
    
    1
    2
    3
    4
    5
    6

    这也是我们有时候需要对对象进行深拷贝的原因,我们又想操作原始对象的属性,但是又不想原始对象受到影响。

    函数内的参数 都是 按值传递 的,这个仔细想想也是有道理的。我们肯定是希望函数内部的操作是不会影响函数外部的变量的,不然代码也太难维护了。但是有一个令人迷惑的地方,函数的参数是引用类型时,在函数内部修改参数的值,同时也会影响函数外部。

    function test(obj) {
      obj.name = 'Lucy'
    }
    
    const person = {
      name: 'Jack'
    }
    test(person)
    console.log(person.name); // Lucy
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    从这个例子来看 似乎函数内部的参数是按 引用 传递的,再看这个例子:

    function test(obj) {
      obj.name = 'Lucy'
      obj = new Object();
      obj.name = "Greg";
    }
    
    const person = {
      name: 'Jack'
    }
    test(person)
    console.log(person.name); // Lucy
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    还是打印的是 Lucy,如果是按照 引用传递 的话,应该打印的是 Greg,因为 person 的引用指向了新的地址。所以,总的来说,不管是函数传递的参数是基本类型还是引用类型,都是 按值传递 的。

    # 确定类型

    确定基本类型,使用typeof:

    const num = 0
    console.log(typeof num) // number
    
    1
    2

    确定引用了类型使用 instanceof

    const obj = {}
    console.log(obj instanceof Object) // true
    
    1
    2

    当然,有个通用的做法就是,使用 Object.prototype.toString.call()方法:

    const num = 0
    const obj = {}
    
    // 判断基本类型
    console.log(Object.prototype.toString.call(num) === '[object Number]') // true
    
    // 判断引用类型
    console.log(Object.prototype.toString.call(obj) === '[object Object]') // true
    
    1
    2
    3
    4
    5
    6
    7
    8

    # 上下文

    变量和函数,都有一个 上下文 的概念,上下文决定了决定了它们可以访问哪些数据和变量。每个上下文都有一个变量对象,数据和变量都存在这个对象上。我们并不能通过代码访问这个这个对象,但是后台处理数据会用到它。

    上下文 是个笼统的概念,主要分为 全局上下文 和 局部上下文。在浏览器中 全局上下文 就是我们常说的window对象,在node中则是 global。函数上下文 就属于局部上下文。 这里值得关注的是 临时上下文,又如下两种情况会增加 临时上下文:

    • try/catch 语句的 catch 块
    • with 语句with 语句 这两种情况下,都会在作用域链前端添加一个变量对象。对 with语句来说,会向作用域链前端添加指定的对象;对 catch 语句而言,则会创建一个新的变量对象,这个变量对象会包含要抛出的错误对象的声明。

    比如,使用with创建一个临时上下文:

      const obj = {
        name: 'mss',
        age: 18
    }
    
    with(obj) {
      console.log(name, age) // mss 18
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    这里一旦执行完毕,这个上下文就会立刻销毁。

    # 块级作用域

    使用 var 声明变量的时候,有一个令人又爱又恨的特性,就是变量提升。 看个例子:

    console.log(num) // undefined
    var num = 1
    
    1
    2

    细心的朋友会发现,这里打印的是 undefined。在JavaScript中,这意味着该变量未初始化。这其实是令人困惑的,打印的时候,明明没有声明这个变量,按照正常的理解这里应该抛出错误的。但是上述的代码会被解析为:

    var num;
    console.log(num)
    num = 1
    
    1
    2
    3

    num变量被提升到作用域顶部了,也就导致打印出了 undefined。

    为了避免这种 变量提升后造成的各种魔幻的问题,ES6提出了 块级作用域,就是一个花括号 {} 就是一个块级作用域。花括号外部无法访问花括号内部的变量。 如下面的例子,提前打印num变量,就会抛出错误 num is not defined

    console.log(num) // num is not defined
    {
      let num = 1
      console.log(num) // 1
    }
    
    1
    2
    3
    4
    5
    编辑 (opens new window)
    上次更新: 2021/07/09, 23:00:24
    语言基础

    ← 语言基础

    最近更新
    01
    总结
    10-19
    02
    优化
    10-16
    03
    实现ref
    10-16
    更多文章>
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 生命绿
    • 收获黄
    • 天空蓝
    • 激情红
    • 高贵紫