平凡前端之路_15.let 和 const声明

系列文章

平凡前端之路_01.HTML与 HTML5
平凡前端之路_05.CSS与CSS3
平凡前端之路_14.ES与ES6
平凡前端之路_15.let 和 const声明
平凡前端之路_16.变量的解构赋值

作用域与let和const声明

ES6有两种新的声明变量的方式(let 和 const),再加上过去使用的方式(var)共三种。


提示:以下是本篇文章正文内容,下面案例可供参考

作用域

作用域定义了一个变量的生命周期和访问权限,可以是任意一个模块,一个函数,或者一个代码块。

let 和 const 所声明的变量在指定块的作用域外无法被访问。


let 关键字声明

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

  	let x = '收手吧阿组'
  	let x = '长得帅不用交房组'	// Identifier 'x' has already been declared

let 在当前作用域中声明并可以选择初始化变量,声明的变量可以再重新赋值。

	let x = '靓仔啊下个月涨房组';	
	x = '房东告诉我下个月涨房组';	
	x //=> '房东告诉我下个月涨房组'

let 在当前作用域中声明并可以选择初始化变量,未被初始化的变量的值是 undefined;

	let x; 
	let y = '如来佛组'  
	x //=> undefind
	y //=> '如来佛组'

下面这段代码强调了 let 的块级作用域:

  	let x = '关东组';
 	x //=> '关东组'
  	{ 
    	let x = '斗地组';
    	x //=> '斗地组'
  	}
	x //=> '关东组'

相反,使用 var 声明的变量则不具备块及作用域:

  	var x = '光宗耀组';
  	x //=> '光宗耀组'
  	{ 
    	var x = '欺师灭组';
    	x //=> '欺师灭组'
  	}
  	x //=> '欺师灭组'

let不存在变量提升。

变量提升(变量可以在声明之前使用,提升到当前代码块的顶部,不会报错,值为undefined)。

在一个作用域中如果存在let,这个区块对声明的变量从一开始就会形成封闭作用域,如果在声明前使用这些变量就会报错,这称为“暂时性死区TDZ”。

  console.log(x); // x is not defined
  let x = '败事有余,成事不组';

  if(true){	// 不管代码块是否被执行
	let y = '比下有余,比上不组'
  }
  console.log(y); // y is not defined

相反,使用 var 声明的变量存在变量提升。

变量提升(变量可以在声明之前使用,提升到当前代码块的顶部,不会报错,值为undefined):

  console.log(x); // undefind
  var x = '兵精粮组';

  if(false){	// 不管代码块是否被执行
	var y = '鼎分三组'
  }
  console.log(y); // undefind

const 关键字声明

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

  const y = '收手吧阿组'
  const y = '长得帅不用交房组'	// Identifier 'y' has already been declared

const 在当前作用域中声明并可以选择初始化变量,声明的变量不能再重新赋值。

不可变的值是指那些一旦创建,就不能再改变的值。

	const x = '靓仔啊下个月涨房组';	
	x = '房东告诉我下个月涨房组';	// Assignment to constant variable.
	x //=> '靓仔啊下个月涨房组'

const 声明变量必须强制进行初始化操作。

	const x ;	// Missing initializer in const declaration
	const y = '如来佛组'	
	y //=> '如来佛组'	

本质上const是让变量指向的那个内存地址保存的数据不得改动,对于基本类型数据是不可变的, 对于复合类型的数据(对象Object、Map、Set和数组)是可变的,const变量指向的内存地址保存的只是一个指向实际数据的指针,const只能保证这个指针固定,对象或数组本身是可变的。

  const foo = {};
  foo //=> {}
  
  // 为 foo 添加一个属性,可以成功
  foo.prop = '贩夫走组';
  foo //=> { prop:  '贩夫走组'}


  // 将 foo 指向另一个对象,就会报错
  foo = {}; // TypeError: "foo" is read-only


  const a = [];
  a //=> []
  
  a.push('画蛇添组'); // 可执行
  a //=> ['画蛇添组']
  
  a.length = 0;    // 可执行
  a //=> []
  
  a = ['身先士组']; // 报错TypeError: "a" is read-only 

声明一个不变的常量,可以使用Object.freeze()方法来冻结变量 ,如:

  const obj = {
    name: '白雪公组'
  }
  Object.freeze(obj)  // 此时对象obj被冻结,返回被冻结的对象
  obj //=> { name: '白雪公组'}

需要注意的是,被冻结后的对象不仅仅是不能修改值,同时也

  • 不能向这个对象添加新的属性
  • 不能修改其已有属性的值
  • 不能删除已有属性
  • 不能修改该对象已有属性的可枚举性、可配置性、可写性

下面这段代码强调了 const 的块级作用域:

    const x = '关东组';
 	x //=> '关东组'
  	{ 
    	const x = '斗地组';
    	x //=> '斗地组'
  	}
	x //=> '关东组'

相反,使用 var 声明的变量则不具备块及作用域:

  	const x = '光宗耀组';
  	x //=> '光宗耀组'
  	{ 
    	const x = '欺师灭组';
    	x //=> '欺师灭组'
  	}
  	x //=> '欺师灭组'

const 不存在变量提升。
变量提升(变量可以在声明之前使用,提升到当前代码块的顶部,不会报错,值为undefined)。

在一个区块中如果存在const ,这个区块对声明的变量从一开始就会形成封闭作用域,如果在声明前使用这些变量就会报错,这称为“暂时性死区TDZ”。

  console.log(x); // x is not defined
  const x = '败事有余,成事不组';

  if(true){	// 不管代码块是否被执行
	const y = '比下有余,比上不组'
  }
  console.log(y); // y is not defined  

相反,使用 var 声明的变量存在变量提升(变量可以在声明之前使用,值为undefined):

  console.log(x); // undefind
  var x = '兵精粮组';

  if(false){	// 不管代码块是否被执行
	var y = '鼎分三组'
  }
  console.log(y); // undefind

循环与作用域

在循环中,循环体不在同一个作用域内(创建了多个作用域)

  (function run(){
    for(let i = 0; i < 5; i++){
   		let k = 5;		// 如果循环体在同一个作用域,会报错 Identifier 'k' has already been declared
   		console.log(k); 
    }
  })();

在循环中,循环条件和循环体不在同一个作用域内

  (function run(){
    for(let i = 0, k = 1; i < 5; i++){
   		let k = 5;		// 如果循环条件和循环体在同一个作用域内,会报错 Identifier 'k' has already been declared
   		console.log(k);
    }
  })();

在循环中,循环条件在同一个作用域内

  (function run(){
    for(const i = 0; i < 5; i++){ // 报错 Assignment to constant variable.
   		console.log(i);	// 同一个作用域内 const 声明的变量不能再重新赋值
    }
  })();

由于const 在同一作用域中声明的变量不能再重新赋值,const不能用于循环条件中作为迭代变量。


闭包与作用域

在 ES5 及之前,如果循环内有产生闭包,闭包内访问闭包外的变量,会产生问题:

循环中闭包内访问闭包外同一个 i 变量(变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i,每一次循环,变量i的值都会发生改变),所有的闭包函数都会访问同一个全局的 i 变量最终的值。

  (function run(){
    for(var i = 0; i < 5; i++){
      let j = i
      var k = i
      setTimeout(function log(){
      	console.log(i);	// 5 5 5 5 5     变量i 自增6次,i值最终为5,最后一次不符合循环条件并结束循环
      	console.log(j); // 0 1 2 3 4
        console.log(k); // 4 4 4 4 4	 使用同一个变量k,k值最终为4
      }, 0);
    }
  })();

在 ES6及之后,可以在循环条件中使用 let 声明变量,在循环中创建多个块级作用域,循环中闭包内访问闭包外当前块级作用域中的变量(只在本轮循环有效)。

  (function run(){
    for(let i = 0; i < 5; i++){
    	let j = i
      	var k = i
      setTimeout(function log(){
        console.log(i);	// 0 1 2 3 4
      	console.log(j); // 0 1 2 3 4
        console.log(k); // 4 4 4 4 4	使用同一个变量k,k值最终为4
      }, 100);
    }
  })();

在 ES5及之前,闭包解决方案:
将每次循环 i 的值保存到函数作用域里,作为参数传递

  (function run(){
    for(var i = 0; i < 5; i++){
    	(function(i) {
   	 		setTimeout(function log(){
      			console.log(i);
      		}, 0);
    	})(i)
    }
  })();

在 ES6及之后,闭包解决方案:

 (function run(){
    for(let i = 0; i < 5; i++){
   	 	setTimeout(function log(){
      		console.log(i);
      	}, 0);
    }
  })();

总结

以上就是今天要讲的内容,本文简单介绍了两种新的声明变量的方式(let 和 const)与作用域关系。

JS知识题

问题答案
const 声明的变量不能再重新赋值const 在同一作用域中 声明的变量不能再重新赋值,在不同作用域或不同块级作用域中是可以重新声明赋值的
let 声明的变量不能再重新声明let 在同一作用域中 声明的变量,在不同作用域或不同块级作用域中是可以重新声明
let 关键字定义的变量声明时必须进行初始化再使用let 声明的变量只需先声明再使用,无需进行初始化const 关键字定义的变量声明时必须进行初始化再使用,var 关键字定义的变量可以先使用后声明。
const 能重新声明赋值var已声明的变量const 在同一作用域中 不能重新声明赋值var已声明的变量,在不同作用域或不同块级作用域中const是可以重新声明赋值var已声明的变量
let 能重新声明赋值var已声明的变量let 在同一作用域中 不能重新声明赋值var已声明的变量,在不同作用域或不同块级作用域中let 是可以重新声明赋值var已声明的变量
var 能重新声明赋值var已声明的变量var 在任何地方都可以重新声明赋值var已声明的变量
const能用于循环条件中作为迭代变量const 在同一作用域中声明的变量不能再重新赋值,所以const不能用于循环条件中作为迭代变量。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值