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

Author: 邵威儒Wechat: 166661688

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

前言:

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

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

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

正文:

var、let、const
// 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
// 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)	
}
// 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 = {} // 报错
解构赋值

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

let arr = [0,1,2]	
let a = arr[0]	
let b = arr[1]	
let c = arr[2]

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

数组的解构赋值
let arr = [0,1,2]	
let [a,b,c] = arr	
console.log(a) // 0	
console.log(b) // 1	
console.log(c) // 2

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

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
// 数组的拼接	
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]
// 数组的克隆	
// 假如我们简单地把一个数组赋值给另外一个变量	
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]
对象的解构赋值

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

let {name,age} = {name:"swr",age:28}	
console.log(name) // 'swr'	
console.log(age) // 28

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

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

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

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

变量能否也设置默认值?

let {name="swr",age} = {age:28}	
console.log(name) // 'swr'	
console.log(age) // 28	
// 这里规则和数组的解构赋值一样,当name = undefined时,则会使用默认值
let [a] = [{name:"swr",age:28}]	
console.log(a) // {name:"swr",age:28}	
	
let { length } = "hello swr"	
console.log(length) // 9
function ajax({method,url,type='params'}){	
    console.log(method) // 'get'	
    console.log(url) // '/'	
    console.log(type) // 'params'	
}	
	
ajax({method:"get",url:"/"})
扩展运算符

我们先看下代码

// 在以往,我们给函数传不确定参数数量时,是通过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
// 接下来我们用扩展运算符看看	
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是一个数组,直接对数组进行操作会比对伪数组进行操作更加方便,还有一些注意点需要注意

// 正确的写法 扩展运算符只能放在最后一个参数	
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)

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

// 以往我们是这样拼接数组的	
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 ]
// 以往我们这样来取数组中最大的值	
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
// 扩展运算符可以把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]

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

// 以往我们这样合并对象	
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对对象进行合并的行为,是属于浅拷贝,那么我们在开发当中,经常需要对对象进行深拷贝,接下来我们看看如何进行深拷贝。

// 方法一:利用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数据这样的键值对有效	
// 对于函数等等反而无效,不好用,接着继续看方法二、三。
// 方法二:	
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: '母' } ] }
// 方法三:	
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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值