先上三段说明作用域的代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//==========例1==========
  
var scope='global';
function fn(){
  alert(scope);
  var scope='local';
  alert(scope);
}
fn();    //输出结果?
alert(scope);//输出结果?
  
//===========例2==========
  
var scope='global';
function fn(){
  alert(scope);
  scope='local';
  alert(scope);
}
fn();    //输出结果?
alert(scope);//输出结果?
  
//===========例3=========
  
var scope='global';
function fn(scope){
  alert(scope);
  scope='local';
  alert(scope);
}
fn();    //输出结果?
alert(scope);//输出结果?

这三段代码只有小许差异,但结果缺截然不同,例1分别输出[undefined , local , global],例2分别输出[global , local , local],例3结果输出[undefined , local , global],如果不能答对说明你对javascript的作用域特性还未理解透彻。

什么是作用域?

也许有人会问:变量a的作用域是什么?一会儿又问:函数a的作用域是什么?变量和函数的作用域分别是啥玩意?

我们先来看看“作用域”是什么意思,“作用域”拆开来就是“作用”和“域”

  • 作用:访问、操作、调用……
  • 域:区域、范围、空间……

作用域就是变量和函数的可访问范围,或者说变量或函数起作用的区域。

1.javascript函数的作用域:

函数内的区域,就是这个函数的作用域,变量和函数在这个区域都可以访问操作。最外层函数外的区域叫全局作用域,函数内的区域叫局部作用域。

2.javascript变量的作用域:

在源代码中变量所在的区域,就是这个变量的作用域,变量在这个区域内可以被访问操作。在全局作用域上定义的变量叫全局变量,在函数内定义的变量叫局部变量。

简单地理解,JS源代码被函数{ }划分成一块一块的区域,这些区域换个身份就是某函数或某变量的作用域,变量的作用域和函数的作用域在源代码中有可能指的是同一块区域。

作用域链
作用域链(Scope Chain)是javascript内部中一种变量、函数查找机制,它决定了变量和函数的作用范围,即作用域,理解作用域链的作用原理,上一篇文章的三个例子也就能理解了,从而知其然也知其所以然。

作用域链是ECMAScript-262说明文档中的概念,javascript引擎是按ECMAScript-262说明文档去实现的,了解javascript引擎的工作原理有利于我们理解javascript的特性,但绝大多数js程序员不会去了解非常底层的技术,所以阅读ECMAScript-262说明文档,我们可以有一个直观的方式去模拟javascript引擎的工作原理。

本文将通过1999年的ECMAScript-262-3th第三版来说明作用域链的形成原理,将会介绍执行环境,变量对象和活动对象,arguments对象,作用域链等几个概念。2009年发布了ECMAScript-262-5th第五版,不同的是取消了变量对象和活动对象等概念,引入了词法环境(Lexical Environments)、环境记录(EnviromentRecord)等新的概念,所以两个版本的概念不要混淆了。

1.执行环境(Execution Contexts)

执行环境(Execution Contexts)也被翻译为执行上下文,当解析器进入ECMAScript的可执行代码,解析器就进入一个执行环境,活动的执行环境组成一个逻辑上的栈,在这个逻辑栈顶部的执行环境是当前运行的执行环境。

注:ECMAScript中有三种可执行代码,Global、Function和Eval,全局环境即是Global可执行代码,函数即是Function可执行代码。逻辑栈是一种特殊的数据存储格式,特点是‘先进后出,后进先出',添加数据会先压入逻辑栈顶部,删除数据必须先从顶部开始删除。

变量对象(Variable Object)、活动对象(Activation Object)和Arguments对象(Arguments Object)

每个执行环境都有一个与之关联的变量对象,当解析器进入执行环境时,就会创建一个变量对象,变量对象保存着在当前执行环境中声明的变量和函数的引用。

变量对象是一个抽象的概念,在不同的执行环境中,变量对象有不同的身份,在解析器进入任何执行环境之前,就已经创建了一个Global对象,当解析器进入全局执行环境时,Global对象就充当变量对象,当解析器进入一个函数时,就会创建一个活动对象充当变量对象。

2.解析器处理代码时的两个阶段

我们都知道javascript解析器是一段一段解析处理代码的,为毛?这就要涉及解析器处理代码时的两个阶段,解析代码和执行代码。

当解析器进入执行环境时,变量对象就会添加执行环境中声明的变量和函数作为它的属性,这就意味着变量和函数在声明之前已经可用,变量值为undefined,这就是变量和函数声明提升(Hoisting)的原因,与此同时作用域链和this确定,此过程为解析阶段,俗称预解析。接着解析器开始执行代码,为变量添加相应值的引用,得到执行结果,此过程为执行阶段。

举两个好吃的栗子:

?
1
2
3
4
5
var a=123;
var b="abc";
function c(){
  alert('11');
}

上述全局环境中的代码解析执行后,会将Global对象作为变量对象,保存以下数据。

?
1
2
3
4
5
6
7
8
function testFn(a){
  var b="123";
  function c(){
    alert("abc");
  }
}
  
testFn(10);

当解析器进入函数执行环境时,则会创建一个活动对象作为变量对象,活动对象还会创建一个Arguments对象,arguments对象是一个参数集合,用来保存参数,这就是我们写函数时可以使用arguments[0]等来使用参数的原因。

3.作用域链(Scope Chain)

每个执行环境都有一个与之关联的作用域链,当解析器进入执行环境时被定义,作用域链是一个对象列表,用来检索各个变量对象中的变量和函数,这样可以保证执行环境有权访问哪些变量和函数,举个栗子。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
var a='123';
function testFn(b){
  var c='abc';
  
  function testFn2(){
    var d='efg';
    alert(a);
  }
  
  testFn2();
}
  
testFn(10);

testFn2内未声明变量a,为什么testFn2能调用全局变量a?整个过程是怎么发生的呢?请看下图。

当解析器进入全局执行环境时,调用变量和函数时只在Global对象中查找。

当解析器进入testFn函数执行环境时,函数内部属性[[scope]]中首先填入Global对象,然后将testFn活动对象添加到Global对象之前,形成一个作用域链。

当解析器进入testFn2函数执行环境时,函数内部属性[[scope]]首先填入父级的作用域链,然后再将当前的testFn2活动对象添加到作用域链的前端,形成一个新的作用域链。

testFn2调用变量a时,首先在当前的testFn2活动对象中查找,如果没有找到就顺着作用域链向上,在testFn活动对象中查找变量a,如果没有找到再顺着作用域链向上查找,直到在最后Global对象中找到为止,否则报错。所以函数内部可以调用外部环境的变量,外部环境不能调用函数内部的变量,这就是作用域特性的原理。

WEB前端学习二 JS作用域和作用域链相关推荐

  1. 深圳Web前端学习:js中的模块化--【千锋】

    深圳Web前端学习:js中的模块化–[千锋] 0.前言 我们知道最常见的模块化方案有CommonJS.AMD.CMD.ES6,AMD规范一般用于浏览器,异步的,因为模块加载是异步的,js解释是同步的, ...

  2. Web 前端学习 之js概述

    Web 前端学习 之js概述 1.JavaScript 介绍 Javascript 语言诞生主要是完成页面的数据验证.因此它运行在客户端,需要运行浏览器来解析执行JavaScript 代码. JS 是 ...

  3. WEB前端学习五 js什么是引用类型

    js中的数据类型有以下几种: Number   Boolean   undefined     Object   Function    String  Null 基本类型:Number Boolea ...

  4. WEB前端学习一 JS预解释

    javascript 的预解析:(个人理解)就是js代码在执行之前 会在相应的执行环境中 预先把 一些东西解析到内存.如果理解错误,请多多指正 一. 那究竟预先解析哪些东西那??答:预先解析 func ...

  5. WEB前端学习六 js什么是闭包

  6. WEB前端学习四 js什么是原始类型

    原始值与引用值 在ECMAScript 中,变量可以存放两种类型的值,即原始值和引用值. 原始值(primitive value)是存放在栈(stack)中的简单数据字段,也就是说,它们的值直接存储在 ...

  7. web前端学习笔记(最新)

    web前端学习笔记 大家好,我是链表哥,新的学期,新的学习,我会为您展示我的学习进程. 一:什么是WEB前端? 所谓的Web前端指的是用户所能接触到的,并服务于用户的前沿端口,经我们程序员编辑修饰后展 ...

  8. js怎么在一个div中嵌入另一网站_好程序员web前端学习路线分享HTML5常见面试题集锦一...

    好程序员web前端学习路线分享HTML5常见面试题集锦,接下来将会持续为大家分享几篇HTML5常见面试题. 1.布局 左边20% 中间自适应 右边200px 不能用定位 答案:圣杯布局/双飞翼布局或者 ...

  9. web前端学习基础教程,简单的图片旋转木马自动轮播js代码

    一款简单的图片旋转木马自动轮播js代码,图片叠加轮播切换效果,支持点击左右箭头按钮控制切换. 案例效果图 案例源码: <!DOCTYPE html> <html lang=" ...

最新文章

  1. 烦人,周报要不要取消?| 每日趣闻
  2. java事务过大影响系统性能吗_Java编程性能优化-影响性能的因素你都知道吗?
  3. centos下svn与mysql_centos下SVN搭建
  4. docker pull 从仓库拉取镜像
  5. 最新开发android版本,Android版本检测升级
  6. linux 秒数转时间格式,通过delphi将秒数转换成日期格式
  7. MS SQLSERVER 各种乱七八糟
  8. 三级分类菜单的数据库设计
  9. Qt工作笔记-QMainWindow自定义窗体中利用状态栏进行窗体放缩
  10. 深圳蛇口人民医院办理健康证,免费体检,2017年后国家承担体检费用
  11. C++编译器与链接器工作原理
  12. php与ununtu通信,Ubuntu 20.04 LTS 已引入 PHP 7.4
  13. 最新win10镜像下载
  14. java成员变量注释规范_java编程规范之java注释规范
  15. Java List集合排序 Java8 List集合排序方法 Java Lambda集合排序
  16. js二次压缩工具nodejs版
  17. 网络空间同样需要“天朗气清”
  18. 二维码在企业移动中的应用
  19. 文科类文献综述怎么写?
  20. 基于python的在线音乐系统设计与实现

热门文章

  1. 输入正方形对角线两个端点坐标,求中点坐标
  2. Spark Streaming 实战案例(二) Transformation操作
  3. Spark与Mysql(JdbcRDD)整合开发
  4. Golang 使用Protocol Buffer 案例
  5. Elasticsearch filter和query的不同
  6. Java JDK代理、CGLIB、AspectJ代理分析比较
  7. docker-compose初试及命令基础
  8. SpringBoot基础篇AOP之基本使用姿势小结
  9. python tkinter chk
  10. Spring Boot集成JPA的Column注解命名字段无效的问题