携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
let命令
基本用法
let所声明的变量,只在let所在的代码块内有效
{
let a = 1
var b = 2
}
console.log(b); // 2
console.log(i); // Uncaught ReferenceError: i is not defined
for(let i = 0; i < 5; i++){
console.log(i); // 0 1 2 3 4
}
console.log(i);//Uncaught ReferenceError: i is not defined
不存在变量提升
var存在变量提升,var声明的变量,在声明之前调用时undefined;let不存在变量提升,不能在声明之前调用它
console.log(a) //undefined
var a = 1
console.log(b) //Cannot access 'b' before initialization
let b = 2
暂时性死区
只要块级作用域内存在let命令,他所声明的变量就被绑定了,这个区域就不再受外界的影响
let a = 3
if(true){
console.log(a) //3
}
let a = 3
if(true){
console.log(a) //Cannot access 'a' before initialization
let a
}
ES6明确规定,如果区块内存在let或者const命令,这个区块对这些命令声明的变量,从一开始就会形成封闭作用域。凡是在声明这些变量之前使用,就会报错。在代码块内,在声明之前,这些变量都是不可用的,在语法上称为暂时性死区。
let tmp = 1;
if(true){
tmp='123'
console.log(tmp)//会报错 Cannot access 'tmp' before initialization
let tmp; //以上都是死区,现在暂时性死区结束
tmp = 'abc';
console.log(tmp)//abc
tmp='def'
console.log(tmp)//def
}
typeof
console.log(typeof a);// Cannot access 'aa' before initialization
let a = 1
console.log(typeof b); //undefined
var b = 2
隐蔽的暂时性死区
function handler(x=y,y=2){
return [x,y]
}
handler() //Cannot access 'y' before initialization
反之
function handler(x=y,y=2){
return [x,y]
}
console.log(handler(1)) //1
let a = a //Cannot access 'x' before initialization
var b = b //不报错
暂时性死区的本质:只要已进入当前作用域,所要使用的变量就已经存在了,但是不是可以获取,只有等到声明变量的那一行出现,才可以获取和使用该变量。
不允许重复声明
function func(){
let a = 1
let a = 2
}
//Identifier 'a' has already been declared
function func(){
let a = 1
var a = 2
}
// Identifier 'a' has already been declared
不允许声明函数参数
function func(arg){
let arg = 1
}
//Identifier 'arg' has already been declared
function func(arg){
{
let arg = 2
}
}
//不报错
块级作用域
为何需要快捷作用域?
ES5只用全局作用域和函数作用域,没有块级作用域
var a = 1
function func(){
console.log(a)
if(false){
var a = 2
}
}
func() //undefined(false里边的a变量提升导致)
用来循环计数的变量变为全局变量
for(var i=0;i<3;i++){}
console.log(i)//3
ES6的快捷作用域
function func(){
let a = 1
if(true){
let a = 2
}
console.log(a)
}
func()//1
//允许作用域的任意嵌套,每一层都是单独的作用域,外界访问不到
{{{
let a = 1
{
console.log(a)//1
let b = 2
}
}}}
//访问它下一层会报错
{{{
let a = 1
console.log(b)//b is not defined
{
let b = 2
}
}}}
//在不同作用域内也可以重名
{{{
let a = 1
{
let a = 2
}
}}}
块级作用域和函数声明
ES5规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明,浏览器没有此规定
function fn() {
console.log('我在外边')
}
(function() {
if (false) {
function fn() {
console.log('我在里边')
}
}
fn() //fn is not a function
}()
);
避免在块级作用域中声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。
//块级作用域内部的函数声明语句,建议不要使用
{
let a = 1
function b(){
return a;
}
}
//块级作用域内部,建议使用函数表达式
{
let c = 1
let d = function(){
return c;
}
}
注意:ES6快捷作用域必须有大括号,如果没有大括号,js引擎会认为不存在块级作用域
if(true) let a = 1 //Uncaught SyntaxError: Lexical declaration cannot appear in a single-statement context (
//不报错
if(true){
let b = 2
}
函数声明也是如此,严格模式下,函数声明只能在当前作用域的顶层
// 不报错
"use strict"
if(true){
function fn(){}
}
// 报错
"use strict"
if(true)
function fn(){}
// In strict mode code, functions can only be declared at top level or inside a block.
const命令
const声明一个只读的常量,一旦声明,常量的值就不能改变了。
const a = 1
a = 2
//Uncaught TypeError: Assignment to constant variable.
const只声明,不赋值会报错。const一旦声明变量,就立即初始化,不能留到以后赋值
const a;//Uncaught SyntaxError: Missing initializer in const declaration
const只在所声明的代码块内有效
{
let a = 1
}
console.log(a); //aa.html:14 Uncaught ReferenceError: a is not defined
const不存在变量提升
{
console.log(a);//Uncaught ReferenceError: Cannot access 'a' before initialization
a = 1
}
const也不能重复声明
const a = 1
const a = 2
//Uncaught SyntaxError: Identifier 'a' has already been declared
const保证的并不是变能被改变,而是变量指向的那个内存地址所保存的数据不能被改动。对于简单的数据类型,值就保存在变量指向的那个内存地址,因此等同于常量。对于符合类型的数据(主要是数组和对象),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针的固定的(指向固定的地址),至于他指向的数据结构是不是变化的,就完全不可控制的,因此,用const声明一个对象或者数组需注意。
const a = {}
a.name="a"
a = {}//aa.html:13 Uncaught TypeError: Assignment to constant variable.
const a = []
a[0] = 1
a = [] //Uncaught TypeError: Assignment to constant variable.
可使用Object.freeze来冻结对象或者数组,常规模式下没有错误,严格模式下会报错
let a = Object.freeze([1])
a[0] = 2
console.log(a)//[1]
"use strict"
let a = Object.freeze([1])
a[0] = 2
console.log(a)//[1] Uncaught TypeError: Cannot assign to read only property '0' of object '[object Array]'
将对象彻底冻结
function frozenHandler(obj){
Object.freeze(obj)
Object.keys(obj).forEach(res => {
if(typeof obj[res] === 'object){
frozenHandler(obj[res])
}
})
}
声明变量的六种方法
let fonst function var class import
顶层对象的属性
顶层对象,在浏览器中指的是window对象,在NodeJS中指的是global对象,在ES5中顶层对象和全局对象是等价的
window.a = 1
a = 2
console.log(window.a) //2
在ES6中,var和function声明的变量依旧是全局变量,依旧是顶层对象的属性。let、const、class声明的全局变量不属于顶层变量。
window.a = 1
let a = 2
console.log(window.a) //1
globalThis顶层对象
JS语言中存在一个顶层对象,他提供全局环境(即全局作用域),所有代码都在这个环境中运行,但是顶层变量与各个环境不统一 浏览器---window web worker----self Node ---global
ES2020,将global引入,作为顶层对象。任何环境下,globalThis都是存在的,任何环境下,都可以从他拿到顶层对象,指向全局环境的this.