前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小邵教你玩转ES6(一)-let,const和解构赋值

小邵教你玩转ES6(一)-let,const和解构赋值

作者头像
前端迷
发布2019-12-03 16:40:52
5840
发布2019-12-03 16:40:52
举报
文章被收录于专栏:前端迷前端迷

这篇文章主要让你学会工作当中常用的es6技巧,以及扩展如实现数据双向绑定,class用es5如何实现、如何给伪数组添加迭代器等等。

前言:

大家好,我叫邵威儒,大家都喜欢喊我小邵,学的金融专业却凭借兴趣爱好入了程序猿的坑,从大学买的第一本vb和自学vb,我就与编程结下不解之缘,随后自学易语言写游戏辅助、交易软件,至今进入了前端领域,看到不少朋友都写文章分享,自己也弄一个玩玩,以下文章纯属个人理解,便于记录学习,肯定有理解错误或理解不到位的地方,意在站在前辈的肩膀,分享个人对技术的通俗理解,共同成长!

后续我会陆陆续续更新javascript方面,尽量把javascript这个学习路径体系都写一下 包括前端所常用的es6、angular、react、vue、nodejs、koa、express、公众号等等 都会从浅到深,从入门开始逐步写,希望能让大家有所收获,也希望大家关注我~

这篇文章主要让你学会工作当中常用的es6技巧,以及扩展如实现数据双向绑定,class用es5如何实现、如何给伪数组添加迭代器等等。

正文:

var、let、const
代码语言:javascript
复制
// 1.var存在变量作用域的提升
console.log(a) // 打印输出 undefined
var a = 1

// 怎么理解作用域的提升呢?
// var str = 'hello swr'
// function(){
//     console.log(str) // 打印输出 undefined
//     var str = 'goodbye swr'
// }
// test()

// 上面这段代码实际上是
var str = 'hello swr'
function(){
    var str
    console.log(str) // 打印输出undefined
                     // 实际上就是var声明的变量,拿到
                     // 当前作用域的最顶层,而此时尚未赋值
                     // 只是声明,所以打印出undefined,而非当运行
                     // 到这段代码时才声明,优先声明,
                     // 当运行到那行的时候,实际上是赋值
                     // 同样的,function xx(){}也存在作用域提升
    str = 'goodbye swr'
}
test()

// var 不存在块级作用域的概念
// 我的理解是在es6之前,是没有块级作用域的概念,
// 变量只有遇到函数的时候才会变为局部变量
{
    var str 1 = 'hello swr'
}

console.log(str1) // 打印输出 hello swr
代码语言:javascript
复制
// 2.let
// 2.1 不存在变量作用域提升,这样可以避免了我们还没声明变量就拿变量来用
// 2.2 同一作用域的同一个变量不能够重复声明,避免我们重复声明变量
// 2.3 let声明的变量不会到全局上
// 2.4 let和代码块{}结合使用会形成块级作用域

// 2.1
// console.log(a) // 报错,a未声明
// let a = 'hello swr'

// 2.2
// let a = 'hello swr'
// let a = 'hello swr' // 报错,变量被重复声明

// 2.3
// let a = 'hello swr'
// console.log(window.a) // undefined

// 2.4
// 在代码块以外调用str2,会报错
{
    let str2 = 'hello swr'
}

console.log(str2) // 报错,未找到变量

// 上面这种写法,也有点类型es6之前的立即执行函数
(function(){
    var str2 = 'hello swr'
})()

// 一个例子
// 使用var,会发现最终console.log中打印的i都是3
// 因为for循环不是函数,而此时var i是处于全局当中
// for循环是同步代码,所以会执行完同步代码后
// 再执行setTimeout的异步代码,此时i已为3,所以打印出来都是3
for(var i = 0;i < 3;i++){
    setTimeout(function(){
        console.log(i)
    },1000)
}

// 那么我们用let试下
// let和代码块结合起来使用会形成块级作用域
// 那么当for时,这3个setTimeout会分别在3个不同的块级作用域
// 当执行setTimeout的console.log(i)时,会先寻找最近的块级作用域中的i
// 所以会依次打印出0 1 2
for(let j = 0;j < 3;j++){
    setTimeout(function(){
        console.log(i)
    },1000)
}
代码语言:javascript
复制
// 3.const
// 3.1 const和let基本上可以说是完全一致的,
//但是const声明的对象不能更改其指向的引用地址(即堆区)


// 3.1
// 当用普通值赋值给const声明的变量后,再重新赋值时
// 值引用会被更改,所以会报错
const STR1 = 'hello swr'
STR1 = 'goodbye swr' // 报错,Assignment to constant variable

// 当我们修改这个引用地址里面的内容时,则不会报错
// 因为这个变量是指向这个引用地址的
const OBJ = {name:"swr"}
OBJ.name = 'hello swr'
console.log(OBJ) // {name:"hello swr"}
// 但是当我们把这个变量重新赋值一个引用地址时,则会报错
OBJ = {} // 报错
解构赋值

解构赋值主要分为对象的解构和数组的解构,在没有解构赋值的时候,我们赋值是这样的

代码语言:javascript
复制
let arr = [0,1,2]
let a = arr[0]
let b = arr[1]
let c = arr[2]

这样写很繁琐,那么我们有没办法既声明,又赋值,更优雅的写法呢?肯定是有的,那就是解构赋值,解构赋值,简单理解就是等号的左边和右边相等。

数组的解构赋值
代码语言:javascript
复制
let arr = [0,1,2]
let [a,b,c] = arr
console.log(a) // 0
console.log(b) // 1
console.log(c) // 2

但是很多时候,数据并非一一对应的,并且我们希望得到一个默认值

代码语言:javascript
复制
let arr = [,1,2]
let [a='我是默认值',b,c] = arr
console.log(a) // '我是默认值'
console.log(b) // 1
console.log(c) // 2
// 从这个例子可以看出,在解构赋值的过程中,a=undefined时,会使用默认值
// 那么当a=null时呢?当a=null时,那么a就不会使用默认值,而是使用null
代码语言:javascript
复制
// 数组的拼接
let a = [0,1,2]
let b = [3,4,5]
let c = a.concat(b)
console.log(c) // [0,1,2,3,4,5]

let d = [...a,...b]
console.log(d) // [0,1,2,3,4,5]
代码语言:javascript
复制
// 数组的克隆
// 假如我们简单地把一个数组赋值给另外一个变量
let a = [0,1,2,3]
let b = a
b.push(4)
console.log(a) // [0,1,2,3,4]
console.log(b) // [0,1,2,3,4]
// 因为这只是简单的把引用地址赋值给b,而不是重新开辟一个内存地址,所以
// a和b共享了同一个内存地址,该内存地址的更改,会影响到所有引用该地址的变量
// 那么用下面的方法,把数组进行克隆一份,互不影响

let a = [0,1,2,3]
let b = [...a]
b.push(4)
console.log(a) // [0,1,2,3]
console.log(b) // [0,1,2,3,4]
对象的解构赋值

对象的解构赋值和数组的解构赋值其实类似,但是数组的数组成员是有序的 而对象的属性则是无序的,所以对象的解构赋值简单理解是等号的左边和右边的结构相同

代码语言:javascript
复制
let {name,age} = {name:"swr",age:28}
console.log(name) // 'swr'
console.log(age) // 28

对象的解构赋值是根据key值进行匹配

代码语言:javascript
复制
// 这里可以看出,左侧的name和右侧的name,是互相匹配的key值
// 而左侧的name匹配完成后,再赋值给真正需要赋值的Name
let { name:Name,age } = { name:'swr',age:28 }
console.log(Name) // 'swr'
console.log(age) // 28

那么当变量已经被声明了呢?

代码语言:javascript
复制
let name,age
// 需要用圆括号,包裹起来
({name,age} = {name:"swr",age:28})
console.log(name) // 'swr'
console.log(age) // 28

变量能否也设置默认值?

代码语言:javascript
复制
let {name="swr",age} = {age:28}
console.log(name) // 'swr'
console.log(age) // 28
// 这里规则和数组的解构赋值一样,当name = undefined时,则会使用默认值
代码语言:javascript
复制
let [a] = [{name:"swr",age:28}]
console.log(a) // {name:"swr",age:28}

let { length } = "hello swr"
console.log(length) // 9
代码语言:javascript
复制
function ajax({method,url,type='params'}){
    console.log(method) // 'get'
    console.log(url) // '/'
    console.log(type) // 'params'
}

ajax({method:"get",url:"/"})
扩展运算符

我们先看下代码

代码语言:javascript
复制
// 在以往,我们给函数传不确定参数数量时,是通过arguments来获取的
function sum() {
  console.log(arguments) 
  // { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6 }
  // 我们可以看出,arguments不是一个数组,而是一个伪数组
  let total = 0
  let { length } = arguments
  for(let i = 0;i < length;i++){
    total += arguments[i]
  }
  return total
}

console.log(sum(1,2,3,4,5,6)) // 21
代码语言:javascript
复制
// 接下来我们用扩展运算符看看
function sum(...args){ // 使用...扩展运算符
    console.log(args) // [ 1, 2, 3, 4, 5, 6 ] args是一个数组
    return eval(args.join('+'))
}

console.log(sum(1,2,3,4,5,6)) // 21

得到的args是一个数组,直接对数组进行操作会比对伪数组进行操作更加方便,还有一些注意点需要注意

代码语言:javascript
复制
// 正确的写法 扩展运算符只能放在最后一个参数
function sum(a,b,...args){
    console.log(a) // 1
    console.log(b) // 2
    console.log(args) // [ 3, 4, 5, 6 ]
}

sum(1,2,3,4,5,6)

// 错误的写法 扩展运算符只能放在最后一个参数
function sum(...args,a,b){
    // 报错
}

sum(1,2,3,4,5,6)

我们可以对比下扩展运算符的方便之处

代码语言:javascript
复制
// 以往我们是这样拼接数组的
let arr1 = [1,2,3]
let arr2 = [4,5,6]
let arr3 = arr1.concat(arr2)
console.log(arr3) // [ 1, 2, 3, 4, 5, 6 ]

// 现在我们用扩展运算符看看
let arr1 = [1,2,3]
let arr2 = [4,5,6]
let arr3 = [...arr1,...arr2]
console.log(arr3) // [ 1, 2, 3, 4, 5, 6 ]
代码语言:javascript
复制
// 以往我们这样来取数组中最大的值
function max(...args){
    return Math.max.apply(null,args)
}
console.log(max(1,2,3,4,5,6)) // 6

// 现在我们用扩展运算符看看
function max(...args){
    return Math.max(...args) 
    // 把args [1,2,3,4,5,6]展开为1,2,3,4,5,6
}
console.log(max(1,2,3,4,5,6)) // 6
代码语言:javascript
复制
// 扩展运算符可以把argument转为数组
function max(){
    console.log(arguments) // { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6 }
    let arr = [...arguments]
    console.log(arr) // [1,2,3,4,5,6]
}

max(1,2,3,4,5,6)

// 但是扩展运算符不能把伪数组转为数组(除了有迭代器iterator的伪数组,如arguments)
let likeArr = { "0":1,"1":2,"length":2 }
let arr = [...likeArr] // 报错 TypeError: likeArr is not iterable

// 但是可以用Array.from把伪数组转为数组
let likeArr = { "0":1,"1":2,"length":2 }
let arr = Array.from(likeArr)
console.log(arr) // [1,2]

对象也可以使用扩展运算符

代码语言:javascript
复制
// 以往我们这样合并对象
let name = { name:"邵威儒" }
let age = { age:28 }
let person = {}
Object.assign(person,name,age)
console.log(person) // { name: '邵威儒', age: 28 }

// 使用扩展运算符
let name = { name:"邵威儒" }
let age = { age:28 }
let person = {...name,...age}
console.log(person) // { name: '邵威儒', age: 28 }

需要注意的是,通过扩展运算符和Object.assign对对象进行合并的行为,是属于浅拷贝,那么我们在开发当中,经常需要对对象进行深拷贝,接下来我们看看如何进行深拷贝。

代码语言:javascript
复制
// 方法一:利用JSON.stringify和JSON.parse
let swr = {
    name:"邵威儒",
    age:28
}

let swrcopy = JSON.parse(JSON.stringify(swr))
console.log(swrcopy) // { name:"邵威儒",age:28 }
// 此时我们修改swr的属性
swr.age = 29
console.log(swr) // { name:"邵威儒",age:29 }
// 但是swrcopy却不会受swr影响
console.log(swrcopy) // { name:"邵威儒",age:28 }
// 这种方式进行深拷贝,只针对json数据这样的键值对有效
// 对于函数等等反而无效,不好用,接着继续看方法二、三。
代码语言:javascript
复制
// 方法二:
function deepCopy(fromObj,toObj) { // 深拷贝函数
  // 容错
  if(fromObj === null) return null // 当fromObj为null
  if(fromObj instanceof RegExp) return new RegExp(fromObj) // 当fromObj为正则
  if(fromObj instanceof Date) return new Date(fromObj) // 当fromObj为Date

  toObj = toObj || {}

  for(let key in fromObj){ // 遍历
    if(typeof fromObj[key] !== 'object'){ // 是否为对象
      toObj[key] = fromObj[key] // 如果为普通值,则直接赋值
    }else{
      toObj[key] = new fromObj[key].constructor // 如果为object,则new这个object指向的构造函数
      deepCopy(fromObj[key],toObj[key]) // 递归
    }
  }
  return toObj
}

let dog = {
  name:"小白",
  sex:"公",
  firends:[
    {
      name:"小黄",
      sex:"母"
    }
  ]
}

let dogcopy = deepCopy(dog)
// 此时我们把dog的属性进行修改
dog.firends[0].sex = '公'
console.log(dog) // { name: '小白',
                   //   sex: '公',
                   //   firends: [ { name: '小黄', sex: '公' }] }
// 当我们打印dogcopy,会发现dogcopy不会受dog的影响
console.log(dogcopy) // { name: '小白',
                      //    sex: '公',
                      //    firends: [ { name: '小黄', sex: '母' } ] }
代码语言:javascript
复制
// 方法三:
let dog = {
  name:"小白",
  sex:"公",
  firends:[
    {
      name:"小黄",
      sex:"母"
    }
  ]
}

function deepCopy(obj) {
  if(obj === null) return null
  if(typeof obj !== 'object') return obj
  if(obj instanceof RegExp) return new RegExp(obj)
  if(obj instanceof Date) return new Date(obj)
  let newObj = new obj.constructor
  for(let key in obj){
    newObj[key] = deepCopy(obj[key])
  }
  return newObj
}

let dogcopy = deepCopy(dog)
dog.firends[0].sex = '公'
console.log(dogcopy)
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端迷 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 正文:
    • var、let、const
      • 解构赋值
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档