es6新增特性(1)

1.let和const关键字
let:

  • 变量不能重复声明。
let a = 1;
let a = 2;
//报错:Uncaught SyntaxError: Identifier 'a' has already been declared
  • 块级作用域。在if-else语句、while循环、for循环中使用了let声明的变量,都会产生块级作用域。
let flag = true
 if(flag){
    var a = 11
}
console.log(a) //11
//而把var换成let后就报错
let flag = true
 if(flag){
    let a = 11
}
console.log(a) //Uncaught ReferenceError: a is not defined
  • 没有变量提升,必须先定义才能使用。
    变量提升:在代码执行前,会先收集通过var声明的变量,把这些变量提到作用域的最顶端,而赋值操作不变。
  • let声明的变量是独立的,不能被window调用。
let a = 10
console.log(window.a) //undefined
var b = 10
console.log(window.b) //10

let doSth = function(){
      console.log('123');
}
window.doSth()//ncaught TypeError: window.doSth is not a function

var doSth = function(){
      console.log('123');
}
window.doSth()//123
  • 不会影响作用域链
{
    let a = 10
    function fun(){
        console.log(a)//10
    }
    fun()
}

1
2
3

const:

  • 初始化常量,必须给初始值,否则报错(Uncaught SyntaxError: Missing initializer in const declaration)。
  • 在同一个作用域内,const定义的常量不能修改其值。在同一个作用域内,const定义的常量不能重复声明。但是在不同的作用域,用const声明一样的变量没问题。另外,如果const声明的变量是个引用数据类型,比如对象,那么对对象进行修改的时候,不会报错。
const a = 10
a = 100 // Uncaught TypeError: Assignment to constant variable.
//const定义的常量不能重复声明
const a = 10
const a = 100//Uncaught SyntaxError: Identifier 'a' has already been declared

但是在不同的作用域,用const声明一样的常量没问题。
const a = 100
function fn(){
     const a = 200
     console.log(a)//200
}
fn()
如果const声明的变量是个引用数据类型,比如对象,那么对对象进行修改的时候,不会报错
const obj = {
     name:'yy'
}
obj.name = 'gg'
console.log(obj)
  • 没有变量提升。
  • 具有块级作用域。
  • const声明的常量也是独立的,不能被window调用
const a = 10
console.log(window.a)
  • const声明的数组和对象,对其修改不会报错
const arr = [1,2,3,4]
arr.push(5)
console.log(arr)// [1,2,3,4,5]

var/let/const的共同点:在函数内部都可以访问到在外部通过var/let/const声明的变量或常量。
2.解构赋值

//对象的解构赋值
const obj = {
        name:'yy',
        age:18,
        sayHello:function(){
            console.log('你好')
        }
    }
let {name,age,sayHello} = obj
sayHello() //'你好'

//还有数组的解构赋值,用的少就不细说了

3.箭头函数

//声明函数
let add1 = (a) =>{
    console.log(a*2)
}
//调用函数
add1(3)//6

//箭头函数与普通函数的区别
//1.this是静态的,this始终指向函数声明时所属的那个作用域。普通函数中的this与调用它的对象有关。
window.name = '杨洋'
let getName = () =>{
    console.log(this.name)
}
getName() //杨洋
const obj = {
    name:'李易峰'
}
//通过call方法看能不能改变this的指向?不能
getName.call(obj)//杨洋

//2.箭头函数不能作为构造函数去使用
let Person = (name,age) =>{
        this.name = name
        this.age = age
    }
let person = new Person('杨洋',18)
console.log(person)//Uncaught TypeError: Person is not a constructor

//3.不能使用arguments
let person = (name,age) =>{
    console.log(arguments) //Uncaught ReferenceError: arguments is not defined
}
person('杨洋',18)

//4.可以简写
let person = name =>{ //只有一个参数的时候可以不用写括号
    console.log(name)//杨洋
}
person('杨洋')
//代码体只有一行的时候省去花括号
let person = name => console.log(name) //杨洋
person('杨洋')

4.模板字符串:使用反引号``代替双引号创建字符串

const name='小刘',age='18'
console.log(`我是新来的同学${name},今年${age}岁。`)

let num = Math.round('12.339')
console.log(`${num}`)

模板字符串可以再嵌套模板字符串
const arr = [
       {title:'响应式布局'},
       {title:'Vue'},
       {title:'React'},
]
      
function template(){ //模板字符串可以再嵌套模板字符串
       return `
    ${arr.map(item=>`
  • ${item.title}
  • `).join('')}
` } document.body.innerHTML = template()

5.标签模板

       let name = '杨洋'
       let age = 18
       console.log(tag`我是${name},今年${age}岁。`)//如果在模板字符串前面加个标签
       function tag(strings,...args){ //strings:数组,数组元素是模板字符串中${}前后的元素截取出来的  args:${}内的变量组成的数组
            // console.log(strings)
            console.log(args)
       }
    //有个细节,strings的数组长度大于变量的个数
//标签模板实例
       const arr = [
           {title:'bootstrap框架',author:'杨老师'},
           {title:'Vue框架',author:'李老师'},
           {title:'React框架',author:'盛老师'},
       ]

       function template(){
           return `
    ${arr.map(item=>{ return links`
  • 作者:${item.author},课程:${item.title}
  • ` }).join('')}
` } function links(strings,...args){ return strings.map((str,key)=>{ return str + (args[key]?args[key].replace('框架',"框架"):'') }).join('') } document.body.innerHTML = template()

5.扩展运算符

//扩展运算符能将数组转为逗号分割的参数列表:[1,2,3] =>  1,2,3

            //扩展运算符的使用:  
            //数组克隆
            const fruits = ['苹果','梨','橘子']
            const newFruits = [...fruits]
            console.log(newFruits)//['苹果','梨','橘子']
            const num = [{a:1},{b:2},{c:3}]
            const newNum = [...num]// 扩展运算符拷贝数组,如果数组中有引用类型的数据,则是浅拷贝。如果改变克隆数组某元素的值,原数组的值也会改变,要注意
            console.log(newNum)//[{a:1},{b:2},{c:3}]
            const newNum1 = JSON.parse(JSON.stringify(newNum))//深拷贝一份
            newNum1[0].a = 11
            console.log(num)//原数组就不会被改变了

            //数组合并
            const flowers1 = ['月季','玫瑰']
            const flowers2 = ['牡丹','菊花']
            const newFlowers = [...flowers1,...flowers2]
            console.log(newFlowers)

            //将伪数组转为真正的数组
            let items = document.querySelectorAll('.item')
            let newItems = [...items]
            console.log(newItems)//转成真正的数组后就能使用forEach()

6.新增了一些字符串和数组方法

//4.1 新增的字符串方法
const str = '我 们都有一个家'
console.log(str.includes('一')) //true
console.log(str.startsWith('我们')) //false,判断某个字符串是否在原字符串的头部
console.log(str.endsWith('家')) //true,判断某个字符串是否在原字符串的尾部
//4.2 新增的数组方法
const arr = [4,9,16,25]

//4.2.1 includes方法(es7新增的方法)
console.log(arr.includes(9)) //true
//注意,数组元素是引用类型的时候,使用includes()无法查找。
const arr = [{name:'yy'},{name:'uu'},{name:'jj'}]
console.log(arr.includes({name:'uu'}))//false

//4.2.2 map()方法:得到一个经过处理后的新数组
let newArr = []
arr.map(item=>{
        newArr.push(Math.sqrt(item))
    })
console.log(newArr)//[2,3,4,5]

//4.2.3 filter()方法:过滤操作,得到想要的结果
let newArr1 = arr.filter(item=>{
    return item > 10
})
console.log(newArr1)

//4.2.4 reduce(prev,current,index,arr)
let total = arr.reduce((prev,current)=>{ //求和
    return prev + current
})
console.log(total)

const arr1 = [[1,2],[3],[4,6]]
let newArr2 = arr1.reduce((prev,current)=>{ //二维数组变成一维数组(数组扁平化)
    return prev.concat(current)
})
console.log(newArr2)

const arr2 = [1,2,[3,4,[5,6]],7,8]
function flatten(arr){
        let newArr = arr.reduce((pre,cur)=>{ //多维数组变为一维数组(数组扁平化)
              return pre.concat(Array.isArray(cur)?flatten(cur):cur)
        },[])
        return newArr
}
console.log(flatten(arr2))

//需求:把“我正在学习php和css”中的php和css替换成超链接
 const arr = ['php','css']
const str = '我正在学习php和css'
let replaceStr = arr.reduce((pre,cur)=>{
     return pre.replace(cur,`${cur}`)
},str)
document.body.innerHTML  = replaceStr

//4.2.5 some(callback):检查数组中的每个元素是否符合条件,只要有一个满足条件就返回true,都不满足返回false
const ageArr = [16,17,18,19,20]
console.log(ageArr.some(item=>item>17))

//4.2.6 Array.from(new Set(arr)):数组去重
const arr3 = [1,1,2,3,4,5,5,5,6]
console.log(Array.from(new Set(arr3))) //[1,2,3,4,5,6]
//数组去重的其它方法:利用reduce()或者Object.keys(obj)
let arr31 = arr3.reduce((pre,cur)=>{
    if(!pre.includes(cur)){
        return pre.concat(cur)
    }else{
        return pre
    }
},[])
console.log(arr31)//[1,2,3,4,5,6]

let obj = {}
arr3.forEach(item=>{
    obj[item] = 1
})
let newArr32 = Object.keys(obj).map(item=>{
    return Number(item)
})
console.log(newArr32)//[1,2,3,4,5,6]

//4.2.7 find()和findIndex()
 const arr = [{name:'yy'},{name:'uu'},{name:'jj'}]
    let res = arr.find(item=>{ //查找数组中符合回调函数要求的第一个数组元素。意思就是:如果有多个元素元素都符合此要求,只返回符合的第一个
      return item.name === 'uu'
    })
    console.log(res)

    let index = arr.findIndex(item=>{ //查找数组中的指定元素的索引值
      return item.name === 'uu'
    })
    console.log(index)//1

    const Arr=[1,2,3,4];
    var num = Arr.find(function(value){
      return value > 2
    })
    console.log(num)

    const Arr1 = [1,2,3,4];
    var num1 = Arr1.findIndex(function(value){
      return value > 2
    })
    console.log(num1)//2

rest参数:...args,es6中引入这个参数获取多余的实参,代替es5中的arguments。

function sum(){
    console.log(arguments)
}
sum(1,2,3)//结果不是个数组

function sum1(...args){
    console.log(args)
}
sum1(1,2,3)//[1,2,3]

function multiple(a,...args){
     return args.map(item=>{
          return a * item
   })
}
let result = multiple(3,4,5,6,7)
console.log(result)//[12,15,18,21]

//rest参数必须放到最后
function sum(a,b,...args){
    console.log(a,b,args)
}
sum(1,2,3,4,5,6,7)//1 2  [3, 4, 5, 6, 7]

7.Symbol

Symbol是es6引入的新的原始数据类型,是js的第七种数据类型,是类似于字符串的数据类型。

在对象中,凡是属性名属于Symbol类型的,这个属性就是独一无二的,在对象中就不会产生命名冲突的问题。
Symbol不能用于和其它数据类型运算。
Symbol定义的对象不能用for...in获取对象属性,但是可以使用Reflect.ownKeys获取对象的所有属性名。

//创建Symbol
let s = Symbol()
console.log(typeof s)//symbol
//创建Symbol时,可以给Symbol函数传递参数,表示对Symbol实例的描述,容易区分不同的Symbol值。
let s1 = Symbol('哈哈')
let s2 = Symbol('哈哈')
console.log(s1)//Symbol(哈哈)
//注意:Symbol函数的参数只是对当前Symbol值的描述,两个参数一致的Symbol函数,它们的返回值是不相等的
console.log(s1 === s2)//false
//Symbol函数的参数是个对象,会自动调用对象的toString()方法
const obj = {
    toString() {
        return 222
    }
};
let sym = Symbol(obj);
console.log(sym) //Symbol(222)

var fruits = {
        type1:'苹果',
        type2:'橘子', 
}
    //我想把otherFruits对象中的两个属性添加到fruits对象中去,但是我不确定fruits对象是否已经有了这两个属性
    //所以,我把otherFruits对象中的两个属性的属性值设置成Symbol对象,即独一无二的,这样可以避免把fruits对象中的属性给覆盖掉
    let mySymbol1 = Symbol()
    let mySymbol2 = Symbol()
    let otherFruits = {
        type1:mySymbol1,
        type2:mySymbol2
    }
    fruits[otherFruits.type1] = '哈密瓜'
    fruits[otherFruits.type2] = '红柚'
    //但是这添加的Symbol属性,怎么输出呢?
    console.log(fruits[mySymbol1],fruits[mySymbol2])//哈密瓜  红柚


//Symbol.for创建
let s3 = Symbol.for('哈哈')
console.log(typeof s3)//symbol

//Symbol的内置属性


8.迭代器(Iterator)
迭代器:一种数据接口,为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口,就能实现遍历操作。
es6创建了一种新的遍历方式for...of循环,迭代器使用for...of循环就能进行遍历操作。
原生具备迭代器接口的数据结构有Array、Arguments、Set、Map、String、TypedArray、NodeList
迭代器工作原理:
(1)创建一个指针对象,指向当前数据结构的起始位置。
这个对象是由Symbol(Symbol.iterator)这个函数创建
(2)第一次调用对象的next方法,指针自动指向第一个数据成员
(3)接下来不断调用next方法,指针一直往后移动,直至到最后一个成员
(4)每调用一次next方法,都会返回一个包含value和done的对象

            const arr = [1,2,3,4]
            //创建指针对象
            let iterator = arr[Symbol.iterator]()
            //调用对象的next方法
            console.log(iterator.next())
            console.log(iterator.next())
            console.log(iterator.next())
            console.log(iterator.next())
            console.log(iterator.next())

迭代器的应用:使用for...of按照自己的意愿去遍历对象,依次输出hobbies的值。
思考:
当使用for...of循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。所以,某种数据结构身上有迭代器接口,就能使用for...of
那某种数据结构没有迭代器接口,怎么办?比如,对象,对象本身是没有迭代器接口的(Uncaught TypeError: obj is not iterable),所以给对象部署个迭代器接口。
怎么部署呢?
es6默认迭代器接口部署在数据结构的Symbol.iterator属性上,只要有了这个属性,就可以进行遍历。
Symbol.iterator属性本身是一个函数,执行这个函数会返回一个遍历器。

const obj = {
                name:'杨洋',
                hobbies:[
                    '打篮球',
                    '踢足球',
                    '打网球',
                    '打羽毛球'
                ],
                [Symbol.iterator](){ //部署迭代器接口
                    let index = 0
                    let _this = this
                    return {
                        next:function(){ //index++
                            return index < _this.hobbies.length?{value:_this.hobbies[index++],done:false}:{value:undefined,done:true}
                        }
                    }
                }
            }
            
            for(let value of obj){
                console.log(value)//打篮球 踢足球 打网球 打羽毛球
            }

9.生成器

生成器就是一个特殊的函数,异步编程一种新的解决方案。
执行生成器函数会返回一个遍历器对象(代表生成器函数内部指针),返回的遍历器对象,可以对生成器函数内部的状态进行遍历。
生成器函数内部使用yield表达式,定义了不同的内部状态。

            function * gen(){
                console.log('生成器')
            }
            let iterator = gen()
            iterator.next()

            function * gen1(){ //yield:暂停标志
                yield '哈哈哈'
                yield '呵呵呵'
                yield '嘿嘿嘿'
            }
            //for...of循环可以自动遍历生成器函数生成的遍历器对象
            //for(let value of gen1()){
            //  console.log(value)
            //}
            let iterator1 = gen1()
            console.log(iterator1.next())//{value: "哈哈哈", done: false}
            console.log(iterator1.next())//{value: "呵呵呵", done: false}
            console.log(iterator1.next())//{value: "嘿嘿嘿", done: false}
          //上面代码定义了一个生成器函数,执行函数返回遍历器对象,然后调用了三次遍历器对象的next()方法。
          //第一次调用next()方法,开始遍历生成器函数,遇到yield表达式会停止。yield表达式的值会作为next()方法返回对象的value属性的值。
          //所以,第一次调用next()方法的结果就是:{value: "哈哈哈", done: false}
          //第二次调用next()方法,遍历器对象从上一次停下的地方开始,遇到下一个yield表达式后又停下。输出结果:{value: "呵呵呵", done: false}
          //第三次调用next()方法,遍历器对象从上一次停下的地方开始,遇到下一个yield表达式后又停下。输出结果:{value: "嘿嘿嘿", done: false}

yield表达式:
假如生成器函数内部的yield后面的表达式是12+34,不会立即求值,而是在遍历器调用了next()方法,指针指向了这个语句时,才会求值。

next()方法带参数:next()的参数会作为上一个yield表达式的返回值。
                function * sum(){
                    let one = yield 111
                    console.log(one)
                    let two = yield 222
                    console.log(two)
                    let three = yield 333
                    console.log(three)
                }

                let itt = sum()
                console.log(itt.next())
                console.log(itt.next('a'))
                console.log(itt.next('b'))
                console.log(itt.next('c'))
//生成器函数的应用
//模拟数据获取——>先获取用户数据,再获取订单数据,最后获取商品数据
function getUsers(){
                    setTimeout(()=>{
                        let data = '用户数据'
                        it.next(data) 
                    },1000)
                }
                function getOrders(){
                    setTimeout(()=>{
                        let data = '订单数据'
                        it.next(data) 
                    },1000)
                }
                function getGoods(){
                    setTimeout(()=>{
                        let data = '商品数据'
                        it.next(data) 
                    },1000)
                }

                function * gen1(){
                    let users = yield getUsers()
                    console.log(users)
                    let orders = yield getOrders()
                    console.log(orders)
                    let goods = yield getGoods()
                    console.log(goods)
                }

                let it = gen1()
                it.next()
            

你可能感兴趣的:(es6新增特性(1))