资讯专栏INFORMATION COLUMN

JavaScript基础系列---变量及其值类型

sugarmo / 311人阅读

摘要:但对于引用类型的数据主要是对象和数组,变量指向的内存地址,保存的只是一个引用地址指针,只能保证这个引用地址指针是固定的,至于它指向的堆内存中的存储的值是不是可变的,就完全不能控制了。

基础概念

变量是存储信息的容器,这里需要区分一下:变量不是指存储的信息本身,而是指这个用于存储信息的容器,可以把变量想象成一个个用来装东西的纸箱子

变量需要声明,并且建议在声明的同时进行初始化,如下所示:

var aa = "1";
let bb = 2;
const CC = 3;

如果重新声明变量,该变量的值不会丢失

var aa = "cc";
var aa; //此时的值还是cc

变量名(标志符)命名要求:

适用于变量/属性/函数/函数参数的名字等

可由字母、数字、下划线_及美元符$组成

不可以使用关键字、保留字以及truefalsenull、(evalarguments严格模式下)

使用undefined虽然不会报错,但是并不能声明成功,无论给它初始化为什么,它的值仍然为undefined

最好使用字母开头(还可以使用_$

值类型

变量可能包含两种不同数据类型的值:基本类型值(简单的数据段)和引用类型值(可能由多个值构成的对象)

因为所有引用类型的值都是Object的实例,所以可以使用 xx instanceof Object 来判断是否为引用类型的值,如果是则会返回true,否则返回false

存储方式

基本类型值在内存中占据固定大小空间,因此被保存在栈内存中(栈由操作系统自动分配释放);

引用类型值是对象,保存在堆内存中;(堆:一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收)

访问方式

对于基本类型,变量存储的是具体的值,可以按值访问,直接操作存储的值

对于引用类型,变量存储的是对象引用地址(指针),是按引用访问,当查询时,我们需要先从栈中读取存储的内存地址,然后再顺藤摸瓜地找到保存在堆内存中的值

复制变量值

复制时,基本类型拷贝的是具体的值,而引用类型拷贝的是引用地址,所以引用类型赋值时要格外注意!!!

//基本类型赋值
var num1 = 10;
var num2 = num1;
console.log(num1,num2);// 10, 10
num2 = 100;
console.log(num1,num2);// 10, 100

//引用类型赋值
//person2拷贝了person1的引用地址,所以person1和person2实际上指向的是同一个对象
//所以改变person2会影响到person1
var person1 = {
    name:"cc"
};
var person2 = person1;
console.log(person1.name, person2.name);// cc, cc
person2.name = "cshine";
console.log(person1.name, person2.name);// cshine, cshine

var arr1 = [1,2,3];
var arr2 = arr1;
console.log(arr1, arr2);// [1, 2, 3], [1, 2, 3]
arr2[1]= "string";
console.log(arr1, arr2);// [1, "string", 3], [1, "string", 3]

传递参数

ECMAScript中所有的函数的参数都是按值传递,即使是引用类型值的传递也是按值传递

传递基本类型值时,被传递的值会被复制给一个局部变量(即命名参数或者arguments中的一个元素)

传递引用类型值时,引用类型值是一个引用地址(指针),它也会复制给一个局部变量,这个局部变量再根据这个地址去按引用访问对象,因此这个局部变量的变化会反映在函数的外部。所以还是按值传递,而不是因为它是按引用传递才导致局部变量的变化会反映在函数的外部

注意点

es6新增了letconst命令用来声明变量

let命令,它的用法类似于var,但是所声明的变量,只在let命令所在的块级作用域内有效,且在该块级作用域内不可重复声明

const声明一个只读的常量,一旦声明,常量的值就不能改变,与let一样,只在声明所在的块级作用域内有效,且在该块级作用域内不可重复声明

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于引用类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个引用地址(指针),const只能保证这个引用地址(指针)是固定的,至于它指向的堆内存中的存储的值是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

未使用var/let/const进行声明,此时创建的变量为全局变量,尽量不要这样做!

var命令和function命令声明的全局变量,是顶层对象的属性,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性(浏览器中为windownode中为global

对于var/let来说,只声明而未初始化,此时变量存储的信息是“空”的,也就是undefined;但是对于const来说,只声明而不进行初始化是不允许的,会抛出Missing initializer in const declaration的错误,因为const定义的是常量,一旦定义便不可更改。

声明提升(hoisting

var命令声明的变量会发生”声明提升“现象,即变量可以在声明之前使用,值为undefined

console.log(aa);// undefined
var aa = "string";
console.log(aa);// string

function声明的函数也会发生”声明提升“现象,函数在声明之前可以使用,值为指向该函数的指针;但是如果是在支持ES6的环境中,ES6允许在块级作用域内声明函数,此时在块级作用域中声明的函数,会提升到所在的块级作用域的头部,值为整个函数块,同时还会提升到块级作用域外,但是值为undefined。(环境导致块级作用域内声明函数的行为差异非常大,所以应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句)

console.log(fn);// ƒ fn(){}
function fn(){}
console.log(fn);// ƒ fn(){}

//支持es6的环境中
console.log(fb);// undefined
if(true){
    console.log(fb); // ƒ fb(){}
    function fb(){}
}
console.log(fb);// ƒ fb(){}

console.log(fc);// undefined
if(false){
    console.log(fc); // 未执行
    function fc(){}
}
console.log(fc);// undefined
fc() //Uncaught TypeError: fc is not a function

函数和变量都会声明提升,此时若函数名和变量名同名,函数名的优先级要高;但是正式执行代码时,同名函数会覆盖只声明却未赋值的变量,但是它不能覆盖声明且赋值的变量(原因分析可见另一篇文章JavaScript基础系列---执行环境与作用域链)

console.log(bb);// ƒ bb(){}
var bb;
function bb(){}
console.log(bb);// ƒ bb(){}


console.log(cc);// ƒ cc(){}
var cc = "string";
function cc(){}
console.log(c)c;// string

局部变量/函数(函数作用域function块里面的变量/函数)也会声明提升,可以先使用后声明,不影响外部同名变量/函数

letconst命令改变了语法行为,它们所声明的变量一定要在声明后使用,否则报错。由于letconst命令在当前块级作用域内不可重复声明所以当有同名函数时,会直接报错Identifier xx has already been declared

参考资料

《JavaScript高级程序设计第三版》

ECMAScript 6 入门

MDN变量

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95063.html

相关文章

  • JavaScript基础系列---闭包及其应用

    摘要:所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。所以本文中将以维基百科中的定义为准即在计算机科学中,闭包,又称词法闭包或函数闭包,是引用了自由变量的函数。 闭包(closure)是JavaScript中一个神秘的概念,许多人都对它难以理解,我也一直处于似懂非懂的状态,前几天深入了解了一下执行环境以及作用域链,可戳查看详情,而闭包与作用域及作用域链的关系密不可分,所...

    leoperfect 评论0 收藏0
  • 【重温基础】22.内存管理

    摘要:内存泄露内存泄露概念在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。判断内存泄漏,以字段为准。 本文是 重温基础 系列文章的第二十二篇。 今日感受:优化学习方法。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1-14篇 【重温基础】15.JS对象介绍 【重温基础】16.JSON对象介绍 【重温基础】1...

    Pandaaa 评论0 收藏0
  • JavaScript 是如何工作的:JavaScript 的内存模型

    摘要:调用堆栈是存放原始数据类型的地方除了函数调用之外。上一节中声明变量后调用堆栈的粗略表示如下。解释改变的正确方法是更改内存地址。在声明时,将在调用堆栈上分配内存地址,该值是在堆上分配的内存地址。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第 21 篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可以在这里找到它们:...

    baoxl 评论0 收藏0
  • 前端每周清单半年盘点之 JavaScript

    摘要:前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点分为新闻热点开发教程工程实践深度阅读开源项目巅峰人生等栏目。背后的故事本文是对于年之间世界发生的大事件的详细介绍,阐述了从提出到角力到流产的前世今生。 前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点;分为新闻热点、开发教程、工程实践、深度阅读、开源项目、巅峰人生等栏目。欢迎...

    Vixb 评论0 收藏0
  • JavaScript 是如何工作的:JavaScript 的共享传递和按传递

    摘要:它对数组和对象使用按值传递,但这是在的共享传参或拷贝的引用中使用的按值传参。例如在这里,变量和值在执行期间存储在堆栈中。返回值这是可选的,函数可以返回值,也可以不返回值。变量被推入堆栈,从而在执行时成为的副本。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第 22 篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可...

    keithyau 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<