谈谈JavaScript中函数的作用域和预解析

1,003 阅读4分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

前言

大家好哇,今天我们来谈谈JavaScript中函数的作用域的概念,对于非科班出身的小伙伴而言,这其中的概念还是很容易被搞混的,所以今天我们就来好好谈一下这个问题。

文章目标

先来看一下本文的目标吧,大神请绕道,因为本文基本摘自我刚入行时的笔记哦~

重点

1. 知道词法作用域的规则
2. 知道如何使用字面量形式创建一个对象
3. 知道如何使用构造函数批量的创建对象
4. 熟记new关键字的作用

函数的作用域

函数的作用域的还是比较常用的概念,同时也会涉及到参数生效范围、this的指向等问题。

作用域链

作用域链:当一段js代码中书写完毕之后,就会形成一个作用域链

var num = 0    // 全局变量
function fn(){
    var num1 = 1    // fn的局部变量
    function fnSon(){
        var num2 = 2    // fnSon的局部变量
    }
}

上面的代码对应的作用域的图解:

作用域链.png

词法作用域规则

什么是词法作用域规则

  1. 词法作用域又叫做静态作用域(因为代码书写完毕之后,作用域以及作用域链就形成了)

  2. 它规定了js中访问变量的规则

为什么会有词法作用域规则

  1. 掌握了词法作用域的规则,才能更清楚的知道代码是如何执行的

  2. 面试的过程中,经常会出现词法作用域规则的笔试题

词法作用域有哪些规则:

  1. 内部的作用域可以访问外部作用域的变量,但是外部的作用域不可以访问内部作用域的变量

  2. 访问变量或者给变量赋值时,如果当前作用域有变量则使用当前作用域的变量,如果没有则顺着作用链往上找,直到全局

内部作用域可以访问外部作用域的变量,如:

var num = 0
function fn(){
    console.log(num)    // 0  内部作用域可以访问外部作用域的变量
}
fn()

外部作用域不可以访问内部作用域的变量,如:

function fn(){
    var num1 = 1
}
fn()
console.log(num1)    // 报错: num1 is not defined  

当前作用域有,则使用当前作用域的变量,如:

var num = 0
function fn(){
    var num = 1
    console.log(num)     // 1  
}

当前作用域没有,则顺着作用链往上找,如:

var num = 0
function fn(){
    function fnSon(){
        console.log(num)     // 0  
    }
}

词法作用域题目

相信通过上面的了解,你对词法作用域已经有了一个基本的了解,下面我们来做几道题目验证一下。感兴趣的话可以把你的答案贴在评论区哦~

第一题

function fn(){
    var a = 1, b = 1, c = 1
}
fn()
console.log(c)    //
console.log(b)    //
console.log(a)    //

第二题

var num = 123
function f1(num) {
  console.log(num); //  
}
function f2() {
  var num = 456
  f1(num)
}
f2()

第三题

var a = 1
function fn(){
    var a = 2
    function fnSon(a){
        a = 3
        console.log(a)    //
    }
    fnSon()
    console.log(a)    // 
}
console.log(a)    // 
fn()
console.log(a)    // 

预解析(变量与函数提升)

预解析:JS引擎 会在 执行js代码之前,扫描一遍所有的代码,会将用var 声明的变量和函数申明提前加载到内存中,就相当于把用var声明变量和函数声明写在了代码的最上面,所以又叫做变量提升和函数提升。

变量提升

如下代码:

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

这段代码可以正常执行,由于预解析,把num变量提前加载到内存中,所以就相当于把var num写在了代码的最上面。即:

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

函数提升

注意: 只有函数声明会提升,函数表达式不会提升

fn()    // hello world
function fn(){
  console.log('hello world')
}

这段代码可以正常执行,由于预解析,把fn函数提前加载到内存中,所以就相当于把fn函数写在了代码的最上面。

如果变量名和函数名冲突的话,那么函数名会覆盖变量名。如:

function fn(){}
var fn = 2
console.log(fn)    // 打印的是函数

函数中的代码执行之前也会先进行预解析

function fn(){
    console.log(num)    // undefined 
    var num
    fnSon()
    function fnSon(){
     console.log('fnSon里面的代码')
    }
}
fn()

这段代码可以正常执行,因为函数内的代码执行之前,也会先进行预解析。

后记

你好哇,我是南极大冰块,一个技术与颜值成正比的前端工程师,崇尚一针见血的搞定前端问题,希望我的博客有帮助到了你。

关注我,前端路途一起走。嘿哈~😛