JavaScript之变量及作用域

2018-10-13 admin

本文共 1700 字,读完只需 7 分钟

概述

变量,编程语言中我们用来模拟现实概念的工具,比方说,变量可以表示对象,数组,数字,字符。既然是工具,那么就用工具的适用范围,这个工具在这个适用范围中才有效,在编程语言中,我们称这个适用范围叫作用域(scope)

本文会总结 JS 中作用域的相关概念。

  1. 什么是作用域
  2. 全局作用域
  3. 函数作用域
  4. 块级作用域
  5. 词法作用域(静态作用域)
  6. 作用域链

一、什么是作用域?

作用域, 英文意思是 scope, 我自己的话来理解就是:

变量访问规则的有效范围

  1. 作用域外,无法引用作用域内的变量
  2. 离开作用域后,作用域的变量的内存空间会被清除,比如执行完函数或者关闭浏览器。

二、全局作用域

先看一段代码:

foo = "bar";
console.log(window.foo);  // bar

在浏览器环境中声明变量,该变量会默认成为全局 windows 对象的属性。

再看下面这段代码:

function foo() {
    name = "bar"
}
foo();
console.log(window.name) // bar

在函数中,如果不加 bar声明一个变量,那么这个变量会默认被声明为全局变量,如果是严格模式则会报错。

全局变量可以在任何地方访问到,但是有很大的问题存在。

全局变量会造成命名污染,如果在多处对同一个全局变量进行操作,那么就会覆盖全局变量的定义。同时全局变量数量过多,非常不方便管理。

这也是为什么像jQuery 和 underscore 这样的类库,要在全局建立 $ 和 _ 变量,其余私有方法属性挂载到该全局变量下。

三、函数作用域

JS 是函数作用域,在函数中定义一个局部变量,那么该变量只可以在该函数作用域中被访问。

function doSomething() {
    var thing = "吃早餐";
}

console.log(thing);  // Uncaught ReferenceError: thing is not defined

嵌套函数作用域:

function outter() {
    var thing = "吃早餐";
    function inner() {
        console.log(thing);
    }
    inner();
}

outter();  // 吃早餐

在外层函数中,嵌套一个内层函数,那么这个内层函数可以向上访问到外部作用域的变量。

那么,既然内层函数可以访问到外层函数的变量,那么把内层函数返回后呢?

function outter() {
    var thing = "吃晚餐";

    function inner() {
        console.log(thing);
    }
    return inner;
}

var foo = outter();
foo();  // 吃晚餐

前面我们提到了,函数执行完后,函数作用域的变量会被垃圾回收,以上代码可以看出当我们返回了一个访问了外部函数变量的内部函数,最后外部函数的变量得以保存。

这种当变量存在的函数已经执行结束,但仍在可以访问的方式就是`闭包`。

闭包的具体实践,后续文章会详细说明。

四、块级作用域

JS 在 ES6 之前只有函数作用域,没有块级作用域的概念。 看一下代码:

function doSomething() {
    for (var i = 0; i < 10; i++) {
        ...
    }
    console.log(i)
}
doSomething();  // 10

由于 JS 没有块级作用域,变量 i 在函数作用域中只有一个,每次 for 循坏都在改变这一个变量。

再看阮一峰老师 ES6 教程里的一段代码:

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6]();   // 10;

以上代码中,由于没有块级作用域,i 变量全局只有一个,当 for 循坏结束,变量 i 的值等于 10, 所以 a[6]() 对应函数内的变量 i 的打印值就是 10。

ES 6 中通过 letconst关键字 引用了块级作用域的概念,所谓块级作用域,就是以 {}包裹的区域。

我们将阮一峰老师 ES6 教程里的一段代码改成 let 的形式:

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6]();   // 6;

这时,数组内的索引为6函数内的变量打印值为6,每次循环,会创建新的块级作用域,然后重新声明一个新的变量 i;JS 的解释引擎会记住上次循环的变量值,所以能够返回正确的结果。

letconst 会声明一个块级作用域的变量及常量,不易发生变量命名污染的问题,能规避冲突,帮助你写出简洁优雅的代码,建议一直使用。

五、词法作用域(静态作用域)

词法作用域,也可以叫做静态作用域,是什么意思呢?

无论函数在哪里调用,词法作用域都只由函数被声明时所处的位置决定。

既然有静态作用域,那么也有动态作用域。

而动态作用域的作用域则是由函数被调用执行的位置所决定。

var a = 123;

function func1() {
    console.log(a);
}

function func2() {
    var a = 456;
    func1();
}

func2(); // 123

以上代码,最后输出结果 a 的值,来自于 func1 声明时所在位置访问到的 a 值 123。

所以 JS 的作用域是静态作用域,也叫词法作用域。

六、作用域链

在 JS 引擎中,通过标识符查找标识符的值,会从当前作用域向上寻找,直到作用域找到第一个匹配的标识符为止。就是 JS 的作用域链

如果嵌套作用域有多个相同标识符,那么,最内部的标识符会覆盖外层标识符,这叫做“遮蔽效应”

var a = 1;
function func1() {
    var a = 2;
    function func2() {
        var a = 3;
        console.log(a);  // 3
    }
    func2();
}

func1(); // 3

func2 中变量 a,会从内部开始向外部上层寻找,找到最近的 a 标识符的声明为止。

总结

JS 是一门基于词法作用域(静态作用域)的语言,JS 会沿着作用域链像气泡一样向外部寻找变量声明。

JS 又是函数作用域的语言,在 ES6 中,使用 letconst 关键字后,能让变量处于块作用域中,而且不存在声明提升。

后面的文章会介绍 JS 中的声明提升和闭包,敬请期待。

原文链接:https://segmentfault.com/a/1190000016670383

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-42583.html

文章标题:JavaScript之变量及作用域

相关文章
破解前端面试(80% 应聘者不及格系列):从 闭包说起
不起眼的开始 招聘前端工程师,尤其是中高级前端工程师,扎实的 JS 基础绝对是必要条件,基础不扎实的工程师在面对前端开发中的各种问题时大概率会束手无策。在考察候选人 JS 基础的时候,我经常会提供下面这段代码,然后让候选人分析它实际运行的结...
2017-06-02
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
javascript是什么意思
avaScript是Netscape开发的一个对象脚本语言,它使用在世界各地数以百万计的网页和服务器应用程序上。 网景的JavaScript是ecma - 262版的标准脚本语言,和公布的标准只有轻微的差异。 与广为流行的错误理解相反,Ja...
2015-11-12
21天学通javascript
简介: 本书是Javascript入门教程。Javascript是Web开发中应用最早、发展最成熟、用户最多的脚本语言。其语法简洁,代码可读性在众多脚本语言中最好,它在使用时不用考虑数据类型,是真正意义上的动态语言。本书总分为四篇,共21章...
2015-11-16
JavaScript的组成
一个完整的JavaScript由3个部分组成:核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ECMAScript 描述了该语言的语法和基本对象 ; DOM 描述了处理网页内容的方法和接口 ; BOM 描...
2015-11-12
canvas图片绘制跨域问题解决方案Tainted canvases may not be exported
图片跨域问题的一般解决方法 当使用canvas绘制网络图片的时候,经常会出现“Tainted canvases may not be exported”报错,上网搜一下解决方案,应该给的都是给img添加crossOrigin属性,尝试了一下...
2018-04-19
JavaScript 事件流、事件处理程序及事件对象总结
JS与HTML之间的交互通过事件实现。事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用监听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式,支持页面的行为与页面的外观之间的松散耦合。...
2017-04-05
JavaScript变量的声明
声明变量 变量在脚本中的首次亮相是在其声明中。 在变量首次出现时将会在内存中设置它,因此您稍后可在脚本中引用它。 应在使用变量之前先声明变量。 可以使用 var 关键字实现此目的。 &lt;span id=“mt9” class=“sent...
2015-11-12
jQuery中DOM树操作之使用反向插入方法实例分析
本文实例讲述了jQuery中DOM树操作之使用反向插入方法。分享给大家供大家参考。具体分析如下: 使用反向插入方法 这里我们先把创建的内容插人到元素前面,然后再把同一个元素插人到文档 中的另一个位置。通常,当在jQuery中操作元素时,利用...
2015-11-13
7个提高效率的JavaScript调试工具
鐜板湪鐨凧avaScript浜嬪疄涓婂凡鐒舵垚涓轰簡娴佽�岀殑web璇�瑷€锛屽嵆浣垮畠骞朵笉瀹岀編銆傚緢澶氱▼搴忓憳涓嶅枩娆㈢敤JavaScript鍐欎唬鐮侊紝鏄�鍥犱负鍐欏埌鍚庢潵鎬讳細鍑虹幇鍚勭�嶈帿鍚嶅叾濡欑殑bug锛岃€屼笖鍦ㄥ紑...
2015-11-11
回到顶部