js之预解析

在谈js的预解析之前,先看一段c++程序

#include <iostream>
using namespace std;void useGreet(){greet();
}void greet(){cout << "hello" <<endl;
}int main() {useGreet();return 0;
}
复制代码

只要是有c++基础的都会知道以上程序是无法通过编译的,因为useGreet()函数中调用了一个未声明的函数,即便greet()函数在其声明。这是c++编译器编译顺序本身决定的。 再来看用js写的一段代码

function useGreet(){greet();
}function greet(){console.log('hello');
}useGreet();
复制代码

以上代码是可以正常运行的

以上两个例子虽然与js的预解析的关系不是很大,但是也反映了js中预解析的内容

再来看这段js代码

let a = 1;
let fun = function(){console.log(a);let a = 2;console.log(a);
}
fun();
复制代码

代码的运行结果为

undefined
2
复制代码

再来看下面这段代码

let a = 1;
let fun = function(){console.log(a);a = 2;console.log(a);
}
fun();
复制代码

代码的运行结果为

1
2
复制代码

两段看似差异十分微小的代码的运行结果是天差地别的。在解释运行结果之前,我们需要知道js中函数和变量在内存中的存储模型,js的预解析,js的作用域链。

函数和变量在内存中的存储模型

js的数据分为两类

  • 基本类型
  • 引用类型(对象类型) 其中基本类型是存储于栈内存中,而对象是存储于堆内存中。
var person = {name: "Jack",sex: "man"
}
复制代码

上例中先声明了一个变量person,然后对其进行赋值。其中person的值{name: "Jack", sex: "man"}存储于堆内存,而person这个字面量存储了一个地址,这个地址即为person的值在堆内存中的存储地址

预解析

  js的预解析是指,在当前作用域中,JavaScript代码执行之前,浏览器首先会默认的把所有带var和function声明的变量进行提前的声明或者定义。即先执行变量的声明和函数的声明与赋值的语句,然后在按顺序执行其他语句。注意,变量和函数的预解析是不同的,变量的预解析只进行声明而不执行赋值,而函数的预解析声明与赋值都会执行。

例如

fun();
function fun(){console.log("hello");
}
复制代码

以上代码可以正常输出hello,其执行顺序为,先对 fun()函数进行声明和赋值,再执行fun()的调用

再看下面一段代码

fun(str);
function fun(str){console.log(str);
}
let str = "hello";
复制代码

以上代码可以的输出结果为undefined,其执行顺序为,先对 fun()函数进行声明和赋值,在进行str的声明,然后执行fun()函数的调用,然后执行str的赋值,因为在赋值之前就使用了str,所以输出了str的初始值undefined

作用域链

  作用域链为: 当在某个作用域要使用某个变量时,首先查找本作用域是否有该变量,若有停止查找并取得该变量,若没有再查找本作用域的上级作用域,重复以上过程,直到找到该变量为止。若到全局作用都没有找到则报错。

现在就可以对最开始的两段代码的运行结果做出解释

第一段

let a = 1;
let fun = function(){console.log(a);let a = 2;console.log(a);
}
fun();
复制代码

这段代码有两个作用域,一个全局作用域,还有一个fun()匿名函数作用域

先执行全局作用域的代码

let a
=> let fun = function
=> fun()
=> a=1
复制代码

再执行函数作用域的代码,执行fun()函数调用的执行过程

let a // 预解析
=> console.log(a)
=> a=2
=> console.log(a)
复制代码

第二段

let a = 1;
let fun = function(){console.log(a);let a = 2;console.log(a);
}
fun();
复制代码

这段代码也有两个作用域,一个全局作用域,还有一个fun()匿名函数作用域,唯一不同的是匿名函数作用域内a没有let或var声明

全局作用域的代码的执行过程与第一段是一样的

let a
=> let fun = function
=> fun()
=> a=1
复制代码

但fun()函数调用的执行过程就不同了

console.log(a)
=> a=2
=> console.log(a)
复制代码

fun()的作用域内没有任何变量或者函数的声明,所以fun()作用域内没有预解析过程,第一句执行console.log(a),由于afun()作用域内没有定义,所以到fun()作用域的上一级作用域中找,上一级中a已经声明并且赋值为1,所以先输出1;在执行a=2,对a赋值为2,再执行console.log(a),此时a = 2,所以输出2

转载于:https://juejin.im/post/5cf9cb2df265da1bc8541a5c

javascript之作用域与预解析相关推荐

  1. js变量提升_学习笔记:JS中的作用域和预解析

    知识总结:谢静贤.汤昊 在javascript中作用域是非常重要的,本文将会说明作用域以及我们在工作,以及面试中的一些面试题,如果有不足的地方希望大家可以评论指出来,自己一定会及时的改正错误,避免大家 ...

  2. this指向-作用域、作用域链-预解析 变量提升-Vue组件传值 父子 子父 非父子-Vue数据双向绑定原理

    目录 this指向 作用域.作用域链 预解析 变量提升 Vue组件传值 父子 子父 非父子 Vue数据双向绑定原理 1.this指向 函数的this指向 看调用.不看声明 (1)普通函数调用 ①函数名 ...

  3. JavaScript匿名函数、作用域、预解析

    匿名函数: 没有名字的函数/定义时未直接命名的函数. 优点: 非匿名函数在定义时,已经创建了函数对象和作用域对象:所以,即使没有调用也会占用内存空间:但是匿名函数仅在调用时,才临时创建函数对象和作用域 ...

  4. 重学前端之(4)函数、作用域、预解析

    函数 为什么要有函数? 在写代码的时候,有一些常用的代码需要书写多次,如果直接复制粘贴的话,会造成大量的冗余代码. 如果修改呢?? 多个页面呢?? 函数可以封装一段重复的JavaScript代码,它只 ...

  5. 函数(定义、参数、return、变量、作用域、预解析)

    一.函数定义 1.方式一       function 函数名(参数){  函数体  }------函数声明的方法 function fn(a){console.log(a);}: 2.方式二    ...

  6. JS函数声明和预解析的理解

    JS函数声明方法 今天看到了一个自己关注了的大神给我回了私信,觉得自己仿佛摸到了大神的裤腿,哈哈,而且人还特别好,居然会给小菜鸟回私信,特别开心呀,一个菜鸟的小激动,言归正传啦 1.最为常见的函数声明 ...

  7. JS基础 -- 大复习(阶段六:对象和内置对象及预解析)

    1.概念 现实生活:对象就是东西,是一个具体的事物.万物皆对象 程序角度: 对象是一组无序的数据的集合.包含属性与方法 2.对象的创建 方式1:字面量 var person = {      name ...

  8. 前端学习记录27-JavaScript-(作用域、预解析、自定义对象)

    前端学习记录27-JavaScript-作用域.预解析.自定义对象 作用域 全局作用域 局部作用域(函数作用域) 变量作用域 全局变量 局部变量 作用域链 预解析 变量预解析 函数预解析 预解析经典面 ...

  9. JavaScript中的作用域、作用域链、预解析

    作用域: /* 变量--->局部变量和全局变量 * 作用域:就是变量的适用范围 * 局部作用域和全局作用域 * js中没有快级作用域---一对括号中定义的变量,这个变量可以在大括号外面使用 * ...

最新文章

  1. python常用数据结构的常用操作
  2. 宝塔mysql优化_宝塔面板下实现MySQL性能优化处理
  3. FD.io/VPP — Overview
  4. 为博客园博文添加目录的两种方法
  5. can bus 中spn是什么_CP AUTOSAR功能栈简介NM网络管理(Can)
  6. 风险预测模型_随访78个月,仁济医院完成世界首个间质性肺病风险预测模型
  7. 程序猿果真有前端后端client吗
  8. NGINX内部:我们如何设计性能和规模
  9. UFLDL 教程学习笔记(二)反向传导算法
  10. Git:git合并分支
  11. 3-unit4 postfix+mysql
  12. linux Rootkit检查
  13. 海康linux 密码忘记,linux下调用海康sdk
  14. 改变elementui卡片crad样式_Ueditor文字和echarts图片 生成 word 前端解决方案
  15. Java编程思想之对象入门
  16. win10系统磁盘进行再分区的方法
  17. 志强:经过测试有效的四种引流方法
  18. FTPS、SFTP和FTP的区别与联系
  19. 【PC】【MTU】PC查看和修改MTU的方法小结
  20. c语言写plc程序正反转,西门子PLC控制电机正反转编程实例!

热门文章

  1. Maven初探-如何快速入手一个项目
  2. redis成长之路——(一)
  3. 巧用row_number和partition by分组取top数据
  4. Linux远程拷贝下载文件
  5. 计算机仿真技术与应用,[计算机软件及应用]计算机仿真技术与应用简介.pdf
  6. iphone微信美颜插件_Cydia插件推荐
  7. Kali Linux 2020.1修改root用户密码
  8. SQLite中的SELECT子句使用别名
  9. UGUI的优点新UI系统二 直观、易于使用
  10. OUYA游戏开发核心技术剖析大学霸内部资料