es6学习笔记-let,const和块级作用域_v1.0_byKL

云计算扫地僧
• 阅读 1612

es6学习笔记-let,const和块级作用域_v1.0

块级作用域

  • javascript 原来是没有块级作用域的,只有全局作用域和函数作用域

例子1

因为没有块级作用域,所以每次的i都是一样

var a = []; 
for (var i = 0; i < 10; i++) {//变量i是var声明的,在全局范围内都有效
    a[i] = function () {
        console.log(i); //每次的i变化都是全局的,所以每一个i的值都会变成最终的10
    };
}
a[1](); //返回10
a[6](); //返回10

例子2

这里可以跟以前在<js高程3>里面有一个闭包的例子一起理解

function createFunctions() {
        var result = new Array();
        for (var i = 0; i < 10; i++) {
            result[i] = function () { //这是一个闭包
                //因为闭包保存的是整个createFunctions变量对象,所以当他执行完成的时候(for循环结束),
                //i是等于10的,所以就会是10,由始至终,闭包里面引用的都是一整个变量对象,而不是一个变量
                return i;
            };
        }
        return result; //返回的是一个数组,数组里面每一个项目都是一个function
    }
    var test = createFunctions();
    for (var i = 0; i < 10; i++) {
        //需要执行这个 function 才能够获取里面的值
        console.log(test[i]());//都是10 
    }
//看看区别,这里会传入一个参数来避免使用变量对象的参数
      function createFunctions() {
        var result = new Array();
        for (var i = 0; i < 10; i++) {
            result[i] = function (num) {//这是一个匿名函数,参数是 num
                return function () {// 这是一个闭包,不过这个闭包访问的num是我们传入的num,即使闭包保存一整个变量对象,但是我们将变量对象改成了外面这个匿名函数
                    return num;     //相当于在闭包外面包了一层活动对象,将活动对象改变成能够改变值 num的活动对象
                };
            }(i);//这个匿名函数会立即执行,并且传入了 i 作为 num
        }
        return result; //返回的是一个数组,数组里面每一个项目都是一个function
    }
    var test = createFunctions();
    for (var i = 0; i < 10; i++) {
        //需要执行这个 function 才能够获取里面的值
        console.log(test[i]());//返回0-9
    }

虽然这里是说闭包保存的是整个变量对象,导致了i的值跟着变量对象一起变化,不过也是殊途同归,这里只是替换为做全局对象来做例子

例子3

这里还原到真实的应用

<button class="button">
    a
</button>
<button class="button">
    b
</button>
<button class="button">
    c
</button>
<button class="button">
    d
</button>

<p id="output"></p>
//这里运行报错:Uncaught TypeError: Cannot read property 'innerText' of undefined
    at HTMLButtonElement.<anonymous> (
 var buttons = document.querySelectorAll('.button');
 var output = document.querySelector('#output');

 for(var i =0;i<buttons.length;++i){
 //注意到这里就是因为i的没有块级作用域,都是使用全局作用域导致问题
     buttons[i].addEventListener('click',function (i) {
     //这里的i会出现4的情况,因为buttons的长度是4,但是数组的话是0-3,没有4,所以报错
             output.innerText = buttons[i].innerText;
     },false)
 }

//需要改成这样才能正常
 var buttons = document.querySelectorAll('.button');
 var output = document.querySelector('#output');

 for(var i =0;i<buttons.length;++i){
 //需要改成这样,单独使用一个新的内部函数作用域的变量来保持顺序
     buttons[i].addEventListener('click',function (num) {
         return function () {
             output.innerText = buttons[num].innerText;
         }
     }(i),false)
 }

总的来说,那么在以前的js里面要实现块级作用域的话:

  • 要么使用函数作用域,例如在函数里面重新定义变量

  • 要么就使用类似闭包的方式,使用独立的变量

let

  • let的作用就是将变量保持在块级作用域里面,那就没有了跟其他作用域互抢的情况了.

  • var会出现变量提升的情况(变量可以在声明前使用),但是let没有

  • 不允许重复声明(var可以,let不可以)

改成用let之后


var a = [];
for (let i = 0; i < 10; i++) {
    a[i] = function () {
        console.log(i);
    };
}
a[1](); // 返回1
a[6](); // 返回6

其他例子也是可以简单的改成let代替var就可以生效了.

块级作用域与函数声明

  • es5规定函数只能在顶层作用域和函数作用域之中声明,不能再块级作用域声明(一般模式可以使用,但是严格模式不可以使用)

  • es6规定,明确允许在块级作用域之中声明函数

但是考虑到旧代码的问题,为了减轻因此产生的不兼容问题,ES6在附录B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式

考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

// 函数声明语句
{
  let a = 'secret';
  function f() {
    return a;
  }
}

// 函数表达式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

for循环

for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。

for (let i = 0; i < 3; i++) { //i在for循环变化
  let i = 'abc'; //然后for循环内部的i也设置了一个
  console.log(i);
}
// abc
// abc
// abc

上面代码输出了3次abc,这表明函数内部的变量i和外部的变量i是分离的。

const

const声明一个只读的常量。一旦声明,常量的值就不能改变。

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

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

  • 于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了

const foo = {};

// 为 foo 添加一个属性,可以成功,虽然设置了常量,但是里面的属性还是能够改变
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错,这里看到常量还是设置成功的
foo = {}; // TypeError: "foo" is read-only

如果需要将对象冻结,保证为一个真正的"常量",需要用Object.freeze,而且为了保证排除对象里面包含对象的情况,需要递归冻结

//普通冻结
const foo = Object.freeze({});

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

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

总而言之:

  1. 一般情况下,使用const来定义值的存储容器(变量)

  2. 只有在值容器明确地被确定将会被改变时才使用let来定义变量

  3. 不在使用var

参考引用:

  1. es6-函数的扩展

  2. es实战2015

点赞
收藏
评论区
推荐文章
Dax Dax
4年前
JS核心原理理解闭包
前置概念在正式看闭包之前,我们先来学习一下前置知识,那就是JS中的作用域,我们知道,在ES5之中,作用域分为两种:全局作用域和函数作用域,随着ES6的到来,新增了块级作用域,想更好的理解闭包,那么搞清楚作用域是首要条件全局作用域我们知道,对于变量而言,我们一般会分成两类:全局变量和局部变量,一般定义在最外围环境的为全局变量,定义在函数当中的为局部变量,在we
Jacquelyn38 Jacquelyn38
4年前
你所知道的JS变量作用域
变量的作用域,指的是变量在脚本代码中的可读、可写的有效范围,也就是脚本代码中可以使用这个变量的区域。在ES6之前,变量的作用域主要分为全局作用域、局部作用域(也称函数作用域)两种;在ES6及其之后,变量的作用域主要分为全局作用域、局部作用域、块级作用域这3种。相应作用域变量分别称为全局变量、局部变量、块级变量。全局变量声明在所有函数之外;局部变量是在函数体内
Stella981 Stella981
4年前
ES6 新特性之 let, const : JavaScript在变量方面的改进。
let:块级作用域我们知道,JavaScript是没有块级作用域的,如果在块内使用var声明一个变量,它在代码块外面仍旧是可见的:if(true){varfoo3;}console.log(foo);//3for(vari0
Wesley13 Wesley13
4年前
ES6新增内容(部分)
ES6新增内容(部分)一、两个声明变量的方法let、constlet:不能重复声明、有暂时性死区,不能提前访问、{}块级作用域。const:声明常量、声明之后不能被修改。二、箭头函数语法:(参数){表达式}箭头函数中this没有固定指向,一般指向宿主对象。
Wesley13 Wesley13
4年前
ES6编程风格
1、块级作用域使用let和const2、解构赋值letarr\1,2,3,4,5,6,7,8,9\;let\a,b,c\arr;letobj{foo:'aaa',bar:'bbb'};let{bar,foo}obj;//bar等于'bbb',foo等于'aaa'(
Stella981 Stella981
4年前
ES6的let命令(二)
2.暂时性死区暂时性死区(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.51code.com%2F)是指只要块级作用域内存在let命令,它所声明的变量就绑定这个作用域,不会受到外部的影响。       varnum12;    if(true){    nu
Wesley13 Wesley13
4年前
ES6 简单整理
1.变量声明let和constlet与const都是块级作用域,letfunctionname(){letage12;//age只在name()函数中存在}constconstname'tom'name'jack'//
Stella981 Stella981
4年前
ES6+(前端面试题整合)
谈一谈let与var和const的区别let为ES6新添加申明变量的命令,它类似于var,但是有以下不同:let命令不存在变量提升,如果在let前使用,会导致报错let暂时性死区的本质,其实还是块级作用域必须“先声明后使用”的性质,let暂时性死区的原因:var会变量提升,let不会。
Stella981 Stella981
4年前
JavaScript作用域
一、JavaScript中无块级作用域在Java或C中存在块级作用域,即:大括号也是一个作用域。!(https://oscimg.oschina.net/oscnet/ea3e9460a4d20056c59315db47e2a0cbc2b.jpg)!(https://oscimg.oschina.ne
Wesley13 Wesley13
4年前
JS作用域和变量提升看这一篇就够了
作用域是JS中一个很基础但是很重要的概念,面试中也经常出现,本文会详细深入的讲解这个概念及其他相关的概念,包括声明提升,块级作用域,作用域链及作用域链延长等问题。什么是作用域第一个问题就是我们要弄清楚什么是作用域,这不是JS独有的概念,而是编程领域中通用的一个概念。我们以下面这个语句为例:letx1;这
Wesley13 Wesley13
4年前
ES6的语法
一,定义变量let(类似var)在js一直有一个bug是var:1、var声明的变量会有变量提升console.log(name);//jhonvarname'jhon';2、var没有块级作用域varname2'jjjon';{varname2'tom';