JavaScript专题之一:变量提升与预编译

JavaScript专题之一:变量提升与预编译

目录

前言一、有趣的现象二、Js的预解析三、提升之间的优先级四、ES6

写在最后

(相关免费学习推荐:javascript视频教程)

前言

立即学习“Java免费学习笔记(深入)”;

本篇文章是《JavaScript专项进阶系列》的第一篇文章,全系列大概会包含例如:

防抖节流扁平化深浅拷贝数组去重排序…

等等经典的专项知识点。取名为专项进阶是因为它们在很多场合的“出镜率高”很高,为了避免化身google内容搜索师,《JavaScript专项进阶系列》诞生了!!!

在这里插入图片描述

一、有趣的现象

按照大家的常识,JavaScript代码在执行是一定是自上而下的,你需要输出一个字符串,当然需要提前声明一个保存string类型的变量。如果深奥的道理我都能懂,于是我阅读了下面的代码。

1.1 我以为的开局
var str = '123';console.log(str); // 123

登录后复制

我们调换一下代码的位置在再看:

console.log(str); // undefinedvar str = '123';

登录后复制

我好像找到规律了!!!

当我看完了前两段代码并且进行了“深度思考”后,我好像找到规律了,那就是:在当前代码块后函数中,在变量声明和初始化之前使用变量,会拿不到正确的值。

JavaScript专题之一:变量提升与预编译

1.2 实际上是这样的

带着上面的“结论”我来到了这里

var val = '余光';(function(){    console.log(val); // 余光})();

登录后复制

果然如此!,在变量声明和初始化之后耶稣也阻挡不了我拿到val的值,我说的!!!

当我看到下面一段代码时,我已经产生了动摇,此事必要蹊跷。

var val = '余光';(function(){    console.log(val); // undefined    var val = '测试';})();

登录后复制

Ps:如果大家立即执行函数存在疑问,不妨看看《JavaScript之深入理解立即调用函数表达式(IIFE)》吧~

这…我怂了,是什么原因导致这样的现象发生的呢?Js又是如果处理的呢?

JavaScript专题之一:变量提升与预编译

二、Js的预解析

在当前的作用域内,无论在哪里变量声明,在幕后,都会进行一次看不见的移动。

注意:仅声明被“移动”。即声明和赋值在某些时候被动分开了。而这次看不见的移动实际上就是Js在编译阶段的解析。

来看一段《你知不知道的Js》中经典的例子:

name = '余光'; // 未添加关键字(未声明),name为全局变量,,即window.name = '余光'var name; // 再次声明name,此时name未进行初始化,它的值是undefined吗?console.log(name); // ?

登录后复制

结果是成功打印“余光”,这样看不见的移动就发生在Js预解析(编译)之中。

2.1 核心:预解析

为了搞明白这个核心问题,我们需要回顾一下,引擎会在解释JavaScript代码之前首先对其进行编译。编译阶段中的一部分工作就是找到所有的声明,并用合适的作用域将它们关联起来。感兴趣的小伙伴可以阅读《JavaScript中的变量对象》和《从作用域到作用域链》这两篇文章哦~

因此,发生这样的事情,包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。当你看到var a = 2;时,可能会认为这是一个声明。但JavaScript实际上会将其看成两个声明:var a;和a = 2;。

第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段。

即代码是这样写的:

// 我们看到的代码:var name = '余光';

登录后复制

但Js会将它解析成:

// 声明(Declaration)var name; // 声明但未初始化,所以分配 undefined// 初始化(Initialization)name = '余光'; // 初始化(赋值)

登录后复制

所以本小结的一段代码应该这样分析:

var name; // 声明name提到作用域顶部,并被分配了一个undefinedname = '余光'; // 进行初始化操作console.log(name); // '余光'

登录后复制

2.2 注意:只有声明被提升了

只有声明会被提升,而赋值和其他代码逻辑会在执行到代码的位置时才会生效。所以会有下面的问题:

foo();function foo(){    console.log(name); // undefined    var name = '余光';}

登录后复制登录后复制

函数被提升了,自然可以正常执行,但变量仅仅是声明被提升了。

2.3 每个作用域都会进行提升操作

还是上面的代码:

foo();function foo(){    console.log(name); // undefined    var name = '余光';}

登录后复制登录后复制

实际它在编译时是这样的:

function foo(){    var name; // 声明    console.log(name); // undefined    name = '余光'; // 初始化}foo(); // 函数执行

登录后复制

JavaScript专题之一:变量提升与预编译

三、提升之间的优先级

既然我们知道了变量和函数会被提升,他们之间又是如何判断优先级的呢?

3.1 函数会被首先提升,然后才是变量

我们分析下面的代码:

foo();var foo; // 1function foo(){    console.log('余光');}foo = function(){    console.log('小李');}

登录后复制

本着函数优先提升的原则,他会被解析成这样:

function foo(){    console.log('余光');}foo(); // 余光foo = function(){    console.log('小李');}

登录后复制

注意,var foo 因为是一个重复声明,且优先级低于函数声明所以它被忽略掉了。

3.2 函数字面量不会进行函数提升

最直观的例子,就是在函数字面量前调用该函数:

foo();var foo = function(){    console.log(1);}// TypeError: foo is not a function

登录后复制

这段程序中:

变量标识符foo被提升并分配给所在作用域(在这里是全局作用域),因此在执行foo()时不会导致ReferenceError(),而是会提示你 foo is not a function。然后就是执行foo,foo此时并没有赋值(注意变量被提升了)。由于对undefined值进行函数调用而导致非法操作,因此抛出TypeError异常。

四、ES6和小结

ES6新增了两个命令let和const,用来声明变量,有关它们完整的概念我会在《ES6基础系列》中总结,提起它们,是因为变量提升在它们身上不会存在

4.1 变量提升是可以规避的

let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

// var 的情况console.log(foo); // 输出undefinedvar foo = 2;// let 的情况console.log(bar); // 报错ReferenceErrorlet bar = 2;

登录后复制

上面代码中,变量foo用var命令声明,会发生变量提升,即脚本开始运行时,变量foo已经存在了,但是没有值,所以会输出undefined。变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

在变量提升上,const和let一样,只在声明所在的块级作用域内有效,也不会变量提升

4.2 小结

变量提升:函数声明和变量声明总是会被解释器悄悄地被”提升”到方法体的最顶部,但变量的初始化不会提升;函数提升:函数声明可以被看作是函数的整体被提升到了代码的顶部,但函数字面量表达式并不会引发函数提升;函数提升优先与变量提升;let和const可以有效的规避变量提升

最后提炼一下:JavaScript引擎并不总是按照代码的顺序来进行解析。在编译阶段,无论作用域中的声明出现在什么地方,都将在代码本身被执行前首先进行处理,这个过程被称为提升。声明本身会被提升,而包括函数表达式的赋值在内的赋值操作并不会提升。

相关免费学习推荐:javascript(视频)

以上就是JavaScript专题之一:变量提升与预编译的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2717419.html

(0)
上一篇 2025年3月7日 22:04:03
下一篇 2025年3月7日 04:16:39

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • javascript怎么判断是否为null

    javascript判断是否为null的方法:1、通过“if (value === null) {…}”方法进行判断;2、通过“if (!value && typeof value !=”undefi…

    2025年3月7日
    200
  • js怎么删除数组中某一元素

    js删除数组中某一元素的方法:首先获取指定元素在数组中的位置(即索引index);然后使用splice()函数根据索引值来删除数组中的元素,语法格式“splice(index, 1)”。 本教程操作环境:windows7系统、ECMAScr…

    2025年3月7日
    200
  • js中箭头函数和普通函数的区别是什么

    区别:箭头函数是“=>”,普通函数是“function”。箭头函数不能作为构造函数,不能使用new。箭头函数不绑定arguments,但普通函数可以。箭头函数中的this代表上层对象,普通函数中的this代表当前对象。 本教程操作环境…

    2025年3月7日 编程技术
    200
  • js如何获取图片宽高

    js获取图片宽高的方法:1、onload后在打印;2、通过complete与onload一起混合使用;3、通过定时循环检测获取,代码为【from:check : width:’+img.width+’,height:…

    2025年3月7日 编程技术
    200
  • JavaScript专题之二:数组去重

    目录 一、双层循环(暴力方法)二、indexOf和includes三、排序去重四、filter五、键值对(key-value)六、ES6七、一些问题参考 写在最后 (相关免费学习推荐:javascript视频教程) 立即学习“Java免费学…

    2025年3月7日
    200
  • JavaScript专题之三:防抖

    目录 一、为什么需要防抖二、防抖的原理三、防抖简单实现四、防抖进阶 写在最后 (相关免费学习推荐:javascript视频教程) 一、为什么需要防抖 立即学习“Java免费学习笔记(深入)”; 高频的函数操作可能产生不好的影响如:resiz…

    2025年3月7日 编程技术
    200
  • JavaScript专题之五:深浅拷贝

    了解拷贝背后的过程,避免不必要的错误,Js专题系列之深浅拷贝,我们一起加油~ 目录 一、拷贝示例二、浅拷贝三、深拷贝的方法? 四、自己实现深浅拷贝 免费学习推荐:javascript视频教程 一、拷贝示例 立即学习“Java免费学习笔记(深…

    2025年3月7日 编程技术
    200
  • JavaScript专题之六:类型检测

    javascript专题之数据类型检测的那些事 目录 一、typeof二、instanceof三、constructor四、stringTag是什么?五、实现几个数据检测的方法 写在最后 (免费学习推荐:javascript视频教程) 立即…

    2025年3月7日
    200
  • javascript字符串怎么截取前四位

    javascript字符串截取前四位的方法:1、使用“str.slice(0, 4)”来截取字符串前四位;2、使用“str.substr(0,4)”来截取字符串前四位;3、使用“str.substring(0,4)”来截取字符串前四位。 本…

    2025年3月7日 编程技术
    200
  • JavaScript专题之七:类型转换

    目录 前言一、类型转换是什么?二、原始值转换的基本规则三、对象转字符串和数字四、常见的类型转换运算符五、常见的类型转换操作 写在最后 (免费学习推荐:javascript视频教程) 前言 立即学习“Java免费学习笔记(深入)”; 在了解类…

    2025年3月7日 编程技术
    200

发表回复

登录后才能评论