文章目录
let和const命令
1.1 let应用循环定义i
这里就可以体现let声明的优势啦
看看下面两个循环分别应该输出什么 想想为什么结果不一样呢?
var a = [];
for(var i=0 ; i<10 ; i++){
a[i] = function(){
console.log(i);
}
}
a[6]();
var a = [];
for(let i=0 ; i<10 ; i++){
a[i] = function(){
console.log(i);
}
}
a[6]();
怎么样呢?结果和你想的一样吗
执行结果分别为10和6
两个程序的区别在于分别用var和了let定义了循环中的i,那么为什么输出结果不同呢?
主要原因就在于这两者的定义
变量作用域
不同,
- var声明的变量作用域如果在方法里声明则为局部变量(该方法内可以使用)
否则为全局变量(整个程序都可以使用哦),- 而let定义的变量呢,属于只能在定义它的块级作用域里使用哦(什么是块级作用域请看下面解释哦)
知道这个,再来看看具体代码:- 前者var定义的i是全局变量(没在方法中定义哦),那么每个for循环定义的i都是同一个i,所以随着每次循环的推进,i也在不断变化等到循环结束之后,i的值就更新为10了。
而在循环里面,只是对数组的每个值进行函数赋值,并没有执行函数,真正执行函数是等到循环结束之后,这时候程序找的i还是之前那个不断更新的i,所以此时输出i为10,- 后者let呢,每次循环定义的i都不同,自然就没什么更新啦,所以最后数组赋值分别为0到9
块级作用域简单来说:就是作用范围局限在一个括号内,比如if语句的{} 又或者for循环中的{}
1.2 let声明暂时性死区
听起来很高级的样子 其实就是说在变量在let声明之前都是不可以使用的,否则会报错,下面程序为不太好发现的“死区”现象
function(x = y, y = 3){
return[x,y];
}
怎么样?找到错误了吗?
如果找到恭喜你很棒
没有的话也祝贺你找到自己一个可以学习的点,那么就来看看下面的解释吧
这里的y在没有声明之前就使用了(给x赋值),所以会出错
函数形参声明相当于let声明定义(再插一嘴,形参定义过的变量在函数里面再用let声明也是会出错的哦 原因是let变量不能重复声明)
再来看看另一个容易忽视的例子吧
let x = x;
同样会报错,还是“暂时性死区”的锅
1.3 块级作用域
块级作用域也是es6新增的,在es5并没有这个说法,就导致有一些trouble存在,比如在函数里面定义重名的变量(和全局变量重名),就会覆盖外层变量,导致无法在函数内调用到外层变量
另一个trouble就是用来计数的变量(比如i)循环之后会暴露为全局变量
1.3.1作用域的嵌套
由于let这个特性,也比较好理解作用域的嵌套啦,就是外层作用域是没办法读取内层作用域的变量的
1.3.2函数在块级作用域的声明
再来说说函数在块级作用域的声明吧
(ES5之前是不允许的哦,ES6新增的func)
理论上对于ES6块级作用域中声明的函数,效果是类似于let声明一样,但是由于不兼容问题,对于浏览器具体的实现就更改为类似var了,可以通过下面的例子理解一下:
function fn(){
console.log("I am here");
}
(function(){
if(false){
function fn(){
console.log('Can you find me?');
}
}
fn();
})();
上面程序执行结果会出错,报错为 :fn is not a function ,上面一行输出fn为undefined 自然不能当函数调用啦,undefined的原因也很简单,就是在立即执行函数里面用function声明了fn(相当于var声明提升了),但是由于没有进入if语句所有没有进行赋值,所以最后fn就是undefined啦
所以呢,尽量在块级作用域少地声明函数,或者可以使用函数表达式(赋值语句)代替函数声明语句
另外在块级作用域中声明函数不加大括号也会报错哦,如下
if(true)
function fn()
console.log("test");
1.3 const声明变量特性的本质
const定义的变量是不可以修改滴,实际上呢,并不是变量的值不可以改变,而是变量指向的那个内存地址不可以改变。
就比如const声明的对象中的属性和方法以及数组中的元素是可以改变的,但是呢如果更改了其引用就有问题了 见下方两种赋值就是错的哦
const obj = {a:1};
obj = {};//此时就将obj指向了另一个对象了
const arr = [];
arr = ['1'];
js中变量的存储方式
js中的变量根据存储类型分为两类,一类为基本数据类型(包括Number,String,Undefined,Null,Boolean)另一类为引用类型(对象),那么这两种的区别是什么呢?
对于基本数据类型,是栈存储,对于变量存储的就是具体的值,各个之间是独立的,更改一个变量不会影响其他变量
而对于引用类型(对象),是堆存储,每次声明一个对象,都会在堆内存开辟一个空间(对应一个地址),变量里面实际存储的值为在堆内存中地址(对象引用),那么如果两个变量存的是一个地址(对象引用),那么通过其中一个变量当更改数据,另一个也会跟着改变
另外呢,如果真的想用const让对象的属性也冻结不能更改的话,可以用Object.freeze()方法来实现
看看下面的代码我相信你就理解啦
const obj = Object.freeze();
obj.a = 1;//error
1.4 ES6声明变量的6种方法
1.4.1顶层对象的属性
- 什么是顶层对象呢 对于浏览器来是window 对于node来说是global
- 在es5中 顶层对象和全局对象是等价滴(var b和window.b等价) 因为这个等价所造成的一些trouble es6便通过一些规定 让这两者区分开
- 规定是:var和function定义的全局变量还是属于顶层对象(window)滴,巴特const let定义的就不属于啦 比如let b = 1;这时候无法通过window.b找到这个b
1.4.2golbal对象
ES5中的顶层对象在各种实现中不统一,比如在浏览器和node中顶层对象分别为window和global,而同一段代码想通过this取到顶层对象在全局环境和函数中的this指向也不同,所以es6为了在所有情况下都取得顶层对象,可以通过垫片库获取顶层对象(import即可)
三、总结
最后呢 让我们来回顾一下都收获了哪些吧~
首先呢就是有关let和const声明变量问题
- let应用
- 有关let暂时性死区的易错点
- const定义的变量实际上是不能改变变量指向的那个内存地址
- 有关顶层对象和全局对象分隔
- 块级作用域:嵌套和函数声明问题
解构赋值
- 数组解构赋值
- 对象解构赋值 :内部机制(同名),嵌套赋值
- 字符串 数值 解构赋值
- 具体应用:交换数值,提取JSON数据,模块提取,函数参数,函数返回多个值,函数参数,遍历Map结构
怎么样?通过上述这些点,你可以回忆起具体解释吗?如果有模糊的话就是要及时回顾复习哦~
完结撒花❀