博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一天一个知识点 - 浅谈 JavaScript 作用域
阅读量:6003 次
发布时间:2019-06-20

本文共 3312 字,大约阅读时间需要 11 分钟。

前言

前些日子,在掘金上看到一片热门文章。该文作者以面试官的角度,详细阐述了作为一名 web 应聘者应该具有哪些技能,才会更让人青睐。

在对比自身的过程中,发现有一些问题,或许了解,但不全面,这也是本系列文章诞生的缘由。

什么是作用域

在 JavaScript 中,作用域为可访问变量,对象,函数的集合。 根据作用域范围,又分为全局作用域(Global)和局部作用域(Local)

什么是全局作用域

全局作用域(Global)是贯彻应用程序整个声明周期的的可访问变量,对象,函数的集合,换言之,只要它没有被删除或被覆盖,我们便可以在任何位置、任何时间使用它。

例如 window 这个内置的 JavaScript 对象就是一个全局对象,拥有全局作用域。

我们可以方便的创建一个全局对象(但切记不要滥用全局作用域,它可能会对应用的性能产生影响,也同样容易被其它使用者覆盖)。

/* * 在函数外部创建的变量、对象、函数都是全局的,拥有全局作用域 * msg1 就是一个全局变量,可在 func1 函数内部使用 */var msg1 = 'hello world'var func1 = function(){    console.log(msg1)}func1() => hello world复制代码

什么是局部作用域

小贴士: 局部作用域也叫函数作用域

局部作用域(Local)是指那些在函数内部声明的变量,对象,函数的集合,局部作用域只在当前上下文有效。

/* * 在函数外部创建的变量、对象、函数都是全局的,拥有全局作用域 * msg1 就是一个全局变量,可在 func1 函数内部使用 */var msg1 = 'hello world'var func1 = function(){    /*     * 在函数内部创建的变量、对象、函数都是局部的,拥有局部作用域     * msg2 就是一个局部变量,只能在 func1 函数内部使用     */    var msg2 = 'hello world !!!'    console.log(msg2)}func1() => hello world !!!// 当我们视图打印 func1 内部的 msg2 时,提示 msg2 未声明。console.log(msg2) => Uncaught ReferenceError: msg2 is not defined复制代码

局部作用域 - 变量提升的大坑

思考以下代码:

/* * func1 出人意料的结果!!! * '判断开始' 为什么不是应该输出 a is not defined * '判断结束' 为什么不是应该输出 a is not defined */var func1 = function(){    console.log('判断开始:' + a)        if(1 === 1){        var a = 1        console.log('判断语句:' + a)    }        console.log('判断结束:' + a)}func1() => 判断开始:undefined判断语句:1判断结束:1复制代码

JavaScript 中,函数及变量的声明(而非赋值)都将被提升到函数的最顶部

好吧,被打败了。依照这个原则,我们看看这个函数,到底是怎么执行的。

/* * 因为存在变量提升原则,函数及变量的声明都将被提升到函数的最顶部 * 因此 var a = 1 实际在函数开头就被定义好了 * 这样看起来就解释的通了 */var func1 = function(){    var a    console.log('判断开始:' + a)        if(1 === 1){        a = 1        console.log('判断语句:' + a)    }        console.log('判断结束:' + a)}func1() => 判断开始:undefined判断语句:1判断结束:1复制代码

所以,前人在填补了无数这样的坑之后,总结了一个道理:

请一定一定在函数开头,定义变量。

局部作用域 - 块级作用域

在早期的 JavaScript 语法中,是没有块级作用域的。在 ES6 之后,引入了 const 和 let 关键字。const 和 let 的引入帮我们解决了变量提升导致的隐形问题。

回到刚才的例子:

/* * func1 终于正常了的结果 * let 关键字声明的变量,称之为块级作用域。 * 也就是说,在该变量声明之前,是没有办法访问它的。 */var func1 = function(){    console.log('判断开始:' + a)        if(1 === 1){        // 使用 let 关键字声明变量        let a = 1        console.log('判断语句:' + a)    }        console.log('判断结束:' + a)}func1() => Uncaught ReferenceError: a is not defined复制代码

let 和 const 关键字声明的变量,也是局部变量,但它的作用域不是局部作用域,而是块级作用域。它的作用域绑定变量声明的区域不受外部影响,也不能被外部使用。

在语法上,称之为“暂时性死区”

局部作用域 - 词法作用域(闭包)

在局部作用域中,还有一类是词法作用域(闭包)

/* * 这是一个典型的闭包结构 * 在函数 func1 内部,定义了另外一个函数,这个函数的作用是输出 x 和 y 相加的字符串 */var func1 = function(x){    return function(y){        console.log(x + ' ' + y)    }}/* * 得益于闭包的定义规则(闭包就是创建一个了上下文环境,这个环境包含了创建时所能访问的所有局部变量) * f1 和 f2 共享同一个函数定义,却拥有各自不同的上下文环境 */var f1 = func1('hello')var f2 = func1('HELLO')f1('world') => hello worldf2('WORLD') => HELLO WORLD复制代码

什么是作用域链

谈到作用域,不得不谈到作用域链。

什么是作用域链?

作用域链是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问

用人话来说就是:

当一个函数执行时,实际上存在一些的规则,这些规则指明当前函数能够如何访问到有权限变量、对象和函数。

我们通过一段代码来理解作用域链:

/* * 函数有一个内部属性 [[scope]],当函数创建的时候,就会保存自己和所有父变量对象到其中 * 当 func1() 运行时。 * func2 的 [[scope]] = func2.[[scope]] 和 func1.[[scope]] 和 global.[[scope]] * 因此 func2 实际计算的是 func2.[[scope]].msg2 + func1.[[scope]].msg1 + global.[[scope]].msg */ var msg = '0'var func1 = function(){    var msg1 = '1'        var func2 = function(){        var msg2 = '2'                return msg + msg1 + msg2    }        return func2()}func1() => 012复制代码

参考

系列文章

转载于:https://juejin.im/post/5c8602e7e51d453bfd3dc6f2

你可能感兴趣的文章
如何配置Log4Net使用Oracle数据库记录日志
查看>>
一道在知乎很火的 Java 题——如何输出 ab【转】
查看>>
Python学习第二天-编写购物车
查看>>
AD域组策略-只显示指定的控制面板选项配置方法
查看>>
BigTable——针对结构型数据的一种分布式存储系统
查看>>
ip通信基础第二周回顾
查看>>
唯美MACD-完全版
查看>>
Canvas中 drawImage绘制图片不显示
查看>>
MyEclipse新建Server项目
查看>>
茵茵的第一课
查看>>
Linux实战教学笔记53:开源虚拟化KVM(一)搭建部署与概述
查看>>
PAT 1007
查看>>
USACO习题:Friday the Thirteenth
查看>>
C++ VS2012 内存泄露检测
查看>>
zabbix 批量添加聚合图形
查看>>
北京交通大学第六届新生程序设计竞赛题解
查看>>
求解点关于直线的距离、垂足、对称点公式
查看>>
洛谷 P1577 切绳子【二分答案】
查看>>
用 Google Map 的 Geocoder 接口来反向地址解析
查看>>
在中小型公司如何做好测试——论测试计划的重要性
查看>>