Loading

♣ 常用的ES6-解构/let/const

♠ es6

字面量的增强

ES6中对对象字面量进行了增强,称之为Enhancedobjectliterals(增强对象字面量)。

字面量的增强主要包括下面几部分:

属性的简写:PropertyShorthand 
方法的简写:MethodShorthand 
计算属性名:ComputedPropertyNames

点击查看代码
var name = "why"
var age = 18

var obj = {
  // 1.property shorthand(属性的简写)
  name,
  age,

  // 2.method shorthand(方法的简写)
  foo: function() {
    console.log(this)
  },
  bar() {
    console.log(this)
  },
  baz: () => {
    console.log(this)
  },

  // 3.computed property name(计算属性名)
  [name + 123]: 'hehehehe'
}

obj.baz()
obj.bar()
obj.foo()

// obj[name + 123] = "hahaha"
console.log(obj)

解构Destructuring

查看代码
var names = ["abc", "cba", "nba"]
// var item1 = names[0]
// var item2 = names[1]
// var item3 = names[2]

// 对数组的解构: []
var [item1, item2, item3] = names
console.log(item1, item2, item3)

// 解构后面的元素
var [, , itemz] = names
console.log(itemz)

// 解构出一个元素,后面的元素放到一个新数组中
var [itemx, ...newNames] = names
console.log(itemx, newNames)

// 解构的默认值
var [itema, itemb, itemc, itemd = "aaa"] = names
console.log(itemd)
查看代码
var obj = {
  name: "why",
  age: 18,
  height: 1.88
}

// 对象的解构: {}
var { name, age, height } = obj
console.log(name, age, height)

var { age } = obj
console.log(age)

var { name: newName } = obj
console.log(newName)

var { address: newAddress = "广州市" } = obj
console.log(newAddress)


function foo(info) {
  console.log(info.name, info.age)
}

foo(obj)

function bar({name, age}) {
  console.log(name, age)
}

bar(obj)

let/const

在ES5中我们声明变量都是使用的var关键字,从ES6开始新增了两个关键字可以声明变量:let、const

  • let、const在其他编程语言中都是有的,所以也并不是新鲜的关键字;
  • 但是let、const确确实实给JavaScript带来一些不一样的东西;

let关键字:

  • 从直观的角度来说,let和var是没有太大的区别的,都是用于声明一个变量

const关键字:

  • const关键字是constant的单词的缩写,表示常量、衡量的意思;
  • 它表示保存的数据一旦被赋值,就不能被修改;
  • 但是如果赋值的是引用类型,那么可以通过引用找到对应的对象,修改对象的内容;

注意:另外let、const不允许重复声明变量;

let、const和var的另一个重要区别是作用域提升:我们知道var声明的变量是会进行作用域提升的;但是如果我们使用let声明的变量,在声明之前访问会报错;那么是不是意味着foo变量只有在代码执行阶段才会创建的呢?事实上并不是这样的,我们可以看一下ECMA262对let和const的描述;这些变量会被创建在包含他们的词法环境被实例化时,但是是不可以访问它们的,直到词法绑定被求值;

从上面我们可以看出,在执行上下文的词法环境创建出来的时候,变量事实上已经被创建了,只是这个变量是不能被访问的。那么变量已经有了,但是不能被访问,是不是一种作用域的提升呢?事实上维基百科并没有对作用域提升有严格的概念解释,那么我们自己从字面量上理解;作用域提升:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么我们可以称之为作用域提升;在这里,它虽然被创建出来了,但是不能被访问,我认为不能称之为作用域提升;所以我的观点是let、const没有进行作用域提升,但是会在解析阶段被创建出来。

查看代码
// var foo = "foo"
// let bar = "bar"

// const constant(常量/衡量)
// const name = "abc"
// name = "cba"

// 注意事项一: const本质上是传递的值不可以修改
// 但是如果传递的是一个引用类型(内存地址), 可以通过引用找到对应的对象, 去修改对象内部的属性, 这个是可以的
// const obj = {
//   foo: "foo"
// }

// // obj = {}
// obj.foo = "aaa"
// console.log(obj.foo)


// 注意事项二: 通过let/const定义的变量名是不可以重复定义
// var foo = "abc"
// var foo = "cba"

let foo = "abc"
// SyntaxError: Identifier 'foo' has already been declared
let foo = "cba"

console.log(foo)
查看代码
//作用域提升
// console.log(foo)
// var foo = "foo"

// Reference(引用)Error: Cannot access 'foo' before initialization(初始化)
// let/const他们是没有作用域提升
// foo被创建出来了, 但是不能被访问
// 作用域提升: 能提前被访问
console.log(foo)
let foo = "foo"

Window对象添加属性

我们知道,在全局通过var来声明一个变量,事实上会在window上添加一个属性:

但是let、const是不会给window上添加任何属性的。

那么我们可能会想这个变量是保存在哪里呢?最新的ECMA标准中对执行上下文的描述

变量被保存到VariableMap中

也就是说我们声明的变量和环境记录是被添加到变量环境中的:但是标准有没有规定这个对象是window对象或者其他对象呢?

其实并没有,那么JS引擎在解析的时候,其实会有自己的实现;

比如v8中其实是通过VariableMap的一个hashmap来实现它们的存储的。

那么window对象呢?而window对象是早期的GO对象,在最新的实现中其实是浏览器添加的全局对象,并且一直保持了window和var之间值的相等性;

查看代码
// var foo = "foo"
// var message = "Hello World"

// console.log(window.foo)
// console.log(window.message)

// window.message = "哈哈哈"
// console.log(message)

let foo = "foo"

var的块级作用域

查看代码
// 声明对象的字面量
// var obj = {
//   name: "why"
// }

// ES5中没有块级作用域
// 块代码(block code)
// {
//   // 声明一个变量
//   var foo = "foo"
// }

// console.log(foo)


// 在ES5中只有两个东西会形成作用域
// 1.全局作用域
// 2.函数作用域
// function foo() {
//   var bar = "bar"
// }

// console.log(bar)

function foo() {

  function demo() {
    
  }

}

let/const的块级作用域

在ES6中新增了块级作用域,并且通过let、const、function、class声明的标识符是具备块级作用域的限制的:

查看代码
// ES6的代码块级作用域
// 对let/const/function/class声明的类型是有效
{
  let foo = "why"
  function demo() {
    console.log("demo function")
  }
  class Person {}
}

// console.log(foo) // foo is not defined
// 不同的浏览器有不同实现的(大部分浏览器为了兼容以前的代码, 让function是没有块级作用域)
// demo()
var p = new Person() // Person is not defined
查看代码
{

}

// if语句的代码就是块级作用域
// if (true) {
//   var foo = "foo"
//   let bar = "bar"
// }

// console.log(foo)
// console.log(bar)

// switch语句的代码也是块级作用域
// var color = "red"

// switch (color) {
//   case "red":
//     var foo = "foo"
//     let bar = "bar"
// }

// console.log(foo)
// console.log(bar)

// for语句的代码也是块级作用域
// for (var i = 0; i < 10; i++) {
//   // console.log("Hello World" + i)
// }

// console.log(i)

for (let i = 0; i < 10; i++) {
}

console.log(i)

块级作用域的应用

查看代码
const btns = document.getElementsByTagName('button')

// for (var i = 0; i < btns.length; i++) {
//   (function (n) {
//     btns[i].onclick = function () {
//       console.log("第" + n + "个按钮被点击")
//     }
//   })(i)
// }

//console.log(i)

for (var i = 0; i < btns.length; i++) {
  btns[i].onclick = function () {
    console.log("第" + i + "个按钮被点击")
  }
}

//console.log(i)
查看代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <button>按钮1</button>
  <button>按钮2</button>
  <button>按钮3</button>
  <button>按钮4</button>
  <script src="./块级作用域的应用场景.js"></script>
</body>
</html>

var、let、const的选择

那么在开发中,我们到底应该选择使用哪一种方式来定义我们的变量呢?对于var的使用:

  • 我们需要明白一个事实,var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些历史遗留问题;
  • 其实是JavaScript在设计之初的一种语言缺陷;
  • 当然目前市场上也在利用这种缺陷出一系列的面试题,来考察大家对JavaScript语言本身以及底层的理解;
  • 但是在实际工作中,我们可以使用最新的规范来编写,也就是不再使用var来定义变量了;

对于let、const:

  • 对于let和const来说,是目前开发中推荐使用的;
  • 我们会有限推荐使用const,这样可以保证数据的安全性不会被随意的篡改;
  • 只有当我们明确知道一个变量后续会需要被重新赋值时,这个时候再使用let;
  • 这种在很多其他语言里面也都是一种约定俗成的规范,尽量我们也遵守这种规范;

 

posted @ 2021-11-07 21:03  sunflower-js  阅读(51)  评论(0)    收藏  举报