头图

变量的定义:ECMAScript中变量是用来保存任意数据类型的命名占位符。目前有三种声明变量的方式:var、let、const。

一、 var声明

var message;
// 通过var关键字后面跟着变量名的方式来声明变量,可以保存任意类型的数据,在没有赋值的情况下,message的值为undefined
var name = 'Jack'
name = 12
// 这里创建了变量名为name,并保存字符串类型的数据'Jack',只是一个简单的赋值而已。随后,不仅可以改变保存的值,也可以改变值的类型。

1、var声明作用域
注意:使用 var 操作符定义的变量会成为包含它的函数的局部变量,不能在函数外部调用。

function test() { 
 var message = "hi"; // 局部变量
} 
test(); 
console.log(message); // 出错!
//使用 var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁,不能在外部调用。
function test() { 
 message = "hi"; //全局变量
} 
test(); 
console.log(message); // "hi"
// 去掉之前的 var 操作符之后,message 就变成了全局变量,可以在函数外部被调用

注意:在全局作用域下,通过var关键字创建的变量,会在window对象上添加相应的属性

var name = 'jack'
console.log(window.name) //'jack'

2、var变量提升
变量提升其实就是,可以在当前作用域下使用声明前的变量,其值为undefined。

console.log(name) // undefined
var name = 'Tom'
// 在声明变量name前就使用了name变量,并没有报错,而是为undefined

function foo() { 
 console.log(age); 
 var age = 26; 
} 
foo(); // undefined
// 同样在函数作用域里,也会存在变量提升

二、let声明
let关键字创建变量的方式跟var是一样的,区别在于let 声明的范围是块作用域,
而 var 声明的范围是函数作用域。以下是let跟var创建变量的区别

1、let创建变量不存在变量提升

// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

2、let会存在暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var num = 123;
if (true) {
  num= 'abc'; // ReferenceError
  let num;
}
//上面代码中,存在全局变量num,但是块级作用域内let又声明了一个局部变量num,导致后者绑定这个块级作用域,所以在let声明变量前,对num赋值会报错。

ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

3、不能重复声明
let不允许在相同作用域内,重复声明同一个变量。

// 报错
function func() {
  let a = 10;
  var a = 1;
}

// 报错
function func() {
  let a = 10;
  let a = 1;
}

function func(arg) {
  let arg;
}
func() // 报错

function func(arg) {
  {
    let arg;
  }
}
func() // 不报错

4、let会存在块级作用域
在大括号{ }里面使用let创建变量,此时大括号{ }会形成一个块级作用域,外部不能使用块级作用域下创建的变量,会报错,而通过var创建变量,不存在块级作用域。

if (true) { 
 let age = 26; 
 console.log(age); // 26 
} 
console.log(age); // ReferenceError: age 没有定义

if (true) { 
 var name = 'Matt'; 
 console.log(name); // Matt 
} 
console.log(name); // Matt

块级作用域解决以下两种场景:
1、内层变量可能会覆盖外层变量

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

2、用来计数的循环变量泄露为全局变量

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

上述两种场景都可以通过let创建变量形成块级作用域来解决

5、let创建变量不成成为window的属性

let name = 'ken'
console.log(window.name)  // undefined

const声明

const创建变量跟let一样,都会形成块级作用域,都不能重复声明。唯一的区别就是,const声明一个只读的常量,一旦声明,常量的值就不能改变。

const name = 'jack'
name = 'tom' // TypeError: Assignment to constant variable.

const foo;
// SyntaxError: Missing initializer in const declaration
// const声明变量一定要赋值,不然会报错

以上就是我个人总结出来声明变量的三种方式及其区别,下面再做几道题目来巩固以下知识点

if(1){
   a = 1;
   console.log(a)
}
console.log(a)

if(1){
  var b = 2;
  console.log(b)
}
console.log(b)

if(1){
  let c = 3;
  console.log(c)
}
console.log(c)
var a = [];
for (let i = 0; i < 10; i++) {
    console.log(i);
    a[i] = function () {
        console.log(i);//执行此代码时,for循环已经执行完毕。
    };
}
a[6]();
if (true) {
  tmp = 'abc';  
  console.log(tmp);  //在let声明变量tmp之前就使用该变量,会报错。

  let tmp; 
  console.log(tmp);  

  tmp = 123;
  console.log(tmp);  
}

跟我学前端
60 声望6 粉丝