一、块级作用域

  1、为什么需要块级作用域?

  ES5中只有全局作用域和函数作用域,带来很多不合理的场景。

  (1)内层变量可能会覆盖外层变量;   

var tem = new Date();
function f(){
console.log(tmp);
if(false) {
var tmp = "hello world";
}
}
f(); //undefined

  变量提升导致了内层的tmp变量覆盖了外层的tmp变量。

  (2)用来计数的循环变量泄露为全局变量;

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

  2、ES6的块级作用域:

  (1)let为JavaScript新增了块级作用域;外层代码不受内层代码的影响。

function f() {
let n=5;
if(true) { let n=10; }
console.log(n); //5
}

  (2)ES6允许块级作用域任意嵌套;

{{{
{let insane = "hello world"}
console.log(insane); // 报错
}}}

  (3)内层作用域可以定义外层作用域的同名变量;

{{{
{let insane = "hello world"}
let insane = "hello world"
}}}

  (4)块级作用域 与 函数声明;

  函数本身的作用域在其所在的块级作用域之内;

function f() { console.log('I am outside!') }
(function () {
if(false) {
function f() { console.log('I am inside!') } // 重复声明一次函数f
}
f();
}());

  上述代码在ES5中运行,会得到 I am inside!   因为ES5 存在变量声明提升,不管进不进入if代码,在if内声明的函数f会被提升到当前作用域的顶部 ;

// ES5 环境
function f() { console.log('I am outside!'); }
(function () {
function f() { console.log('I am inside!'); } //提升到当前作用域的顶部
if (false) {
}
f();
}());

  但是在ES6中运行,会得到 I am outside!  因为ES6 块级作用域内声明的函数类似于let,对作用域之外没有影响。

  但是,如果你真的在 ES6 浏览器中运行一下上面的代码,是会报错的,!!!!!

  ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。
    1、允许在块级作用域内声明函数。
    2、函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
    3、同时,函数声明还会提升到所在的块级作用域的头部。
  注意,上面三条规则只对 ES6 的浏览器实现有效,其他环境的实现不用遵守,还是将块级作用域的函数声明当作let处理。
 
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
var f = undefined;
if (false) {
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function

  因此,考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。

二、let 命令

  1、仅在块级作用域内有效; 【for循环的计数器 很适合用let命令】

  let 命令用于声明变量, 与var类似, 但是所声明的变量只在let命令的代码块内有效,不受外部影响。

{
let a = 10;
var b = 20;
}
a //a is not defined
b //20

  2、不存在变量提升;变量一定在声明后使用,否则会报错。

console.log(foo);   //报错
let foo = 5;

  3、暂时性死区;

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

if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束 console.log(tmp); // undefined
tmp = 123;
console.log(tmp); //
}

  暂时性死区的本质:只要已进入当前作用域,变量就已经存在了,但是不可获取;只有等到声明变量的那一行代码出现,就可以获取和使用该变量。

  以下是一些比较隐蔽的“死区”:

function bar (x=y, y=2) {
return [x,y];
}
bar(); // 报错 此时x的默认值为参数y,而y此时还没有声明,属于“死区”
function bar (x=2, y=x) {
return [x,y];
}
bar(); //[2,2]

  4、不允许重复声明;

//报错
function(){
let a=1;
var a=2;
}
//报错
function(){
let b=1;
let b=2;
}
//报错
function fun(arg){
   let arg; //报错 不能再函数内部重新声明参数
}

三、const命令

  1、const用来声明常量, 一旦声明其值就不会改变;

const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.

  2、const一旦声明就必须立即初始化;

const foo;
// SyntaxError: Missing initializer in const declaration

  3、const只在声明所在的块级作用域内有效;

  4、const声明的常量也不会提升,同样存在暂时性死区,只能先声明后使用;

  5、const也不可以重复声明常量;

  6、const本质:

    const实际保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。

    对于简单数据类型(数字,字符串,布尔值),值就保存在变量指向的那个内存地址,因此等同于常量;

    对于复合类型的数据(对象,数组),变量指向的内存地址,const只能保证这个地址是不变的,至于它指向的数据结构是不是可变就不能控制了。

const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

  7、若想冻结对象,应该使用object.freeze方法;

const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

  8、冻结对象本身以及对象属性:

var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, value)=> {
if( typeof obj[key] === 'object' ) {
constantize( obj[key] ); // 递归
}
} )
}

  9、ES6声明变量的六种方法:

    ES5提供了两种: var命令 、function命令

    ES6新增了四种: let命令、 const命令、 import命令、 class命令

四、全局对象的属性

  全局对象 又称顶层对象,在浏览器环境指的是window对象, 在Node.js中指的是global对象。

  在ES5中,全局对象的属性 和 全局变量是等价的;

window.a = 9;
a // b=6;
window.b //

  ES6对此做了新的规定:

   1、考虑兼容性,var和function声明的全局变量 依旧是全局对象的属性;

   2、let、const、class 声明的全局变量 不属于全局对象的属性;

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // let b = 1;
window.b // undefined

五、全局对象(顶层对象)的扩展:

  ES5的顶层对象,在各种实现里是不统一的:

    浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。

    浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。

    Node 里面,顶层对象是global,但其他环境都不支持。

下面有两种勉强的方法,使同一段代码能够在各种环境,都能取到顶层对象:

//方法一:
( typeof window != 'undefined'
? window
: ( typeof process === 'object' &&
tyoeof require === 'function' &&
typeof global === 'object'
)
? global : this
); // 方法二:
var getGlobal = function() {
if(typeof self !== 'undefined') { return self }
if(typeof window !== 'undefined') { return window }
if(typeof global !== 'undefined') { return global }
throw new Error('unable to locate global object');
}

ES6标准入门 第二章:块级作用域 以及 let和const命令的更多相关文章

  1. ES6入门一:块级作用域(let&const)、spread展开、rest收集

    let声明 const声明 块级作用域 spread/rest 一.let声明与块作用域 在ES6之前,JavaScript中的作用域基本单元就是function.现在有了let就可以创建任意块的声明 ...

  2. 关于阮大神的es6标准入门第一章

    题记:之前在10月份的时候写过阮大神的es6的第一章,但是由于那段时间项目组的动荡,所以也没有什么后续,导致我现在对es6基本都忘的差不多了,不过,现在换了新公司,最近也没什么任务,所以现在开始重新写 ...

  3. ES6标准入门 第一章:简介

    ECMAScript 6 是JavaScript 语言的下一代标准:发布于2015年,又称为ECMAScript 2015. ECMAScript 与 JavaScript 的关系:前者是后者的规范, ...

  4. 12.24 ES6浅谈--块级作用域,let

    第一部分:ES6新增了块级作用域,let关键字用于声明变量,相较于var而言,let关键字不存在声明提前. 1.ES6真正的出现了块级作用域,使用双花括号括住并在其中用let声明变量,会存在暂时性死区 ...

  5. ECMAScript概述及浅谈const,let与块级作用域

    ECMAScript可以看作javascript的标准规范,实际上javascript是ECMAScript的一门脚本语言,ECMAScript只提供了最基本的语言JavaScript对ECMAScr ...

  6. 《浏览器工作原理与实践》<09>块级作用域:var缺陷以及为什么要引入let和const?

    在前面我们已经讲解了 JavaScript 中变量提升的相关内容,正是由于 JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷 ...

  7. 《ES6标准入门》(阮一峰)--2.let 和 const 命令

    1.let命令 基本用法 let只在命令所在的代码块内(花括号内)有效. for循环的计数器,就很合适使用let命令. //var var a = []; for (var i = 0; i < ...

  8. Javascript高级编程学习笔记(25)—— 函数表达式(3)模仿块级作用域

    昨天写了闭包 今天就来聊聊块级作用域的事情 在绝大多数编程语言中,都有块级作用域这个概念 什么是块级作用域呢? 前面我们在刚开始讲的时候说过,JS中的大括号(不在赋值运算符的后面)表示代码块 块级作用 ...

  9. ECMAScript6 入门教程 初学记录let命令 块级作用域

    一.基本语法-let命令 (1)ES6新增了let命令,用来声明变量.所声明的变量,只在let命令所在的代码块内有效. 循环的计数器,就很合适使用let命令.计数器i只在for循环体内有效,在循环体外 ...

随机推荐

  1. Linux下RabbitMQ安装、运行与管理

    Linux下RabbitMQ安装.运行与管理 安装erlang 安装参考官网 RabbitMQ的安装需要Erlang的基础环境,必须按照RabbitMQ Erlang版本要求进行安装. 关于Erlan ...

  2. Python 中Semaphore 信号量对象、Event事件、Condition

    Semaphore 信号量对象 信号量是一个更高级的锁机制.信号量内部有一个计数器而不像锁对象内部有锁标识,而且只有当占用信号量的线程数超过信号量时线程才阻塞.这允许了多个线程可以同时访问相同的代码区 ...

  3. flockfile, ftrylockfile, funlockfile - 为标准输入输出锁定文件 FILE

    SYNOPSIS 总览 #include <stdio.h> void flockfile(FILE *filehandle); int ftrylockfile(FILE *fileha ...

  4. Linux学习--第三天--linux文件目录、ls、mkdir、mv、rm、touch、cat、tac、more、less、head、tail、ln、chmod、chown、chgrp、umask

    文件目录 目录名 备注 bin 下面的命令所有人都可以运行 sbin 只有root才能运行,s代表super /mnt,/media,/misc 都是挂载目录,但一般只用mnt /opt 第三方软件安 ...

  5. PAT Advanced 1035 Password (20 分)

    To prepare for PAT, the judge sometimes has to generate random passwords for the users. The problem ...

  6. NTC电阻Rt与温度T关系

    NTC电阻Rt与温度T公式如下: Rt=10000*exp(3950*(1/(273.15+T)-1/(273.15+25))). 例:0摄氏度时,电阻为33620.6037214357 欧姆 Rt= ...

  7. DevExpress v18.2版本亮点——Office File API 篇

    行业领先的.NET界面控件——DevExpress v18.2版本亮点详解,本文将介绍了DevExpress Office File API v18.2 的版本亮点,新版30天免费试用!点击下载> ...

  8. ipsec概念理解

    互联网安全协议(英语:Internet Protocol Security,缩写:IPsec): 本质上一个协议包,透过对IP协议的分组进行加密和认证来保护IP协议的网络传输协议族(一些相互关联的协议 ...

  9. 【leetcode】Smallest Rotation with Highest Score

    题目如下: Given an array A, we may rotate it by a non-negative integer K so that the array becomes A[K], ...

  10. CSS盒模型面试知识点

    一.基本概念 1.基本概念:标准盒模型+怪异盒模型(IE模型) 基本组成:由margin.padding.content组成. 2.标准盒模型和IE模型的区别 标准盒模型中width指的是内容区域co ...