JavaScript 作用域scope简单汇总

什么是作用域

程序的执行,离不开作用域,也必须在作用域中才能将代码正确的执行。

所以作用域到底是什么,通俗的说,可以这样理解:作用域就是定义变量的位置,是变量和函数的可访问范围,控制着变量和函数的可见性和生命周期。

而JavaScript中的作用域,在ES6之前和ES6之后,有两种不同的情况。

ES6之前,JavaScript作用域有两种:函数作用域和全局作用域。

ES6之后,JavaScript新增了块级作用域。

作用域的特性

在JavaScript变量提升的讨论中,我们其实是缺少了一个作用域的概念的,变量提升其实也是针对在同一作用域中的代码来说的。

对编译器的了解,让我们明白,对于一段代码【var a = 10】变量的赋值操作,其实是包含了两个过程:

1、变量的声明和隐式赋值(var a = undefined),这个阶段在编译时

2、变量的赋值(a = 10),这个阶段在运行时

先看一下如下代码:

var flag = true;

if(flag) {
  var someStr = 'flag is true';
}

function doSomething() {
  var someStr = 'in doSomething';
  var otherStr = 'some other string';
  console.log(someStr);
  console.log(flag);
}

doSomething();

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

console.log(i);

{
  var place = 'i do not want to be visited';
}

那么这一些代码在编译之后,执行之前,根据变量提升的机制,我们可以知道应该是下面这个样子:

function doSomething() { // 函数优先提升
  // 提升隐式赋值
  var someStr = undefined;
  var otherStr = undefined; 

  someStr = 'in doSomething';
  otherStr = 'some other string';

  console.log(someStr);
  console.log(flag);
}

// 隐式赋值和提升
var flag = undefined;
var someStr = undefined;
var i = undefined;
var place = undefined;

flag = true;

if(flag) {
  someStr = 'flag is true';
}

for(i = 0; i < 10; i++) {
  console.log(i);
}

doSomething();

console.log(i);

{
  place = 'i do not want to be visited';
}

因为变量的提升特性,以及无块级作用域的概念,所以代码中在同一个作用域中变量和函数的定义,在编译阶段都会提升到顶部。

通过上述代码,我们大体上可以得出作用域的特性:

第一、内部作用域和外部作用域是嵌套关系。外部作用域完全包含内部作用域。

第二、内部作用域可访问外部作用域的变量,但是外部作用域不能访问内部作用域的变量,(链式继承,向上部作用域查找)。

第三、变量提升是在同一个作用域内部出现的。

第四、作用域用于编译器在编译代码时候,确定变量和函数声明的位置。

块级作用域

上述代码,在ES6+的环境中运行,也是和ES6之前是相同的结果,但是ES6不是引用了块级作用域吗,为什么大括号块内的代码还是会出现和之前一样的编译方式呢?

那么,ES6中的块级作用域到底是什么?

let & const

利用var定义的变量,具有提升的性质,可能会影响代码的执行结果。

这是var定义变量的缺陷,那么如何规避这种缺陷呢?在ES6中,设计出来了let和const来重新定变量。

但是,由于JavaScript标准定义的非常早,1995年5月JavaScript方案定义,1996年微软提供了JavaScript解决方案JScript。而网景公司为了同微软竞争,神情了JavaScript标准,于是,1997年6月第一个国际标准ECMA-262便颁布了。

C语言标准化的过程却是将近二十年后才颁布。

所以,我们以后设计的语言既要兼容var也要有自己的块级作用域,让var和let以及const在引擎做到兼容。

所以,我们定义块级作用域的标准,只能从定义变量的方式入手,而不是直接一个{}块就可以解决。

先让我们看一下下面代码:

var name = 'someName';

function doSomething(){
  console.log(name);
  if(true) {
    var name = 'otherName';
  }
}
doSomething();
结果:undefined

产生这个结果的原因是我们函数内部的变量提升,覆盖了外部作用域的变量,也就是说,其实打印出来的值是doSomething函数中的变量声明的值。

但是这样却并不符合块级作用域的预期,如果有许多类似代码,理解起来也会相当困难。如果将代码用ES6方式改写:

let name = 'someName';

function doSomething(){
  console.log(name);
  if(true) {
    let name = 'otherName';
  }
}

doSomething();

结果:'someName'

从运行结果看,我们真正的做到了块级作用域应该有的效果,那么let和const又是如何支持块作用域的呢?

执行上下文

先想想一下JavaScript中的一个作用域两个执行上下文中的编译过程中的环境:

变量环境:编译阶段var声明存放的位置(一个大对象)。

词法环境:我们代码书写的位置,也是let和const的初始化位置(代码按词法环境顺序执行,按照{}划分的栈结构)。

而在编译阶段,我们将var定义的变量全都在编译过程在变量环境初始化为undefined,但是用let和const定义的变量,其实他们并未在变量环境初始化,而是在词法环境初始化,也就是执行代码位置初始化。

词法环境的特点:按照{}划分的一个栈结构。

变量查找方式

JavaScript中变量查找的方式:沿着词法环境的栈顶向下查找,找不到的变量去变量环境中查找,这样就形成了先查找代码块中的变量,再查找提升之后的变量环境,这样就形成了块级作用域的概念。

上面的代码形成两种环境的情况如下:

一、全局环境的执行上下文

变量环境:函数声明function doSomething() { … }

词法环境栈:执行到let name = ‘someName';让语句name = ‘someName'入栈。

二、doSomething的执行上下文(被全局环境包裹)

变量环境:无

词法环境栈情况:执行到let name = ‘otherName',语句的时候,name = ‘other'才会入栈;

JavaScript代码执行方式

执行doSomething的时候,还未执行let name = ‘otherName',所以,此时doSomething的词法环境中并未有name = ‘otherName',这个时候查找,只能向外部作用域查找(全局作用域)

此时查找到全局作用域name = ‘someName'所以此时就打印了someName

代码接着执行到了if语句内部,才会将name = ‘otherName'入栈,但是此时因为语句已经执行完毕,所以也就无关痛痒了。

JavaScript也就通过这种方式,实现了块级别作用域。

总结

JavaScript中的作用域总的来说,分为块级作用域、函数作用域、全局作用域。

而每个作用域都会创建自身的执行上下文,每一个执行上下文又分为了变量环境和词法环境两部分。

块级作用域的实现,其实是根据定义的let和const声明的变量放置在词法环境栈中这一特性来实现。

这一特性被社区的人叫做‘暂时性死区',但是在JavaScript标准中并未有这个概念。

只有理解了作用域的概念,才能真正明白JavaScript的执行机制,才能减少我们因为变量定义等发生的错误。

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

(0)

相关推荐

  • 详解angularjs 学习之 scope作用域

    简介 Scope(作用域) 是应用在 HTML (视图) 和 JavaScript (控制器)之间的纽带. Scope 是一个存储应用数据模型的对象,有可用的方法和属性. Scope 可应用在视图和控制器上. 作用域是Web应用的控制器和视图之间的粘结剂: 控制器 --> 作用域 --> 视图(DOM) 指令 --> 作用域 --> 视图(DOM) 当你在 AngularJS 创建控制器时,你可以将 $scope 对象当作一个参数传递: <div ng-controller=

  • 深入探究AngularJs之$scope对象(作用域)

    这两天学习了AngularJs之$scope对象这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记. 一.作用域 AngularJs中的$scope对象是模板的域模型,也称为作用域实例.通过为其属性赋值,可以传递数据给模板渲染. 每个$scope都是Scope类的实例,Scope类有很多方法,用于控制作用域的生命周期.提供事件传播功能,以及支持模板的渲染等. AngularJs的每个应用程序都有一个$rootScope,它是其他所有作用域的父作用域,它的作用范围从包含ng-app指令的H

  • AngularJs学习第五篇从Controller控制器谈谈$scope作用域

    Controller的创建 AngularJs controller使用无处不在,在里代码演示比较简单的创建工作. <!DOCTYPE html> <html xmlns="http://www.w.org//xhtml" ng-app="exampleApp"> <head> <meta http-equiv="Content-Type" content="text/html; charset

  • 深入理解JavaScript系列(14) 作用域链介绍(Scope Chain)

    前言 在第12章关于变量对象的描述中,我们已经知道一个执行上下文 的数据(变量.函数声明和函数的形参)作为属性存储在变量对象中. 同时我们也知道变量对象在每次进入上下文时创建,并填入初始值,值的更新出现在代码执行阶段. 这一章专门讨论与执行上下文直接相关的更多细节,这次我们将提及一个议题--作用域链. 英文原文:http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/ 中文参考:http://www.denisdeng.com/?p

  • javascript中的作用域scope介绍

    而在javascript中,变量的作用域是按函数来划分的--变量在某个函数范围内有效.比如: 复制代码 代码如下: var f = false; if(true) { var f = true; } //此时f位于if内,也就是块内,等价于还是全局范围内 alert(f) //所以,结果为true 再如下例: 复制代码 代码如下: var f = false; function test() { var f = true; //这是函数内定义的变量,只在函数内部有效,函数执行完毕后,该变量将被释

  • 关于angularJs指令的Scope(作用域)介绍

    每当一个指令被创建的时候,都会有这样一个选择,是继承自己的父作用域(一般是外部的Controller提供的作用域或者根作用域($rootScope)),还是创建一个新的自己的作用域,当然AngularJS为我们指令的scope参数提供了三种选择,分别是:false,true,{}:默认情况下是false. 1.scope = false JS 代码: html 代码: result: 修改文本框的内容,两个名字都会变,实际上修改的是同一个$scope的name属性. 2. scope=true

  • Angular.js之作用域scope'@','=','&'实例详解

    什么是scope AngularJS 中,作用域是一个指向应用模型的对象,它是表达式的执行环境.作用域有层次结构,这个层次和相应的 DOM 几乎是一样的.作用域能监控表达式和传递事件. 在 HTML 代码中,一旦一个 ng-app 指令被定义,那么一个作用域就产生了,由 ng-app 所生成的作用域比较特殊,它是一个根作用域($rootScope),它是其他所有$Scope 的最顶层. 除了用 ng-app 指令可以产生一个作用域之外,其他的指令如 ng-controller,ng-repeat

  • JavaScript 作用域scope简单汇总

    什么是作用域 程序的执行,离不开作用域,也必须在作用域中才能将代码正确的执行. 所以作用域到底是什么,通俗的说,可以这样理解:作用域就是定义变量的位置,是变量和函数的可访问范围,控制着变量和函数的可见性和生命周期. 而JavaScript中的作用域,在ES6之前和ES6之后,有两种不同的情况. ES6之前,JavaScript作用域有两种:函数作用域和全局作用域. ES6之后,JavaScript新增了块级作用域. 作用域的特性 在JavaScript变量提升的讨论中,我们其实是缺少了一个作用域

  • javascript作用域链(Scope Chain)用法实例解析

    本文实例分析了javascript作用域链(Scope Chain)用法.分享给大家供大家参考,具体如下: 关于js的作用域链,早有耳闻,也曾看过几篇介绍性的博文,但一直都理解的模棱两可.近日又精心翻看了一下<悟透Javascript>这本书,觉得写得太深刻,在"代码的时空"一节里有一段介绍作用域链的地方寥寥数语,回味无穷(其实还是理解的模棱两可^_^).现在整理下自己的读书笔记,顺便借鉴网上资源,写下来. 一.从一个简单的问题说起 下面的js代码在页面中运行显示什么结果:

  • 简单了解JavaScript作用域

    作用域通常是指在指定的时间内,变量存在于一段代码中.缺乏对作用域的理解可能会导致令人沮丧的调试体验.作用域的概念是关于我们的代码中可以访问到哪些确定的函数或变量,代码的上下文和执行环境. 在 JavaScript 中,有两种类型的作用域:全局和局部作用域. 全局作用域 第一种作用域是全局作用域.它很容易定义.如果一个变量或函数是_全局的_,那么在程序中的任何地方都可以访问到它们.在浏览器中,全局作用域是 window对象.如果在函数外面声明一个变量,那么这个变量就存在全局对象中.例如: var

  • JavaScript作用域链使用介绍

    之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时候发现作用域和执行环境确实很重要,又很基础,对理解JavaScript闭包很有帮助,所以在写一篇对作用域和执行环境的理解. 作用域 作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在JavaScript中变量的作用域有全局作用域和局部作用域. 单纯的JavaScript作用域还

  • 关于JavaScript作用域你想知道的一切

    Javacript 中有一系列作用域的概念.对于新的JS的开发人员无法理解这些概念,甚至一些经验丰富的开发者也未必能.这篇文章主要目的帮助理解JavaScript中的一些概念如:scope,closure, this, namespace, function scope, global scope, lexical scope and public/private scope. 希望从这篇文章中能回答如下的问题: 什么是作用域(scope)? 什么是全局(Global)和局部(Local)作用域

  • 深入理解JavaScript作用域和作用域链

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

  • JavaScript 作用域链解析

    JavaScript中有Scope(作用域),Scope chain(作用域链),Execute context(执行上下文),Active Object (活动对象),Dynamic Scope(动态作用域),Closure(闭包)这些概念,要理解这些概念,我们从静态和动态两个方面去分析一下. 首先我们写一个简单的function来做一个例子: 复制代码 代码如下: function add(num1, num2){ var sum = num1 + num2; return sum; } 我

  • JavaScript作用域与作用域链深入解析

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

  • 详解JavaScript作用域和作用域链

    前言 JavaScript 中有一个被称为作用域(Scope)的特性.虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,本文我会尽我所能用最简单的方式来解释作用域和作用域链,希望大家有所收获! 作用域(Scope) 1. 什么是作用域 作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性.换句话说,作用域决定了代码区块中变量和其他资源的可见性.可能这两句话并不好理解,我们先来看个例子: function outFun2() { var inVariable = "内层变量2&

  • JavaScript作用域链实例详解

    本文实例讲述了JavaScript作用域链.分享给大家供大家参考,具体如下: 跟其他语言一样,变量和函数的作用域揭示了这些变量和函数的搜索路径.对于JavaScript而言,理解作用域更加重要,因为在JavaScript中,作用域可以用来确定this的值,并且JavaScript有闭包,闭包是可以访问外部环境的作用域的. 每一个JavaScript的函数都是Function对象的一个实例,Function对象有一个内部属性[[Scope]],这个属性只能被JavaScript的引擎访问.通过[[

随机推荐