js——作用域和闭包
1. js是编译语言,但是它不是提前编译,编译结果不能在分布式系统中移植。大部分情况下,js的编译发生在代码执行前的几微秒(甚至更短)
2. 一般的编译步骤
- 分词/词法分析:把字符串分解成词法单元
- 解析/语法分析:将词法单元转换成一个由元素组成的语法结构树,抽象语法树AST
- 代码生成:将AST转换成一组机器指令
3. 三个工具
- 引擎:控制整个程序的编译及执行过程
- 编译器:负责语法分析及代码生成等
- 作用域:收集并维护所有声明的标识符的访问权限
4. var a = 2 的编译过程
| var a=2; | --分解成--> |
词法单元 var a = 2; => var、a、=、2 |
--解析成--> |
树结构 AST |
--代码生成--> 1. var a:询问作用域是否有a。 如果有,则忽略。如果没有,则在当前作用域添加一个声明a 2. a = 2:当前作用域是否有a, 如果有,则赋值。如果没有,则向上一层作用域查找 |
5. 代码生成中查找判断作用域是否存在某个变量的两种查找类型
| LHS | RHS | |
| 直观区别 | 变量在=左侧 | 变量不在=左侧 |
| 操作 | 对变量赋值 | 取变量的值 |
| 找不到? |
1. 严格模式 抛出ReferenceError异常 2. 非严格模式 自动隐式创建一个全局变量 |
抛出ReferenceError异常 |
6. 作用域
- 词法作用域
定义在词法阶段的作用域。也就是在写代码时将变量和块作用域写在哪决定的。函数的作用域完全由声明时的位置决定
- 全局作用域
- 函数作用域:每声明一个函数就会创建一个作用域。在该作用域内声明的变量或函数(标识符)都附属于它,可在整个函数范围内被使用。
- 块作用域
| var |
//变量绑定在所在的函数内 |
| try/catch |
try{
|
| let |
ES6新引入的。将变量绑定在所在任意作用域{}中 在循环中for(let i = 1; i < 5; i++),i在每次迭代中会声明,且每次迭代会用上一个迭代结束时的值来初始化 |
| const | ES6新引入的,将变量绑定在所在任意作用域{}中,且值是固定不可修改的 |
- 运行时修改作用域 eval、with
7. 作用域嵌套
在一个作用域A内创建一个新的作用域B,则B被嵌套在A中
B可以访问A中的标识符。A不可以访问B中的标识符
最外层的作用域是全局作用域
作用域层层嵌套形成作用域链,在访问查找一个标识符时从最内层开始向外查找,一旦找到就停止,因此会出现外层的标识符被内层同名的所屏蔽
8. 闭包
在各个文章中对闭包进行了解释,但是好像有很多说法。我理解得了的一个说法是:
当函数可以记住并访问所在的词法作用域时,就产生了闭包
函数A创建一个作用域A,在A中声明一个函数B(创建了作用域B),把函数B作为结果返回,作用域B会记得自己的作用域链,利用B可以向上层作用域访问
9. 循环和闭包(一个好像特别常见的例子)
for( var i = 1; i <= 5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);}
说明:
var i = 1:定义了一个全局变量
setTimeout():在i秒后执行timer函数。timer是回调函数,在for循环执行完成才开始调用。timer会记住循环的作用域
结果:for执行完成后开始调用timer,以每秒一次的频率输出5次6
期待:每秒一次输出1,2,3,4,5
结果解释:
setTimeout时并没有让timer保存i的副本
timer函数执行时,会去引用i的值,这时只有一个i=6
修改1——立即执行
for(var i = 1; i <=5; i++){
(function(){
setTimeout(function timer(){
console.log(i);
}, i*1000);
})();}
结果:以每秒一次的频率输出5次6
即使是立即执行,最后访问的变量也是全局的i
修改2——立即执行+参数
for(var i = 1; i <=5; i++){
(function(j){
setTimeout(function timer(){
console.log(i);
}, i*1000);
})(i);}
结果:每秒一次输出1,2,3,4,5
修改3——块作用域循环变量
for(let i = 1; i <=5; i++){
setTimeout(function timer(){
console.log(i);
}, i*1000);
}
结果:每秒一次输出1,2,3,4,5
修改4——在循环中添加一个变量var j
for(var i = 1; i <=5; i++){
var j = i;
setTimeout(function timer(){
console.log(j);
}, j*1000);
}
结果:以每秒一次的频率输出5次5(j和i一样为全局变量,j=5)
修改5——块作用域变量
for(var i = 1; i <=5; i++){
let j = i;
setTimeout(function timer(){
console.log(j);
}, j*1000);
}
结果:每秒一次输出1,2,3,4,5
参考
1. 《你不知道的javascript》上卷
2. 还看了很多网上的说明,就不一一列举了,因为没记住具体哪些了
js——作用域和闭包的更多相关文章
- JS作用域与闭包
JS作用域与闭包 在JavaScript中,作用域是可访问变量,对象,函数的集合. 变量分为全局变量和局部变量.全局变量在函数外定义,HTML中全局变量是window对象,所有数据对象都属于windo ...
- js作用域及闭包
作用域 执行环境是js最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为. 1.全局执行环境就是最外围的一个执行环境,每一个函数都有自己的作用域 2.简单的说局部作用 ...
- JS作用域与闭包--实例
<script> "use strict" //函数作用域 function func(){ var arr = [1,3,5,7,9]; var sum = 0; f ...
- 浅谈JS作用域和闭包
函数表达式和函数声明 变量/函数声明都会提前 console.log(a) let a =1 那么打印出来的a为 undefined,因为会将a提到前面并赋予默认值undefined 函数声明:函数声 ...
- js闭包的作用域以及闭包案列的介绍:
转载▼ 标签: it js闭包的作用域以及闭包案列的介绍: 首先我们根据前面的介绍来分析js闭包有什么作用,他会给我们编程带来什么好处? 闭包是为了更方便我们在处理js函数的时候会遇到以下的几 ...
- JS教程:词法作用域和闭包 (网络资源)
varclassA = function(){ ; } classA.prototype.func1 = function(){ var that = this, ; function a(){ re ...
- 原来JS是这样的 - 提升, 作用域 与 闭包
引子 长久以来一直都没有专门学过 JS ,因为之前有自己啃过 C++ ,又打过一段时间的算法竞赛(写得一手好意大利面条),于是自己折腾自己的网站的时候,一直都把 JS 当 C 写.但写的时候总会遇到一 ...
- js面试题知识点全解(一作用域和闭包)
问题: 1.说一下对变量提升的理解 2.说明this几种不同的使用场景 3.如何理解作用域 4.实际开发中闭包的应用 知识点: js没有块级作用域只有函数和全局作用域,如下代码: if(true){ ...
- 解析js中作用域、闭包——从一道经典的面试题开始
如何理解js中的作用域,闭包,私有变量,this对象概念呢? 就从一道经典的面试题开始吧! 题目:创建10个<a>标签,点击时候弹出相应的序号 先思考一下,再打开看看 //先思考一下你会怎 ...
随机推荐
- Creating A Moddable Unity Game
前言: 对游戏进行修改与拓展(MOD)是我一直以来感兴趣的东西,我的程序生涯,也是因为在初中接触到GBA口袋妖怪改版开始的,改过也研究过一些游戏的MOD实现方式,早就想在自己的游戏中实现“MOD系统” ...
- CF 1060E. Sergey and Subway
题目链接 题意:给你一棵树,然后连接两个有公共邻居的点,问你连完后,任意两点的距离之和. 一开始看这种题,还不怎么会做,借鉴了这位大佬的博客,get到了新技能,当我们求树上任意俩点的距离之时,可以转化 ...
- linux系统下python升级安装
1.安装gcc gcc-c++ yum install -y gcc gcc-c++ #提前检查是否安装 2.下载python3.5.2安装包 cd /usr/local/src/ wget http ...
- 《shiro》视频目录---1、权限管理-shiro
\day01_shiro\0323\10realm支持散列.avi;\day01_shiro\0323\1权限管理原理.avi;\day01_shiro\0323\2权限管理解决方案.avi;\day ...
- 关于vue2.0 cnpm 镜像安装
前言:这几天安装vue环境一直报错,愁死我了,踩了好多坑,以前学的时候也安装成功了,后来很忙然后一直没顾上学vue,这么最近要学这个 在安装环境中平凡报错特此记笔记如下: 1.首先去下载node.js ...
- 【转载】奇异值分解(SVD)计算过程示例
原文链接:奇异值分解(SVD)的计算方法 奇异值分解是线性代数中一种重要的矩阵分解方法,这篇文章通过一个具体的例子来说明如何对一个矩阵A进行奇异值分解. 首先,对于一个m*n的矩阵,如果存在正交矩阵U ...
- python,类和对象(一)
万物皆对象,在python中也存在对象,首先我们需要知道面向对象的三大特征封装.继承.多态. 封装就是将一种或多种杂乱无序的代码进行有序的分类封装. 继承可以理解为孩子会继承父亲所有的东西. 多态可以 ...
- centos U盘挂载问题
查看u盘路径 fdisk -l Disk /dev/sda: 16.2 GB, 16236150784 bytes, 31711232 sectors Units = sectors of 1 * 5 ...
- P5239 回忆京都
题目地址:P5239 回忆京都 杨辉三角即组合数的"打表"形式 再求一个二维前缀和 然后处理一下负数即可(因为在求前缀和的过程中有减法) #include <bits/std ...
- tomcat目录映射
环境:CentOS 6 + tomcat 7 + jdk 7 目前使用2种方法: 1.tomcat/conf/server.xml <Host name="localhost" ...