【 js 基础 】作用域和闭包
var a = 2;
步骤三:代码生成
将 抽象语法树 (AST)转换为可执行代码。
然而对于解释型语言(例如JavaScript)来说,通过词法分析和语法分析得到语法树,没有生成可执行文件的这一过程,就可以开始解释执行了。
对于 var a = 2; 进行处理的时候,会有 引擎、编译器、还有作用域的参与。
引擎:从头到尾负责整个 Javascript 程序的编译及执行过程。
编译器:负责语法分析及代码生成等。
作用域:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符(变量)的访问权限。
他们是这样合作的:
首先编译器会进行如下处理:
1、var a,编译器会从作用域中寻找是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会自动忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为 a 。
2、接下来编译器会为引擎生成运行时所需的代码,这些代码用来处理 a = 2 这个赋值操作。引擎运行时会首先从作用域中查找 当前作用域集合中是否存在 变量 a。如果有,引擎就会使用这个变量。如果没有,引擎就会继续向上一级作用域集合中查找改变量。
然后 如果引擎最终找到了 变量 a,就赋值 2 给它。如果没有找到,就会抛出一个异常。
总结:
变量的赋值操作分两步完成:第一步 由编译器在作用域中声明一个变量(前提是之前没有声明过),第二步 是在运行时引擎会在作用域中查找该变量,如果可以找到,就对其赋值。
二、作用域
1、RL 查询
在上一部分我们说到了,引擎会对变量 a 进行查找。而查找分为两种,一是 LHS(Left-Hand-Side) 查询,二是 RHS(Right-Hand-Side) 查询。
LHS 查询:试图找到变量的容器本身,从而可以对其赋值。也就是查找 变量 a 。
RHS 查询:查找某个变量的值。查找变量 a 的值,即 2。
例子:
console.log(a); // 这里对 a 是一个 RHS 查询,找到 a 的值,并 console.log 出来。
a = 2; // 这里对 a 是一个 LHS 查询,找到 变量 a,并对其赋值为 2 。
function foo(a){
console.log(a); //
}
foo(2); // 这里首先对 foo() 函数调用,执行 RHS 查询,即找到 foo 函数,然后 执行了 a = 2 的传参赋值,这里首先执行 LHS 查询 找到 a 并赋值为 2,然后 console.log(a) 执行了 RHS 查询。
function foo(a){
var b = a*2;
function bar(c){
console.log(a,b,c);
}
bar(b*3);
}
foo(2); // 2,4,12
在这段代码中有三层作用域,如图所示嵌套:

window.a
foo();
function foo(){
console.log(a);
var a = 2;
}
学了上面的知识,你应该可以猜到 foo() 可以正常执行,而 console.log(a) 会打出 undefined; 原因是当把提升应用到上面代码,代码就相当于 下面的形式:
function foo() {
var a;
console.log(a);
a = 2;
}
foo();
function foo() {
var a;
console.log(a);
a = 2;
}
函数表达式:
var foo = function() {
var a;
console.log(a);
a = 2;
}
(function foo(){
var a;
console.log(a);
a = 2;
})();
那么对于提升,来看个例子:
foo(); // 报TypeError错误
var foo = function() {
var a;
console.log(a);
a = 2;
}
这段代码相当于
var foo;
foo(); // 此时 foo 为 undefined,而我们尝试对它进行函数式调用,属于不合理操作,报 TypeError 错误。
foo = function() {
var a;
console.log(a);
a = 2;
}
foo(); // 1
var foo;
function foo(){
console.log(1);
}
foo = function(){
console.log(2);
}
会输出 1 为不是 2,这段代码提升之后相当于:
function foo(){
console.log(1);
}
foo();
foo = function(){
console.log(2);
}
foo(); //
function foo(){
console.log(1);
}
var foo = function(){
console.log(2)
}
function foo(){
console.log(3)
}
function foo(){
var a = 2;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz(); //
function wait(message){
setTimeout(function timer(){
console.log(message)
},1000)
}
wait("hi");
function foo(){
var a = 2;
function baz(){
console.log(a); //
}
bar(baz);
}
function bar(fn){
fn(); //闭包
}
for (var i=0;i<=5;i++){
setTimeout(function timer(){
console.log(i)
},i*1000)
}
for (var i=0;i<=5;i++){
(function(i){
setTimeout(function timer(){
console.log(i)
},i*1000)
})(i)
}

【 js 基础 】作用域和闭包的更多相关文章
- JS之作用域与闭包
JS之作用域与闭包 作用域在JS中同样也是一个重要的概念.它不复杂,因为ES5中只有全局作用域和函数作用域,我们都知道他没有块级作用域.但在ES6中多了一个let,他可以保证外层块不受内层块的影响 ...
- 解析js中作用域、闭包——从一道经典的面试题开始
如何理解js中的作用域,闭包,私有变量,this对象概念呢? 就从一道经典的面试题开始吧! 题目:创建10个<a>标签,点击时候弹出相应的序号 先思考一下,再打开看看 //先思考一下你会怎 ...
- 你不知道的JS之作用域和闭包 附录
原文:你不知道的js系列 A 动态作用域 动态作用域 是和 JavaScript中的词法作用域 对立的概念. 动态作用域和 JavaScript 中的另外一个机制 (this)很相似. 词法作用域是 ...
- 你不知道的JS之作用域和闭包(五)作用域闭包
原文:你不知道的js系列 一个简单粗暴的定义 闭包就是即使一个函数在它所在的词法作用域外部被执行,这个函数依然可以访问这个作用域. 比如: function foo() { var a = 2; fu ...
- 前端知识体系:JavaScript基础-作用域和闭包-闭包的实现原理和作用以及堆栈溢出和内存泄漏原理和相应解决办法
闭包的实现原理和作用 闭包: 有权访问另一个函数作用域中的变量的函数. 创建闭包的常见方式就是,在一个函数中创建另一个函数. 闭包的作用: 访问函数内部变量.保持函数在环境中一直存在,不会被垃圾回收机 ...
- JS的作用域和闭包
1.作用域 作用域是根据名称找变量的一套规则. 变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对它 ...
- JS(作用域和闭包)
1.对变量提升的理解 1.变量定义(上下文) 2.函数声明 2.说明 this 几种不同的使用场景 常见用法 1.作为构造函数执行 2.作为对象属性执行 3.作为普通函数执行(this === win ...
- 你不知道的JS之作用域和闭包(三)函数 vs. 块级作用域
原文:你不知道的js系列 在第(二)节中提到的,标识符在作用域中声明,这些作用域就像是一个容器,一个嵌套一个,这个嵌套关系是在代码编写时定义的. 那么到底是什么产生了一个新的作用域,只有函数能做到 ...
- js基础知识:闭包,事件处理,原型
闭包:其实就是js代码在执行的时候会创建变量对象的一个作用域链,标识符解析的时候会沿着作用域链一级一级的网上搜索,最后到达全局变量停止.所以某个函数可以访问外层的局部变量和全局变量,但是访问不了里层的 ...
- js中作用域和闭包
作用域链实例 (1) function example() { var age = 23; alert(age) } var age = 25; example(); alert(age); // ...
随机推荐
- SVN将一台服务器上的代码迁移到另一台服务器上
由于我们出差,需要把svn服务器上的代码同步到我电脑上,自己各种百度折腾了快一天才弄好,下面来分享下我的具体思路和操作步骤.有2种方式:第一种方式:直接将本地自己dowm下来的代码导入到本地svn服务 ...
- JavaWeb总结(七)—JSTL标签库
一.JSTL标签库介绍 JSTL标签库的使用时为了弥补HTML标签的不足,规范自定义标签的使用而诞生的.使用JSTL标签的目的是不希望在JSP中出现Java逻辑代码. 二.JSTL标签库的分类 1.核 ...
- java基础之类与对象3
前面我的两篇文章主要介绍了将怎么将事物抽象为对象,以及对象的实例化(就是new一个对象).这篇文章里面我就讲下匿名对象... 还是就举之前的例子把,Car c = new Car();看到这个我们就知 ...
- javascript核心概念之——数组
在javascript中数组就是一个可以存放任何类型的集合.存储在数组中的值用逗号分隔 var arr = ["hello",7,null,undifined,obj,undifi ...
- sql中常见日期获取
获取当前年月日 --获取当前月份 ,GETDATE())) --获取当前月份的下个月 ,GETDATE())) --获取当前月份的上个月 year()获取年 select year(GETDATE() ...
- sql连接查询中on筛选与where筛选的区别
sql查询这个东西, 要说它简单, 可以很简单, 通常情况下只需使用增删查改配合编程语言的逻辑表达能力,就能实现所有功能. 但是增删查改并不能代表sql语句的所有, 完整的sql功能会另人望而生畏. ...
- appium+python做移动端自动化测试
1 导言 1.1 编制目的 该文档为选用Appium作为移动设备原生(Native).混合(Hybrid).移动Web(Mobile Web)应用UI自动化测试的相关自动化测试人员.开发人员等提供 ...
- centos7安装httpd和php
centos7许多命令都变了,又要重新记了. centos7默认安装了httpd吧?记不清了,看一下: rpm -qa |grep httpd 没有的话,安装一下吧. yum -y install h ...
- VueJS实现一个货币结算自定义控件
Vue.component('currency-input', { template: '\ <div>\ <label v-if="label">{{ l ...
- Java中遍历Map的常用方法
以下方法适用于任何map实现(HashMap, TreeMap, LinkedHashMap, Hashtable, 等等): 方式一(推荐): // 推荐 // 在for-each循环中使用entr ...