资讯专栏INFORMATION COLUMN

ES6走走看看—由块级作用域引出的一场变革

荆兆峰 / 3346人阅读

摘要:块级声明块级声明是用于声明在指定块的作用域之外无法访问的变量。美元符号可以放到任何一个位置,甚至多带带一个美元符号。块级函数从开始,在严格模式下,块里的函数作用域为这个块。

持续更新的github笔记,链接地址:Front-End-Basics

此篇文章的笔记地址:字符到底发生了什么变化

ES6走走看看系列,特别鸣谢奇舞读书会~

块级作用域又称词法作用域,存在于:

函数内部(函数作用域)

块中(字符 { 和 } 之间的区域)

注意:ES6允许块级作用域任意嵌套

{{{{{{let text = "Hello World!"}}}}}}

因为有了块级作用域,然后我们才有继续往下聊的可能。

1、 块级声明

块级声明是用于声明在指定块的作用域之外无法访问的变量。

2、 let声明:用来声明一个块级作用域变量

1、 声明的变量具有块级作用域的特性

// 例子
function getValue (condition) {
    if (condition) {
        let value = "blue";
        return value;
    }
    console.log(value)
    // 报错 value is not defined
}
getValue()

2、 在同一个作用域内不能使用let声明同名的变量

// 不管是var,const或者let,新的let声明之前同名的变量,都会报错
var count = 30;
let count = 40;
// 报错 Identifier "count" has already been declared

// 函数形参和函数内部的let声明变量重名,报错
function test(value) {
    let value = 3;
}
test()
// 报错 Identifier "value" has already been declared

// 在不同的作用域声明的变量重名是没问题的
let count = 30;
if(true) {
  let count = 40;
  // 不同的作用域,不会报错
}

3、 声明没有预解析,不存在变量提升,有“临时死区”(TDZ)

从块的开始到变量声明这段的区域被称为临时死区,ES6明确规定,如果区块中存在let和const命令,则这个区块对这些命令声明的变量从一开始就形成封闭作用域,只要在声明之前就使用这些变量(赋值,引用等等),就会报错。

if(true) {
    console.log(typeof value);
    // 报错 value is not defined

    let value = "blue";
}

注意:TDZ是区域是“块开始”到“变量声明”,下面的例子不报错

// typeof 说是相对安全,确实是,永远拿不到想要的结果
console.log(typeof value); // 打印 undefined,没有报错
if(true) {
    let value = "red";
}
3、 const声明:声明常量(如PI),值一旦被设定后不可更改

1、 常量声明的值是不可变的

注意:const声明的对象不允许修改绑定,但可以修改该对象的属性值。

const number = 6;
number = 5;
// 报错 Assignment to constant variable

const obj = {number: 1};
obj.number = 2; // 不报错

obj = {number: 3};
// 报错 Assignment to constant variable

2、 因为常量声明后值就不可更改了,所以声明时必须赋值

// 有效的常量
const count = 30;

// 报错 Missing initializer in const declaration
const name;

3、 声明的常量具有块级作用域的特性

if(true) {
    const number = 5;
}
console.log(number)
// 报错 number is not defined

4、 在同一个作用域内不能使用const声明同名的变量

var message = "Hello";
let age = 25;

// 这两条语句都会报错
const message = "Good";
const age = 30;

5、 声明没有预解析,不存在变量提升,有“临时死区”(TDZ)


总结:一张表格

声明方式 变量提升 作用域 是否需要初始值 重复定义
var 函数级 不需要 允许
let 块级 不需要 不允许
const 块级 需要 不允许

扩展:再提一下变量命名,不管是var、let、const声明的变量名,可以由数字,字母,下划线及美元符号组成,但是不能以数字开头。美元符号可以放到任何一个位置,甚至多带带一个美元符号。

4、 循环中的块作用域绑定
循环中的let声明
// 第一个对比
// before
for(var i = 0; i < 5; i++) {
    // ... 省略一些代码
}
console.log(i)  // 5

//after
for(let i = 0; i < 5; i++) {
    // ... 省略一些代码
}
console.log(i) // 报错 i is not defined


// 第二个对比
// before
var funcs = [];
for(var i = 0; i < 10; i++) {
    funcs.push(() => {console.log(i)})
}
funcs.forEach((ele) => {
    ele()
})
// 打印 10次 10

// after
var funcs = [];
for(let i = 0; i < 10; i++) {
    funcs.push(() => {console.log(i)})
}
funcs.forEach((ele) => {
    ele()
})
// 打印 0 1 2 3 4 5 6 7 8 9

注意:有一点很重要,let 声明在循环内部的行为是标准中专门定义的,它不一定与 let 不提升特性有关。

循环中的const声明
// for 循环会报错
for (const i = 0; i < 1; i++) {
    console.log(i)
}
// 打印 0 ,然后报错 Assignment to constant variable.

// for-in 和 for-of 不会报错
var object = {
    a: true,
    b: true,
    c: true
};
for (const key in object) {
    // 不要在循环体内更改key的值,会报错
    console.log(key)
}
// 打印 a b c

注意:const可以应用在 for-in 和 for-of 循环中,是因为每次迭代不会修改已有绑定,而是会创建一个新绑定。

5、 块级绑定最佳实践的进化
ES6 早期

普遍认为默认使用let来替代var,对于写保护的变量使用const

ES6 使用中

普遍默认使用const,只有确实需要改变变量的值时使用let。因为大部分变量的值在初始化后不应再改变,而预料之外的变量值的改变是许多bug的源头。这样就可以在某种程度上实现代码的不可变,从而防止某些错误的发生。

6、 全局变量将逐步与顶层对象的属性脱钩

顶层对象,在浏览器环境指的是window对象,在Node指的是global对象。

为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;

var a = 1;
window.a // 1

另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。

上图可见let 声明的变量,并没有在Window对象里,而是一个新的Script对象。

扩展:如果需要在浏览器中跨frame或window访问代码,仍然可以用var在全局对象下定义变量。

7、 块级函数

从ECMAScript 6开始,在严格模式下,块里的函数作用域为这个块。ECMAScript 6之前不建议块级函数在严格模式下使用。

"use strict";

function f() {
  return 1;
}

{
  function f() {
    return 2;
  }
}

f() === 1; // true

// f() === 2 在非严格模式下相等

注意:在非严格模式下不要用块级函数,因为在非严格模式下,块中函数的声明表现奇怪,有兼容性风险

if (shouldDefineZero) {
   function zero() {     // DANGER: 兼容性风险
      console.log("This is zero.");
   }
}

ECMAScript 6中,如果shouldDefineZero是false,则永远不会定义zero,因为这个块不执行。这是新标准定义的。然而,这里存在历史遗留问题,无论这个块是否执行,一些浏览器会定义zero。

在严格模式下,所有支持ECMAScript 6的浏览器以相同的方式处理:只有在shouldDefineZero为true的情况下定义zero,并且作用域只是这个块内。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97515.html

相关文章

  • ES6学习 第一章 let 和 const 命令

    摘要:外层作用域不报错正常输出块级作用域与函数声明规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。规定,块级作用域之中,函数声明语句的行为类似于,在块级作用域之外不可引用。同时,函数声明还会提升到所在的块级作用域的头部。 前言:最近开始看阮一峰老师的《ECMAScript 6 入门》(以下简称原...

    番茄西红柿 评论0 收藏2637
  • ES6系列文章 块级作用

    摘要:声明之函数作用域和全局作用域。块级作用域不能重复声明临时性死区等特性用来解决变量存在的种种问题。块级作用域终于在外面访问不到了。一些常量声明使用声明的变量名全部大写。 ES5之前javascript语言只有函数作用域和全局作用域,使用var来声明变量,var声明的变量还存在变量提升使人困惑不已。我们先来复习一下ES5的var声明,再对比学习let和const 。 var var声明之函...

    赵连江 评论0 收藏0
  • ES6基础

    一、块级作用域 1. var 首先看看ES5中得变量声明方式 if (true) { var a = 2 } console.log(a) // 2 以上代码等同于 var a if (true) { a = 2 } console.log(a) 以上可知 : 在块内部定义变量 变量提升,到函数最顶部 通过var声明的变量,无论在何处声明,均为全局作用域 2.let 和 ...

    BigTomato 评论0 收藏0
  • javascript---闭包

    摘要:要理解闭包,首先必须理解特殊的变量作用域。使用闭包有一个优点,也是它的缺点就是可以把局部变量驻留在内存中,可以避免使用全局变量。 js闭包 闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。要理解闭包,首先必须理解Javascript特殊的变量作用域。变量的作用域无非就是两种:全局变量和局部变量...

    stormgens 评论0 收藏0
  • 大白话解释作用域和闭包是个啥 作用域的分类 常见的变量作用域就是 静态作用域(词法作用域) 与 动态作用域 。词法作用域注重的是 write-time ,即 编程时的上下文 ,而 动态作用域 则注重的是 run-time ,即 运行时上下文 。词法作用域中我们需要知道一个函数 在什么地方被定义 ,而动态作用域中我们需要关心的是函数 在什么地方被调用 而 javascript 使用的则是词法作用域 ...

    Jeffrrey 评论0 收藏0

发表评论

0条评论

荆兆峰

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<