JavaScript解析机制之变量提升
1、什么是预解析?
在当前作用域下,JS 运行之前,会把带有 var 和 function 关键字的事先声明,并在内存中安排好。(这个过程也可以理解为变量提升)然后再从上到下执行 JS 语句(预解析只会发生在通过 var 定义的变量和 function 上)
2、var 声明的变量
使用 var 声明的变量预解析:告诉解析器知道有这个名字的存在并默认将该变量赋值 undefined ,如下:
console.log(x); //undefined
var x = 5;
变量 x 虽然是在 console.log 后面定义的,但使用 var 申明的 x 会提前保存在内存中,并赋值 undefined ,然后再从上往下执行 JS 语句 。它的执行顺序类似于下面的结构:
var x;
console.log(x); //undefined
x = 5;
先声明了 x,x 没有定义所以赋值为 undefined ,输出的结果自然为 undefined,然后再给 x 赋值为 5
需注意的是,如果变量声明没有使用 var,则不存在变量提升。如下:
console.log(x); //error: x is not defined
x = 5;
x 没有使用 var 声明,所以报错找不到 x
3、functin 声明的函数
使用 function 声明函数的预解析:先告诉解析器这个函数名的存在,然后在告诉解析器这个函数名的函数体是什么,如下:
console.log(f);
function f() {
console.log("123");
}
声明函数会把整个函数都提升到最前面 ,所以浏览器中结果会输出整个函数,结果如下:
function f() {
console.log("123");
}
如果在一个函数作用域中声明一个变量 ,那么它也会提升到函数作用域的最上面,如下:
var x = 5;
function f() {
console.log(x); // undefined
var x = 2;
}
f();
以上虽然全局作用域声明了一个变量 x,但函数里也声明了一个变量 x,所以会先查找函数里面是否有变量 x,如果有就不会在全局下查找了。函数里面的变量 x 会被提升到函数作用域的最前面 ,并且赋值为 undefined,所以输出结果为 undefined ,类似于如下结构:
var x = 5;
function f() {
var x;
console.log(x); // undefined
x = 2;
}
f();
函数的参数也可以理解为函数作用域的变量 ,如下:
var x = 5;
function f(x) {
console.log(x); // undefined
}
f();
console.log(x); //
为函数 f 传递一个形参 x,由于函数在调用时没有传递实参(也就是说变量 x 没有赋值),所以为 undefined 。而在全局下输出 x 自然在全局下查找变量 x ,结果为 5
4、函数优先
变量声明和函数声明都会被提升,如果同一个作用域下声明的两个相同变量或相同函数,后一个会覆盖前一个,如下:
var x = 5;
var x = 10;
console.log(x); //
function f() {
console.log("xx");
}
function f() {
console.log("yy");
}
f(); // yy
但有一个需要注意的细节,如果声明的变量与函数名相同 ,那又会怎么覆盖呢?可以看如下例子:
var f = 5;
function f() {
console.log("xx");
}
f(); // error: f is not a function
JavaScript 中,函数的预解析优先级是要高于变量的预解析的。无论函数在什么位置声明,都优选把整个函数提升到最前面。所以上面的例子中,虽然函数 f 是在变量 f 下面定义的,但是在预解析时先解析函数 f,然后再解析变量 f,后面的变量 f 会把前面的函数 f 覆盖,最后 f 为 5 为数值类型,所以调用 f 时报错,f 不是一个函数。
需要注意的是 ,如果变量 f 定义后没有赋值 ,那么函数 f 就不会被覆盖了,如下:
var f;
function f() {
console.log("xx");
}
f(); // xx
所以函数提升优先于变量提升,函数提升会把整个函数挪到作用域顶部,变量提升只会把声明挪到作用域顶部
掌握以上知识,我们看下面的例子 :
console.log(x); // function x() {console.log(5);}
var x = 2;
console.log(x); //
function x() {
console.log(3);
}
console.log(x); //
var x = 3;
console.log(x); //
function x() {
console.log(5);
}
console.log(x); //
x(); // error: x is not a function
以上例子,两个函数 x 优先提升,所以第二个函数 x 覆盖第一个函数 x,然后两个变量 x 提升,由于变量 x 提升后为 undefined,所以第二个函数没有被覆盖,第一个输出 x 结果为第二个函数 function x(){console.log(5);}
随后 x 被赋值为 2 ,所以第二个输出 x 结果为 2
因为第一个函数 x 已经被提升到前面去了,所以第三个输出 x 结果还是 2
随后为 x 赋值为 3,所以第四,第五输出 x 结果为 3。最后调用 x,x 因为是数值类型,所以会报错 x 不是一个函数
我的博客也会同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=e1ebv5s48l8j
JavaScript解析机制之变量提升的更多相关文章
- javascript解析机制——预解析
JavaScript解析机制是什么? JavaScript解析过程分为两个阶段,一个是编译阶段,另外一个就是执行阶段. * 编译阶段 编译阶段就是我们常说的JavaScript预解析( ...
- 轻松搞定javascript变量(闭包,预解析机制,变量在内存的分配 )
变量: 存储数据的容器 1.声明 var 2.作用域 全局变量. 局部变量. 闭包(相对的全局变量): 3.类型 a.基本类型(undefi ...
- JavaScript系列文章:变量提升和函数提升
第一篇文章中提到了变量的提升,所以今天就来介绍一下变量提升和函数提升.这个知识点可谓是老生常谈了,不过其中有些细节方面博主很想借此机会,好好总结一下. 今天主要介绍以下几点: 1. 变量提升 2. 函 ...
- JavaScript中函数的变量提升问题
函数的大体分三种,一种是函数的声明,一种是函数表达式(又称为函数的字面量) 1.函数的声明 => function myFn(){}; 2.函数的表达式 => var myFn = fun ...
- JavaScript 预编译(变量提升和函数提升的原理)
本文部分内容转自https://www.cnblogs.com/CBDoctor/p/3745246.html 1.变量提升 console.log(global); // undefined var ...
- javascript解析机制、闭包详解
js解析机制: js代码解析之前会创建一个如下的词法环境对象(仓库):LexicalEnvironment{ } 在扫描js代码时会把: 1.用声明的方式创建的函数的名字: 2.用var定义的变量的名 ...
- JavaScript解析机制与闭包原理实例详解
js代码解析机制: js代码解析之前会创建一个如下的词法环境对象(仓库):LexicalEnvironment{ } 在扫描js代码时会把: 1.用声明的方式创建的函数的名字; 2.用var定义的变量 ...
- JavaScript中的各种变量提升(Hoisting)
首先纠正下,文章标题里的 “变量提升” 名词是随大流叫法,“变量提升” 改为 “标识符提升” 更准确.因为变量一般指使用 var 声明的标识符,JS 里使用 function 声明的标识符也存在提升( ...
- JavaScript解析顺序和变量作用域
JavaScript基础之变量作用域. 一. 1.全局变量:全局变量的意思就是,在代码的不论什么地方都能够訪问到.注意:未定义 直接赋值的变量拥有全局属性. 2.局部变量:局部变量的意思就是,变量的作 ...
随机推荐
- Android MVP框架实现登录案例
一.Model package com.czhappy.mvpdemo.model; /** * author: chenzheng * created on: 2019/5/16 11:06 * d ...
- iOS:Xcode代码块,提升敲代码的效率
一.代码块在哪里? 看下图 或者 快捷键:command+shift+L 长这样: 二.如何创建代码块: 1.先选中要创建的代码片段,然后点击右键,选中 Create Code Snippet 然后会 ...
- IP通信学习心得02
1.7层模型 2.OSI的应用 3.如何辨别.数清冲突域和广播域 1)首先,须知第一层不能隔离冲突域和广播域.例如集线器或者直接连PC 2)其次,第二层可以隔离冲突域,但不能隔离广播域.例如,二层交换 ...
- Jenkins运行python脚本出现 configparser.NoSectionError: No section: 'XXXXXX'
原来的代码如下: def get_test_config(tag, key, config="config.ini"): cf = configparser.ConfigParse ...
- Docker部署ELK 7.0.1集群之Logstash安装介绍
1.下载镜像 [root@vanje-dev01 ~]# docker pull logstash: 2.安装部署 2.1 创建宿主映射目录 [root@vanje-dev01 ~]# mkdir ...
- Linux中的13个基本Cat命令示例
cat(“ concatenate ”的缩写)命令是Linux / Unix等操作系统中最常用的命令之一.cat命令允许我们创建单个或多个文件,查看文件包含,连接文件以及在终端或文件中重定向输出.在本 ...
- 40 多线程(十二)——ReentrantLock 可重入锁
我们使用的synchronized加的锁是可以延续使用的,如下: public void test() { //第一次获得锁 synchronized(this) { while(true) { // ...
- 【题解】Luogu P4910 帕秋莉的手环
原题传送门 "连续的两个中至少有1个金的"珂以理解为"不能有两个木相连" 我们考虑一个一个将元素加入手环 设f\([i][0/1]\)表示长度为\(i\)手环末 ...
- Nginx惊群问题
Nginx惊群问题 "惊群"概念 所谓惊群,可以用一个简单的比喻来说明: 一群等待食物的鸽子,当饲养员扔下一粒谷物时,所有鸽子都会去争抢,但只有少数的鸽子能够抢到食物, 大部分鸽子 ...
- WPF 不要给 Window 类设置变换矩阵(分析篇):System.InvalidOperationException: 转换不可逆。
原文:WPF 不要给 Window 类设置变换矩阵(分析篇):System.InvalidOperationException: 转换不可逆. 最近总是收到一个异常 "System.Inva ...