一次让你了解全部JavaScript的作用域

前言

作用域决定了变量的生命周期和可见性,变量在作用域范围之外是不可见的。

JavaScript 的作用域包括:模块作用域,函数作用域,块作用域,词法作用域和全局作用域。

全局作用域

在任何函数、块或模块范围之外定义的变量具有全局作用域。可以在程序的任意位置访问全局变量。
当启用模块系统时,创建全局变量会变得困难,但仍然可以做到这一点。可以在 HTML 中定义一个变量,这个变量需要在函数之外声明,这样就可以创建一个全局变量:

<script>
let GLOBAL_DATA = { value : 1};
</script>
console.log(GLOBAL_DATA);

当没有模块系统时,创建全局变量会容易很多。在任何文件中的函数外声明的变量都是全局变量。
全局变量贯穿于程序的整个生命周期。

另一种创建全局变量的方法是在程序的任意位置使用 window 全局对象:

window.GLOBAL_DATA = { value: 1 };

这样 GLOBAL_DATA 变量会随处可见。

console.log(GLOBAL_DATA)

不过你也知道这种做法是不好的。

模块作用域

如果不启用模块,在所有函数之外声明的变量是全局变量。在模块中,在函数外部声明的变量都是隐藏的,除非显式导出,否则不可用于其他模块。

导出使函数或对象可用于其他模块。在这个例子中,我从模块文件 sequence.js 中导出了一个函数:

// in sequence.js
export { sequence, toList, take };

当前模块可以通过导入来使用其他模块的函数或对象成。

import { sequence, toList, toList } from "./sequence";

在某种程度上,我们可以认为模块是一个自动执行的函数,它将 import 的数据作为输入,然后返回 export 的数据。

函数作用域

函数作用域意味着在函数中定义的参数和变量在函数内的任何位置都可见,但是在函数外部不可见。

下面是一个自动执行的函数,被称为IIFE。

(function autoexecute() {
let x = 1;
})();
console.log(x);
//Uncaught ReferenceError: x is not defined

IIFE 的意思是立即调用函数表达式,是一个在定义后立即运行的函数。

用 var 声明的变量只有函数作用域。更重要的是,用 var 声明的变量被提升到其作用域的顶部。通过这种方式,可以在声明之前访问它们。看看下面的代码:

function doSomething(){
console.log(x);
var x = 1;
}
doSomething(); //undefined

这种事不会发生在 let 中。用 let 声明的变量只能在定义后访问。

function doSomething(){
console.log(x);
let x = 1;
}
doSomething();
//Uncaught ReferenceError: x is not defined

用 var声明的变量可以在同一作用域下多次重新声明:

function doSomething(){
var x = 1
var x = 2;
console.log(x);
}
doSomething();

用 let 或 const 声明的变量不能在同一作用域内重新声明:

function doSomething(){
let x = 1
let x = 2;
}
//Uncaught SyntaxError: Identifier 'x' has already been declared

也许我们可以不必关心这一点,因为 var 已经开始变得过时了。

块作用域

块作用域用花括号定义。它由 { 和 } 分隔。

用 let 和 const 声明的变量可以受到块作用域的约束,只能在定义它们的块中访问。

思考下面这段关于 let 块范围的代码:

let x = 1;
{
let x = 2;
}
console.log(x); //1

相反,var 声明不受块作用域的约束:

var x = 1;
{
var x = 2;
}
console.log(x); //2

另一个常见问题是在循环中使用类似 setTimeout() 的异步操作。下面的循环代码将显示五次数字 5。

(function run(){
for(var i=0; i<5; i++){
setTimeout(function logValue(){
console.log(i); //5
}, 100);
}
})();

带有 let 声明的 for 循环语句在每次循环都会创建一个新的变量并设置到块作用域。下一段循环代码将会显示 0 1 2 3 4 5。

(function run(){
for(let i=0; i<5; i++){
setTimeout(function log(){
console.log(i); //0 1 2 3 4
}, 100);
}
})();

词法作用域

词法作用域是内部函数访问定义它的外部作用域的能力。

看一下这段代码:

(function autorun(){
let x = 1;
function log(){
console.log(x);
};
function run(fn){
let x = 100;
fn();
}
run(log);//1
})();

log 函数是一个闭包。它从父函数 autorun() 引用 x 变量,而不是 run() 函数中的 x 变量。

闭包函数可以访问创建它的作用域,而不是它自己的作用域。

autorun() 的局部函数作用域是 log() 函数的词法作用域。

作用域链

每个作用域都有一个指向父作用域的链接。当使用变量时,JavaScript 会向下查看作用域链,直到它找到所请求的变量或者到达全局作用域(即作用域链的末尾)。

看下面这个例子:

let x0 = 0;
(function autorun1(){
let x1 = 1;
(function autorun2(){
let x2 = 2;
(function autorun3(){
let x3 = 3;
console.log(x0 + " " + x1 + " " + x2 + " " + x3);//0 1 2 3
})();
})();
})();

内部函数 autorun3() 可以访问本地 x3 变量。还可以从外部函数访问变量 x1 和 x2 和全局变量 x0 。

如果找不到变量,它将在严格模式下返回错误。

"use strict";
x = 1;
console.log(x)
//Uncaught ReferenceError: x is not defined

非严格模式也被称为“草率模式”,它会草率的创建一个全局变量。

x = 1;
console.log(x); //1

总结

在全局作用域中定义的变量可在程序的任何位置使用。

在模块中,在函数外部声明的变量都是隐藏的,除非被显式导出,否则不可用于其他模块。

函数作用域意味着函数中定义的参数和变量在函数的任意位置都可见

用 let 和 const 声明的变量具有块作用域。 var 没有块作用域。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • JS块级作用域和私有变量实例分析

    本文实例讲述了JS块级作用域和私有变量.分享给大家供大家参考,具体如下: 块级作用域 (function(){ //这里是块级作用域 })() 例如: (function(){ for(var i=0;i<5;i++){ alert(i);//0,1,2,3,4 } })(); alert(i);//error 上例中,定义了一个块级作用域,变量i在块级作用域中可见的,但是在块级作用域外部则无法访问. 这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数. 私有变

  • js作用域和作用域链及预解析

    变量---->局部变量和全局变量, 作用域:在某个空间范围内,可以对数据进行读写操作 局部作用域和全局作用域 js中没有块级作用域-一对括号中定义的变量,这个变量可以在大括号外面使用 函数中定义的变量是局部变量 作用域链:变量的使用,从里向外,层层的搜索,搜索到了就可以直接使用了 层层搜索,搜索到0级作用域的时候,如果还是没有找到这个变量,结果就是报错 在 JavaScript 中, 对象和函数同样也是变量. 在 JavaScript 中, 作用域为可访问变量,对象,函数的集合. JavaScr

  • JavaScript变量作用域及内存问题实例分析

    本文实例讲述了JavaScript变量作用域及内存问题.分享给大家供大家参考,具体如下: 学习要点: 1.变量及作用域 2.内存问题 JavaScript的变量与其他语言的变量有很大区别.JavaScript变量是松散型的(不强制类型)本质,决定了它只是在特定时间用于保存特定值的一个名字而已.由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据类型可以在脚本的生命周期内改变. 一.变量及作用域 1.基本类型和引用类型的值 ECMAScript变量可能包含两种不同的数据类型的值:

  • JavaScript静态作用域和动态作用域实例详解

    静态作用域指的是一段代码,在它执行之前就已经确定了它的作用域,简单来说就是在执行之前就确定了它可以应用哪些地方的作用域(变量). 动态作用域–函数的作用域是在函数调用的时候才决定的 JavaScript采用的是词法作用域即静态作用域: // 静态作用域: var a = 10; function fn() { var b = 1; console.log(a + b); } fn(); // 11 在创建fn函数时的时候就已经确定了它可以作用哪些变量,如果函数fn里面有变量a就直接操作变量a,

  • 全面了解JavaScript的作用域链

    JavaScript的作用域链 这是一个非常重要的知识点了,了解了JavaScript的作用域链的话,能帮助我们理解很多'异常'问题. 下面我们来看一个小例子,前面我说过的声明提前的例子. var name = 'Skylor.min'; function echo() { alert(name); var name = 'mm'; alert(name); alert(age); } echo(); 对于这个例子,没有接触过这方面的时候,第一反应是会纠结下,这第一个的name,到底调用全局变量

  • 图解javascript作用域链

    先来一段简单的javascript代码: window.onload=function(){ function sub(a,b){ return a-b; } var result=sub(10,5); } 这段代码中的执行环境已经数清楚了,可是执行环境只是代码在预编译过程中javascript引擎所做的事情,当代码在window onload事件被触发,且sub函数被执行的时候会发生什么事情呢? 1.javascript引擎会在页面加载脚本被执行时为每个函数创建一个作用域(执行上下文)及作用域

  • 网易JS面试题与Javascript词法作用域说明

    调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量).函数参数及Arguments对象都在函数内的作用域中--这意味着它们隐藏了作用域链更上层的任何同名的属性. 2010年9月14日,我去参加网易网页工程师招聘会,应聘JS工程师职位.有幸参加笔试,然后有幸栽在笔试,呵呵.废话少说,抓出音响极深的一题重新研究研究. 题目大概是:写出如下代码的输出结果并进行分析 复制代码 代码如下: var tt = 'aa'; function test(){ alert(tt); var tt

  • 轻松5句话解决JavaScript的作用域

    javascript 程序的每一个字节都是在这个或那个运行上下文(execution context)中执行的.你可以把这些上下文想象为代码的邻居,它们可以给每一行代码指明:从何处来,朋友和邻居又是谁.没错,这是很重要的信息,因为 javascript社会有相当严格的规则,规定谁可以跟谁交往.运行上下文则是有大门把守的社区而非其内开放的小门. 我们通常可以把这些社会边界称为作用域,并且有充足的重要性在每一位邻居的宪章里立法,而这个宪章就是我们要说的上下文的作用域链(scope chain).在特

  • 理解JavaScript变量作用域更轻松

    JavaScript本身作为一门简单的语言,就其变量作用域问题一样令不少人头晕,这主要是因为JavaScript闭包的存在.本文不打算深入讲解JavaScript变量作用域问题(其实本人也没有能力能把这一话题讲的深入些),也不讲"闭包"话题,本文只讨论最实用的JavaScript作用域知识点. 一.JavaScript作用域分类 JavaScript就两种作用域:全局(window).函数级(function).函数级(function)不要理解为"块级(大括号{}级)&qu

  • 跟我学习javascript的作用域与作用域链

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. 一.JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1. 全局作用域(Global

  • JavaScript函数作用域链分析

    本文实例分析了JavaScript函数作用域链.分享给大家供大家参考.具体分析如下: 作用域链: JavaScript的每个函数function都有自己的作用域,使用Active Object(简称AO)活动对象来保存,在相互嵌套的函数中形成了作用域链,如下图所示: 作用域链就是从里到外的AO链 变量的寻找: 函数fn3中使用的变量,如在fn3作用域内寻找不到,则往外层fn2作用域寻找,以此类推,直到全局对象window 代码演示如下: var c = 5; function t1(){ var

  • 一次让你了解全部JavaScript的作用域

    前言 作用域决定了变量的生命周期和可见性,变量在作用域范围之外是不可见的. JavaScript 的作用域包括:模块作用域,函数作用域,块作用域,词法作用域和全局作用域. 全局作用域 在任何函数.块或模块范围之外定义的变量具有全局作用域.可以在程序的任意位置访问全局变量. 当启用模块系统时,创建全局变量会变得困难,但仍然可以做到这一点.可以在 HTML 中定义一个变量,这个变量需要在函数之外声明,这样就可以创建一个全局变量: <script> let GLOBAL_DATA = { value

  • 深入了解JavaScript词法作用域

    JavaScript并不是传统的块级作用域,而是函数作用域! 一.作用域 JavaScript引擎在代码执行前会对其进行编译,在这个过程中,像var a = 2 这样的声明会被分解成两个独立的步骤: 第一步(编译阶段):var a 在其作用域中声明新变量.这会在最开始的阶段,也就是代码执行前进行. 第二步(运行阶段):a = 2 会查询变量a(LHS查询)并对其进行赋值. LHS & RHS(当前作用域->上级作用域->...->全局作用域) LHS(左侧):试图找到变量的容器本

  • JavaScript中作用域链的概念及用途讲解

    从零开始讲解JavaScript中作用域链的概念及用途 引言 之前我写过一篇关于JavaScript中的对象的一篇文章,里面也提到了作用域链的概念,相信大家对这个概念还是没有很深的理解,并且这个概念也是面试中经常问到的,因为这个概念实在太重要了,在我们平时写代码时,也可能会因为作用域链的问题,而出现莫名其妙的bug,导致我们花费大量的时间都查找不出原因.所以我就准备单独写一篇关于作用域链的文章,来帮大家更好地理解这个概念. 正文 一.执行环境 首先,我们要引入一个概念,叫做执行环境(下面简称环境

随机推荐