重学js(ES6语法之字面量增强,解构,let/const)

68 阅读6分钟

字面量增强的写法(Enhanced object literals)

  • 属性的简写:Property Shorthand
  • 方法的简写:Method Shorthand
  • 计算属性名:Computed Property Names
    var name = "why"
    var age = 18
    // 1.property shorthand(属性的简写) 普通写法
    var obj = {
        name: name
        age: age
    }
    // 增强写法 
    var obj = {
        name,
        age
    }
    
    // 2.method shorthand(方法的简写) 普通写法
    var obj = {
        foo: function() {}
    }
  // 增强写法
    var obj = {
        foo() {},
    }
  //和这种写法不一样 这种写法指的是使用箭头函数 
   baz: () => {
    console.log(this)   //windows
  },
    
    //  3.computed property name(计算属性名)
    var name = "test"; 
    var obj = {}
    obj[name] = "123"; //如果key是一个变量 不能用.的方式 要使用[]
    // 增强写法
    var obj = {
      [name]: "123"
    }

解构(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 [, item2, item3] = names

console.log(item4)
    var ages = [1, 2, 3, 4, 5];
// 合并两个数组
var arr = [...names, ...ages]

对象的解构

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)

// // 解构出某个key 并且重新命名 var { name, age: newAge, height } = obj; 
// 不用delete 如何删除 对象的某个属性delete obj.a  
var { name, ...rest } = obj // 删除了name ,  obj = rest; 
// 合并两个对象 相同的属性会被覆盖 var obj2 = {...obj, ...obj1}

var/let/const的基本使用

var

-   没有块级作用域,是弱类型,支持变量提升。
-   可以重复声明,没有报错和警告
-   ES5中只有全局作用域和函数作用域,大部分人会采用闭包来解决ES5的问题

let

-   只有块级作用域 window无法访问

-   不存在变量提升(解析阶段会被创建出来,但是不能被访问)
>   Reference(引用)Error: Cannot access 'foo' before initialization(初始化)
>    let/const他们是没有作用域提升
>    foo被创建出来了, 但是不能被访问 作用域提升: 能提前被访问
>    

image.png

-   存在暂时性死区(TDZ)

    ```
    //"暂时性死区"也意味着**typeof**不再是一个百分之百安全的操作。
    //在没有**let**之前,**typeof**是百分百安全的,现在这一点不成立了.
    typeof x; //ReferenceError: a is not defined.
    let x; 
    ```
-   不允许重复声明

const

-   只有块级作用域 window无法访问
-   不存在变量提升(解析阶段会被创建出来,但是不能被访问)
-   存在暂时性死区(TDZ)
-   不允许重复声明
-   声明时必须赋值,且后续不允许修改,如果是对象,对象的属性可以被修改。const本质上是传递的值不可以修改 但是如果传递的是一个引用类型(内存地址), 可以通过引用找到对应的对象, 去修改对象内部的属性,
  • 实际开发如何选择

建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。 一个是const可以提醒阅读程序的人,这个变量不应该改变;另一个是const比较符合函数式编程思想,运 算不改变值,只是新建值,而且这样也有利于将来的分布式运算;最后一个原因是 JavaScript 编译器会对>const进行优化,所以多使用const,有利于提高程序的运行效率,也就是说letconst的本质区>别,其实是编译器内部的处理不同.

let-const和window关系

image.png 变量被保存到VariableMap中,比如v8中其实是通过VariableMap的一个hashmap来实现它们的存储的。 那么window对象呢?而window对象是早期的GO对象,在最新的实现中其实是浏览器添加的全局对象,并且 一直保持了window和var之间值的相等性;现在window是在浏览器实现,v8不实现

image.png

// var foo = "foo"
// var message = "Hello World"

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

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

let foo = "foo"

作用域

在ES6之前,只存在全局作用域和函数作用域。

var text = "全局作用域"; 
function foo() { var bar = "函数作用域,只有在这个函数才能访问" }

image.png

ES6的时候有了块级作用域

{
var x = 123; 
let y = 456;
const z = 789;
} 
// 只有x 能正常被打印 console.log(x,y,z);
// 对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-switch-for块级代码

// 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 (let i = 0; i < btns.length; i++) {
  btns[i].onclick = function() {
    console.log("第" + i + "个按钮被点击")
  }
}

// console.log(i)

块级作用域的补充

const names = ["abc", "cba", "nba"]

// 不可以使用const
// for (let i = 0; i < names.length; i++) {
//   console.log(names[i])
// }

// {
//   let i = 0
//   console.log(names[i])
// }

// {
//   let i = 1
//   console.log(names[i])
// }

// {
//   let i = 2
//   console.log(names[i])
// }

// for...of: ES6新增的遍历数组(对象)
for (const item of names) {
  console.log(item)
}

// {
//   const item = "abc"
//   console.log(item)
// }

// {
//   const item = "cba"
//   console.log(item)
// }

// console.log(item)

暂时性死区

在ES6中,我们还有一个概念称之为暂时性死区: p它表达的意思是在一个代码中,使用let、const声明的变量,在声明之前,变量都是不可以访问的; p我们将这种现象称之为 temporal dead zone(暂时性死区,TDZ)

var foo='foo'
if(true){
console.log(foo)
let foo ="bar"
}
//函数暂时性死区
function bar(){
console.log(boo)
let boo='123'
}

var、let、const的选择

那么在开发中,我们到底应该选择使用哪一种方式来定义我们的变量呢? 对于var的使用: 
我们需要明白一个事实,var所表现出来的特殊性:比如作用域提升、window全局对象、没有块级作用域等都是一些 历史遗留问题; 
其实是JavaScript在设计之初的一种语言缺陷; 
当然目前市场上也在利用这种缺陷出一系列的面试题,来考察大家对JavaScript语言本身以及底层的理解; 
但是在实际工作中,我们可以使用最新的规范来编写,也就是不再使用var来定义变量了;
对于letconst来说,是目前开发中推荐使用的;
我们会优先推荐使用const,这样可以保证数据的安全性不会被随意的篡改;
只有当我们明确知道一个变量后续会需要被重新赋值时,这个时候再使用let;
这种在很多其他语言里面也都是一种约定俗成的规范,尽量我们也遵守这种规范