为了账号安全,请及时绑定邮箱和手机立即绑定

死磕JavaScript-松散类型、js变量存储模型、变量提升

标签:
JavaScript

好久没来慕课网学习了,上研究生之后,发现突然又变回学生后对自己的要求也松很多,开始到处旅游,做些没计划的事情,也很少写技术博客了,最近静下心来开始研究底层的东西,以后就在这写了,希望能死磕自己,坚持下去。好了,干货走起......

  • 什么是松散类型

  • JavaScript两种变量类型的内存模型

  • 预加载

  • 变量提升

    javascript里的变量和其他语言有很大的不同,javascript的变量是一个松散的类型,松散类型变量的特点是变量定义时候不需要指定变量的类型,变量在运行时候可以随便改变数据的类型,但是这种特性并不代表javascript变量没有类型,当变量类型被确定后javascript的变量也是有类型的。

但是在现实中,很多程序员把javascript松散类型理解为了javascript变量是可以随意定义即你可以不用var定义,也可以使用var定义,其实在javascript语言里变量定义没有使用var,变量必须有赋值操作,只有赋值操作的变量是赋予给window,这其实是javascript语言设计者提升javascript安全性的一个做法。

此外javascript语言的松散类型的特点以及运行时候随时更改变量类型的特点,很多程序员会认为javascript变量的定义是在运行期进行的,更有甚者有些人认为javascript代码只有运行期,其实这种理解是错误的,javascript代码在运行前还有一个过程就是:预加载,预加载的目的是要事先构造运行环境例如全局环境,函数运行环境,还要构造作用域链,而环境和作用域的构造的核心内容就是指定好变量属于哪个范畴,因此在javascript语言里变量的定义是在预加载完成而非在运行时期。

讲一个例子来讲解:

var a = 1;
function test(){
    console.log(a);//undefined
    var a = 2;
    console.log(a);//2
}
test();

这是一个令人诧异的结果,为什么第一个弹出框显示的是undefined,而不是1呢?这种疑惑的原理我描述如下:

一个页面里直接定义在script标签下的变量是全局变量即属于window对象的变量,按照javascript作用域链的原理,当一个变量在当前作用域下找不到该变量的定义,那么javascript引擎就会沿着作用域链往上找直到在全局作用域里查找,按上面的代码所示,虽然函数内部重新定义了变量的值,但是内部定义之前函数使用了该变量,那么按照作用域链的原理在函数内部变量定义之前使用该变量,javascript引擎应该会在全局作用域里找到变量定义,而实际情况却是变量未定义,这到底是怎么回事呢?

这里我要先讲一个知识点,就是JavaScript的变量存储模型。

javascript语言和java语言一样变量是分为两种类型:基本数据类型和引用类型。基本类型是指:Undefined、Null、Boolean、Number和String;而引用类型是指对象,所以javascript的对象指的是引用类型。但是实际开发里如果我们对基本类型和引用类型的区别不是很清晰,就会碰到我们很多不能理解的问题,下面我们来看看下面的代码:

 var str = “Sharpxiajun";
    var num = 1;
    var xxx;
    console.log(str);//运行结果:sharpxiajun
    console.log(num);//运行结果:1
    console.log(xxx);//运行结果:undefined
当我们使用引用类型时候,结果就和上面完全不同了,大家请看下面的代码:
var obj1 = new Object();
obj1.name = "obj1 name”;
console.log(obj1.name);// 运行结果:obj1 name
Javascript里的基本变量是存放在栈区的(栈区指内存里的栈内存),它的存储结构如下图所示:

图片描述

javascript里引用变量的存储就比基本类型存储要复杂多,引用类型的存储需要内存的栈区和堆区(堆区是指内存里的堆内存)共同完成,如下图所示:
图片描述

理解基本类型变量和引用类型变量的存储结构后,结合上面开始讲的预加载的知识点,我们就能分析出开始那个例子的深层原因了。

引子里的代码在函数的局部作用域下变量a被重新定义了,在预加载时候a的作用域范围也就被框定了,a变量不再属于全局变量,而是属于函数作用域,只不过赋值操作是在运行期执行(这就是为什么javascript语言在运行时候会改变变量的类型,因为赋值操作是在运行期进行的),所以第一次使用a变量时候,a变量在局部作用域里没有被赋值,只有栈区的标示名称,因此结果就是undefined了。(这也就是js里的变量提升的原理)

不过赋值操作也不是完全不对预加载产生影响,预加载时候javascript引擎会扫描所有代码,但不会运行它,当预加载扫描到了赋值操作,但是赋值操作的变量有没有被var定义,那么该变量就会被赋予全局变量即window对象。

点击查看更多内容
4人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消