【 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); // ...
随机推荐
- Struts2基础学习(六)—文件的上传和下载
一.文件的上传 1.单个文件上传 Struts2使用拦截器完成了文件的上传,而且底层使用的也是FileUpload开源组件. 客户端注意事项: (1)method="post&qu ...
- ios坐标位置转换
//ios常用坐标转换来处理一些下拉框队形的按钮的位置,我以最下面两个来进行一下个人的理解,不足之处多多见谅 - (CGPoint)convertPoint:(CGPoint)point toView ...
- VS窗体选择BackGroupImage属性报错:已添加具有相同键的项
高墙我今天第一次遇见这个问题.既然说是"已添加具有相同键的项."那我自然地认为会不会是文件夹哪里命名了两个相同的文件名.然后在这个Exception上越走越远. 好了不说废话.出现 ...
- ios 获取当前设备信息、内存
//在[UIDevice currentDevice]中的属性 @property(nonatomic,readonly,strong) NSString *name; // e.g. "M ...
- ZOJ 3195 Design the city 题解
这个题目大意是: 有N个城市,编号为0~N-1,给定N-1条无向带权边,Q个询问,每个询问求三个城市连起来的最小权值. 多组数据 每组数据 1 < N < 50000 1 < Q ...
- Unity属性的封装、继承、方法隐藏
(一)Unity属性封装.继承.方法隐藏的学习和总结 一.属性的封装 1.属性封装的定义:通过对属性的读和写来保护类中的域. 2.格式例子: private string departname; // ...
- std::thread使用
本文将从以下三个部分介绍C++11标准中的thread类,本文主要内容为: 启动新线程 等待线程与分离线程 线程唯一标识符 1.启动线程 线程再std::threada对象创建时启动.最简单的情况下, ...
- AspNetCore-MVC实战系列(二)之通过绑定邮箱找回密码
AspNetCore - MVC实战系列目录 . 爱留图网站诞生 . AspNetCore - MVC实战系列(一)之Sqlserver表映射实体模型 . AspNetCore-MVC实战系列(二)之 ...
- C#中在比较自定义对象的时候要重写Equals方法
using System;using System.Collections.Generic;using System.Text; namespace Equal{ using System; c ...
- 【Netty】第一个Netty应用
一.前言 前面已经学习完了Java NIO的内容,接着来学习Netty,本篇将通过一个简单的应用来了解Netty的使用. 二.Netty应用 2.1 服务端客户端框架图 下图展示了Netty中服务端与 ...