JS/ES6-var、let、const用法与区别

var的特点

1.作用域是全局作用域,没有块级作用域的概念

使用var声明的变量,无论是在全局还是函数内部,生成的变量都会是全局变量,作用域是全局作用域,无论在哪个代码块中都可以直接访问到,此时我们可能就会想是不是无论在哪里都能够修改var声明的变量的值呢?让我们看一下下面的代码:

var a = 10;
function b (value) {
  value = 2;
  console.log(value);
}
b(a);//2
console.log(a);//10
function c () {
  console.log(a);
  a = 2;
  console.log(a);
}
c();// 10 2
console.log(a);//2

从代码运行的结果可以看出,var声明的变量作为参数传入函数中时,在函数里面修改参数的值,此时函数外部对应的变量的值不变;为什么会出现这种结果呢?
因为当var声明的变量a作为参数传入函数b时,进行的是值的传递,即把a的值10赋值给了函数b作用域中的变量value(相当于克隆),这是a与value是没有联系的,在函数中无论怎样修改value的值都不会对a产生影响;
而当变量a没有作为参数传入函数时,执行函数c时,编译器会从函数c的执行上下文中查找变量a,因为在函数执行上下文中没有找到,就会往全局执行上下文查找,从而找到全局作用域下的a,此时如果在函数中修改a的值编译器会找到全局作用域下的a进行修改。
下面我们再看另一种情况;当参数是对象类型时,又会是什么结果?

var a = [10, 2];
function b (value) {
  value[0] = 3;
  console.log(value);
}
b(a);//[3,2]
console.log(a);//[3,2]

function c () {
  console.log(a);
  a[0] = 2;
  console.log(a);
}
c();//[2,2]
console.log(a);//[2,2]

从上面的结果可以看出,当参数为对象类型时,将对象作为参数传入函数中进行修改和直接在函数中进行修改结果是一样的;这是因为在对象变量a对应的内存中存储的是指向数组[10,2]的指针,也就是数组[10,2]对象的地址,对象数据类型(Object、Function、Array)是存储在堆空间中的,而普通数据类型在栈空间中,对象a作为参数时,就将地址值赋值给了value,此时函数中的value与a都指向了数组[10,2];无论对a还是value进行修改最终实际都是在修改数组。

2.存在变量提升

什么是变量提升?

a();//a is not a function
fun();//hello
fun2();//fun2 is not defined
var a = function () {
  console.log('hello');
}
function fun () {
  console.log('hello');
}

console.log(b);//undefined
console.log(d);//d is not defined
var b = 'hello';

变量的提升就是编译器在执行代码时,会先创建相应的执行上下文(全局执行上下文、函数执行上下文),在执行上下文中保存了函数、变量的声明,然后编译器才开始从上至下执行每一条语句;
在执行第一条语句时,因为变量提升的特性,在全局执行上下文中存在变量a的声明,但此时还没有执行后面的赋值语句将函数赋值给变量a,所以编译器认为a是个变量,报出错误a is not a function;而在执行到打印b变量时,同理,因为变量提升,控制台显示的结果为undefined,意思就是变量b已经声明了,并初始化为undefined;之后的打印d变量,因为整段代码中都没有声明d变量,也就不存在变量提升,所以报出错误d is not defined,意思是在代码中没有声明变量d。
在执行代码中的第二条语句时,在全局执行山下文中已经有了函数的声明(称为函数提升),结果为hello;而在执行第三条语句时,因为整段代码中都没有声明函数fun2(),也就不存在函数提升,所以报出错误fun2 is not defined。

let的特点

1.let声明的变量拥有块级作用域,let声明仍然保留了提升特性

var a = 2
let b = 3
function fun() {
  console.log(++b)
  let c = 'dk'
}
fun()
console.log(b) //4
console.log(c) //c is not defined

在ES5中有全局、函数、eval(在严格模式下)三种作用域;let是在ES6中引入的新语法,主要是为了解决var的缺点:没有块级作用域的;这可以说是JavaScript的一个缺陷,由于var没有块级作用域的概念导致无论在函数内部还是外部定义的变量都会变成全局变量,造成全局环境的污染;有时我们不希望在函数的外部去访问函数内部定义的变量,显然var不能满足我们的需求,这时使用let就能完美的解决我们的问题。
说到let有没有变量提升,这是个争议比较大的问题,笔者认为let保留了提升的特性,但不会盲目的提升。

console.log(a);//undefined
console.log(b);//Cannot access 'b' before initialization
var a = 2;
let b = 3;

从上面代码的结果我们就可以发现问题,如果说let声明的变量不存在变量提升的话,第二条语句执行的结果应该是 b is not defined,而不是上面的结果,为什么会是这样呢?因为let保留了部分的提升特性,变量存在声明、初始化和赋值三大过程,var声明的变量声明、初始化都提升了,也就是编译器开始执行代码时,首先在执行上下文中保存了变量的声明,同时将变量初始化为undefined;而使用let声明的变量则只是进行了声明的提升,在执行上下文中保存了变量的声明,但并没有初始化,所以就会出现上面的结果Cannot access ‘b’ before initialization。
想要深入了解let变量提升?

2.let声明的全局变量不是全局对象的属性,不可以通过window.属性名的方式访问这些变量

<script>
      var a = 2
      let b = 3
      console.log(window.a, window.b);//2 undefined
</script>

通过上面的代码可以发现使用var定义的变量会成为window全局对象的属性,有人可能会有疑惑:上面的代码中打印window.b结果出现undefined,那是不是说明window全局对象中存在变量b,b被赋值为了undefined?其实这是因为:JS中如果对象不存在属性,那么访问这个不存在的属性时,编译器不会报错,而是访问的结果为undefined;由于报错会导致程序停止执行后面的代码,有时我们希望在访问一个变量时,变量不存在的情况下代码也不要报错,就可以考虑利用编译器的这个特性,通过对象属性去访问变量,比如:我们访问上面的变量a时可以使用window.a来代替。

3.var和let定义的变量在同一个作用域中不能重复声明

var a = 're'
 var a = 'hello'//Identifier 'a' has already been declared
let h = 'hello'
let h = 'world' //报错 Identifier 'h' has already been declared
var b = 'we'
let c = 'wo'
function fun() {
  var b = 1
  let c = 2
  console.log(b, c)
  }
fun()//1 2
console.log(b, c)//we wo

通过代码可以知道var和let定义的变量在相同作用域下都不可以重复声明;上面的代码中变量b、c在全局作用域和函数作用域中进行了声明,此时的重复声明不会报错。

4.var、let都支持解构特性

解构是ES6新增的一种获取对象中的属性值和数组中相应元素值的方法;相比于传统的方法它更加简洁,访问属性的方法也更加清晰;解构分为对象解构和数组解构;下面用代码演示一下用法

//数组解构
const F4 = ['小沈阳', '刘能', '赵四', '宋小宝'];
let [xiao, liu, zhao, song] = F4;
console.log(xiao, liu, zhao, song);//小沈阳 刘能 赵四 宋小宝
let [we] = F4;
console.log(we);//小沈阳
//对象解构
const zha = {
  name: 'zhaobenshan',
  age: '不详',
  xiaopin: function () {
    console.log('我可以演小品');
  }
}
let { a } = zha;
console.log(a);//undefined
let { name } = zha;
console.log(name);//zhaobenshan

对于对象的解构通过相应的属性名将属性值赋值给变量名与属性名相同的变量;对于数组的解构通过变量声明时所在的下标将数组中的值赋值给相应下标的变量。

const的特点

1.声明时就要进行赋值

const A;//Missing initializer in const declaration

2.一般常量使用大写(潜规则)

3.常量的值不能修改

const SCHOOL = 'zhku123';
SCHOOL = 'zl';//Assignment to constant variable

4.块级作用域

{
  const PLAYER = 'UZI';
}
console.log(PLAYER);//PLAYER is not defined

5.对于数组和对象的元素修改,不算做对常量的修改

const TEAM = ['UZI', 'MXLG', 'Ming', 'Letme']
TEAM.push('Meiko')
console.log(TEAM)//['UZI', 'MXLG', 'Ming', 'Letme', 'Meiko']

6.不能重复声明

const A = 'ko'
const A = 12 //Identifier 'A' has already been declared
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值