ES6常用新语法总结

ES6 介绍

ES6的名称为ESMAScript2015(es2015),是2015年6月份发行的,它是最新ECMAScript的代表版本,一是因为相对与es5变化比较大,二是因为它的发行让标准命名规则发生了变化,ES6更准确的缩写名称应该叫ES2015,

ES6的出现最主要的解决了以下几个问题:

  1. 解决原有语法上的一些问题或不足(比如let,const)
  2. 对原有语法进行增强,更加易用(比如解构,展开,参数默认值,模板字符串)
  3. 全新的对象,全新的方法,全新的功能(promise)
  4. 全新的数据类型和数据结构(symbol,Set,Map)

let,const与var的区别

1.var 存在变量提升,let 不存在
//let
console.log(c) //Uncaught ReferenceError: Cannot access 'c' before initialization
let c = 100      

//var
console.log(c) //underfined
var c =100

因为 let 不存在变量提升,所以上面 let 定义的变量会报错,报错的意思是我们想要打印 c 的值,必须先要初始化,然后再去使用它,必须遵循 先声明,后使用 的使用规则。
var 定义的变量结果为 underfined 代码相当于:

var c;//变量提升,但是值不能提升,所以是underfined
console.log(c) //underfined
c = 100
2.let在同一个作用域下不可以重复定义同一个变量值,而var可以
//let
let c = 100 
let c = 200  //报错 Uncaught SyntaxError: Unexpected identifier

//var
var c = 100
var c = 200 
console.log(c) //200 正常运行,会覆盖前一个值
3.有严格的作用域,var属于函数作用域,let属于块级作用域

ES5 中作用域有:全局作用域、函数作用域,没有块作用域的概念。
ES6 中新增了块级作用域,块作用域由**{ }**包括,if 语句和 for 语句里面的 { } 也属于块作用域。

//let
function fun(){
  let n = 10
  if(true){
    let n = 20 //与上面的 n 不属于同一个作用域,所以不受影响
  }
  console.log(n) //10
}
fun()

//var
function fun(){
  var n = 10
  if(true){
    var n = 20 //会覆盖上面的 n 的值
  }
  console.log(n)//20
}
fun()

4. const
4.1 const 声明的变量为只读的,一旦声明,常量的值就不能改变
const a =2; 
a=3;//错误  Uncaught TypeError: Assignment to constant variable.
4.2 const 声明的变量一定要初始化,不能只声明不赋值,
const a; // 错误  Uncaught SyntaxError: Missing initializer in const declaration
 //(const声明中缺少初始化程序)
4.3 const 声明的变量只在块级作用域内有效。
{
  const  a = 3
}
console.log(a) // Uncaught ReferenceError: a is not defined

但是用 const 声明的对象的属性是可以更改的,const 实质上保证的并不是变量的值不得改动,而是变量指向的 内存地址 的值不得改动,对于简单类型数据,值就保存在变量指向的内存地址中,相当于常量。而对于复合型的数据,变量指向的是内存地址保存的是一个指针。const 只能保证指针是不可以被更改,但指针指向的数据结构是可以被改变的。

//以下代码正常执行
const  obj = {}
obj.name="xiaoke" 
console.log(obj) // {name: "xiaoke"}

const  arr = []
arr.push("xiaoke")
console.log(arr) // ["xiaoke"]

数组的解构

  1. 比如我们要取出arr中的值,我们可以使用下面的方式取出,a1,a2,a3代表了相应位置坐标的值
const arr = ['a','b','c']

const [a1,a2,a3] = arr
console.log(a1,a2,a3)//a b c
  1. 如果我们只想取索引为2的值,我们可以:
const arr = ['a', 'b', 'c']

const [, , a3] = arr //用逗号进行占位
console.log(a3)//c
  1. 如果我们想把b和c一起取出来我们可以:
const arr = ['a', 'b', 'c']

const [, ...arr2] = arr
console.log(arr2) //['b','c']
  1. 如果我们想取出一个数组中不确定存在的元素时,我们可以先给这个值赋值默认值,默认值的意思是数组中不存在此元素时,会输出默认值,存在这个值是输出存在的值:
存在
const arr = ['a', 'b', 'c', 'd']
const [a1, a2, a3, a4 = 'dd'] = arr
console.log(a1, a2, a3, a4)
console.log(arr)//a b c d

不存在
const arr = ['a', 'b', 'c']
const [a1, a2, a3, a4 = 'dd'] = arr
console.log(a1, a2, a3, a4)
console.log(arr)// a b c dd

对象的解构

  1. 对象的解构和数组的解构很相似,只是对象的解构不是按索引位置进行取值的,而是取出对象中的key值
const obj = { name: "xuke", age: 22 }

const { age } = obj
console.log(age) //22
  1. 对象和数组一样也可以设置默认值,如果我们想取出一个对象中不确定存在的元素时,我们可以先给这个值赋值默认值,默认值的意思是数组中不存在此元素时,会输出默认值,存在这个值是输出存在的值。
const obj = { name: "xuke", age: 22 }

const { hobby = "eat" } = obj
console.log(hobby) // eat

3.如果我们代码中已经定义了一个与obj中相同的属性名,我们可以使用下面的方式给属性名一个别名,然后再取出

const obj = { name: "xuke", age: 22 }
const name = 'keke'
const { name } = obj
console.log(newname) //会报错:SyntaxError: Identifier 'name' has already been declared

改正
const obj = { name: "xuke", age: 22 }
const name = 'keke'
const { name : newname } = obj
console.log(newname) //xuke

模板字符串

  1. 我们用反引号来包裹起来字符串: ``
const name = 'xuke'
const str = `my name is ${name}`
console.log(str) // my name is xuke
  1. 模板字符串支持换行

const name = 'xuke'
const str = `my name
 is ${name}`
console.log(str)
//my name
//is xuke
  1. ${}中可以使用函数表达式,返回最终值
const str = `my age is ${true ? 22 : 10}`
console.log(str) //my age is 22

字符串的扩展方法

  1. startsWith() 判断是否以某个字符开头
  2. endsWith()判断是否以某个字符结尾
  3. includes()判断是否包含某个字符
const message = 'welcome to beijing.'

console.log(message.startsWith('welcome'))//true
console.log(message.startsWith('wel'))//true
console.log(message.endsWith('beijing'))//false
console.log(message.endsWith('.'))//true
console.log(message.includes('bei'))//true

参数默认值

  1. 当我们给函数传参的时候,我们可以使用以下方法设置默认值
function num(x = 20) {
    return x += x
}
//x不传为undefined时 ,会使用我们的默认值 20
console.log(num()) //40
//传值的时候会使用我们传的值
console.log(num(10)) //20

注意:我们对参数设置默认值的时候,要把带有默认值的参数放到最后面

//错误的写法
function num(a = 20, x) {
    return a + x
}

//正确的写法
function num(a , x = 20) {
    return a + x
}

展开运算符(…)

  1. 合并数组
let a = [1,2,3];
let b = [4,5,6];
let c = [...a,...b]; // [1,2,3,4,5,6]
  1. 替代apply
function f(a,b,c){
  console.log(a,b,c)
}
let args = [1,2,3];
// 以下三种方法结果相同
f.apply(null,args)
f(...args)
f(1,2,3)
//===========
function f2(...args){
  console.log(args)
}
f2(1,2,3) // [1,2,3]

function f3(){
  console.log(Array.from(arguments))
}
f3(1,2,3) // [1,2,3]
  1. 浅拷贝
//数组
var a = [1,2,4]
var b = [...a]
a.push(6)
console.log(b) // [1,2,4]

//对象
var a = {a:1}
var b = {...a}
a.a = 5
console.log(b.a) // 1

箭头函数

使用

const fun = function () {
    return 'hello'
}
const fun2 = () => 'hello'
console.log(fun()) // hello
console.log(fun2())// hello

//===

const nums = [1, 2.3, 2, 46, 6, 4, 3, 2]
const result = nums.filter(num => num % 2 === 0)
console.log(result)//[ 2, 46, 6, 4, 2]

箭头函数不会改变this的指向

const Person = {
    name:'xuke',
    say1:function(){
        console.log(this.name) //xuke 指向Person
    },
    say2:()=>{
        console.log(this.name) //undefined 指向window
    }
}
Person.say1()
Person.say2()

setTimeOut情况:

const Person = {
    name: 'xuke',
    say1: function () {
        setTimeout(function () {//setTimeOut函数体里面的函数题被放到全局作用域去调用
            console.log(this.name) //undefined 
        }, 1000)
        setTimeout(() => {//setTimeOut函数体里面的箭头函数始终指向当前作用域中的this
            console.log(this.name) //xuke
        }, 1000)
    }
}
Person.say1()

对象字面量

  1. 当我们对象中的属性名相等的时候,我们可以把省略:
const name = 'xuke'
//普通用法
const obj1 = {
    name:name,
    age:12
}
//字面量用法
const obj2 = {
    name,
    age:12
}
console.log(obj1) //{ name: 'xuke', age: 12 }
console.log(obj2) //{ name: 'xuke', age: 12 }
  1. 当我们想用表达式生成对象的属性名的时猴,我们可以在[]中写入我们的表达式
//es2015之前
const name = 'xuke'
const obj = {
    name,
    age:12
}
const obj2 = {}
obj2[obj.name] = 'keke'
console.log(obj2) //{ xuke: 'keke' }

//es2015
const name = 'xuke'
const obj = {
    name,
    age:12
}
const obj2 = {
    [obj.name]:'keke'
}
console.log(obj2) //{ xuke: 'keke' }

Object.assign()

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性覆盖前面的属性

const source = {
    a:123,
    b:123
}
const target ={
    a:456,
    c:111
}

console.log(Object.assign(target,source)) //{a:123,c:111,b:123}

还可用于对象的浅拷贝,

const source = {
    a:123,
    b:123,
    name:{
        cc:'ccc'
    }
}
const target = Object.assign({},source)
target.a = 333
console.log(target) //{ a: 333, b: 123 }
console.log(source) //{ a: 123, b: 123 }

Proxy

proxy真的用处很大,可是我项目中很少用到,总结一下。

proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截.

1. 语法

var proxy = new Proxy(target, handler);

new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为

2. 基本用法
var target = {
   name: 'poetries'
 };
 var logHandler = {
   get: function(target, key) {
     console.log(`${key} 被读取`);
     return target[key];
   },
   set: function(target, key, value) {
     console.log(`${key} 被设置为 ${value}`);
     target[key] = value;
   }
 }
 var targetWithLog = new Proxy(target, logHandler);
 
 targetWithLog.name; // 控制台输出:name 被读取
 targetWithLog.name = 'others'; // 控制台输出:name 被设置为 others
 
 console.log(target.name); // 控制台输出: others

targetWithLog 读取属性的值时,实际上执行的是 logHandler.get :在控制台输出信息,并且读取被代理对象 target 的属性。
在 targetWithLog 设置属性值时,实际上执行的是 logHandler.set :在控制台输出信息,并且设置被代理对象 target 的属性的值

3. 保持只返回一个值
// 由于拦截函数总是返回35,所以访问任何属性都得到35
var proxy = new Proxy({}, {
  get: function(target, property) {
    return 35;
  }
});

proxy.time // 35
proxy.name // 35
proxy.title // 35
4. Proxy 实例也可以作为其他对象的原型对象
var proxy = new Proxy({}, {
  get: function(target, property) {
    return 35;
  }
});

let obj = Object.create(proxy);
obj.time // 35

proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截

5. Proxy的作用
  1. 拦截和监视外部对对象的访问
  2. 降低函数或类的复杂度
  3. 在复杂操作前对操作进行校验或对所需资源进行管理
6.Proxy使用场景
  1. 实现私有变量
var target = {
   name: 'poetries',
   _age: 22
}

var logHandler = {
  get: function(target,key){
    if(key.startsWith('_')){
      console.log('私有变量age不能被访问')
      return false
    }
    return target[key];
  },
  set: function(target, key, value) {
     if(key.startsWith('_')){
      console.log('私有变量age不能被修改')
      return false
    }
     target[key] = value;
   }
} 
var targetWithLog = new Proxy(target, logHandler);
 
// 私有变量age不能被访问
targetWithLog.name; 
 
// 私有变量age不能被修改
targetWithLog.name = 'others'; 

例1:在下面的代码中,我们声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey

var api = {  
    _apiKey: '123abc456def',
    /* mock methods that use this._apiKey */
    getUsers: function(){}, 
    getUser: function(userId){}, 
    setUser: function(userId, config){}
};

// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);

// get and mutate _apiKeys as desired
var apiKey = api._apiKey;  
api._apiKey = '987654321';

很显然,约定俗成是没有束缚力的。使用 ES6 Proxy 我们就可以实现真实的私有变量了,下面针对不同的读取方式演示两个不同的私有化方法。第一种方法是使用 set / get 拦截读写请求并返回 undefined:

let api = {  
    _apiKey: '123abc456def',
    getUsers: function(){ }, 
    getUser: function(userId){ }, 
    setUser: function(userId, config){ }
};

const RESTRICTED = ['_apiKey'];
api = new Proxy(api, {  
    get(target, key, proxy) {
        if(RESTRICTED.indexOf(key) > -1) {
            throw Error(`${key} is restricted. Please see api documentation for further info.`);
        }
        return Reflect.get(target, key, proxy);
    },
    set(target, key, value, proxy) {
        if(RESTRICTED.indexOf(key) > -1) {
            throw Error(`${key} is restricted. Please see api documentation for further info.`);
        }
        return Reflect.get(target, key, value, proxy);
    }
});

// 以下操作都会抛出错误
console.log(api._apiKey);
api._apiKey = '987654321';  

例2:让我们从一个简单的类型校验开始做起,这个示例演示了如何使用 Proxy 保障数据类型的准确性

let numericDataStore = {  
    count: 0,
    amount: 1234,
    total: 14
};

numericDataStore = new Proxy(numericDataStore, {  
    set(target, key, value, proxy) {
        if (typeof value !== 'number') {
            throw Error("Properties in numericDataStore can only be numbers");
        }
        return Reflect.set(target, key, value, proxy);
    }
});

// 抛出错误,因为 "foo" 不是数值
numericDataStore.count = "foo";

// 赋值成功
numericDataStore.count = 333;

例3:对于那些调用频繁、运行缓慢或占用执行环境资源较多的属性或接口,开发者会希望记录它们的使用情况或性能表现,这个时候就可以使用 Proxy 充当中间件的角色,轻而易举实现日志功能

let api = {  
    _apiKey: '123abc456def',
    getUsers: function() { /* ... */ },
    getUser: function(userId) { /* ... */ },
    setUser: function(userId, config) { /* ... */ }
};

function logMethodAsync(timestamp, method) {  
    setTimeout(function() {
        console.log(`${timestamp} - Logging ${method} request asynchronously.`);
    }, 0)
}

api = new Proxy(api, {  
    get: function(target, key, proxy) {
        var value = target[key];
        return function(...arguments) {
            logMethodAsync(new Date(), key);
            return Reflect.apply(value, target, arguments);
        };
    }
});

api.getUsers();

Reflect用法

概述

Reflect是为操作对象而提供的新API,那么我们为什么要去使用它呢?将Object对象的属于语言内部的方法放到Reflect对象上,即从Reflect对象上拿Object对象内部方法,比较方便,可读性更强。

1. Reflect.has(obj,name) 判断对象中的属性是否存在
const object = {
    name:'xuke',
    age:22
}
console.log(Reflect.has(object,'name'))//true
2. Reflect.set(target,propName,propValue)
const object = {
    name:'xuke',
    age:22
}

console.log(Reflect.set(object,'ww','dd'))//true object中不存在会直接添加
console.log(Reflect.set(object,'name','dd'))//true object中存在会覆盖
console.log(object) //{ name: 'dd', age: 22, ww: 'dd' }
3. Reflect.set(target,propName)
const object = {
    name:'xuke',
    age:22
}

console.log(Reflect.get(object,'name'))//xuke  属性名存在直接返回
console.log(Reflect.get(object,'ww'))//undefined  属性名不存在返回undefined
4. Reflect.deleteProperty(obj, name) 删除属性
const object = {
    name:'xuke',
    age:22
}

Reflect.deleteProperty(object,'name')
console.log(object) //{age:22}
5.Reflect.ownKeys (target) 用于返回对象的所有属性
const object = {
    name:'xuke',
    age:22
}

console.log(Reflect.ownKeys(object)) //[ 'name', 'age' ]
6. 上面列举了比较常用的方法,当proxy和Reflect配合起来一起使用:
const object = {
    name:'xuke',
    age:22
}
const proxy = new Proxy(object,{
    get:(target,property)=>{
       return Reflect.get(target,property) //获取被访问的属性
    },
    set:(target,property,value)=>{
        Reflect.set(target,property,value) //设置被访问的属性与新值
    }
})

console.log(proxy.name) //xuke
proxy.name = 'xiaohaha'
proxy.hobby = 'eat'
console.log(object) //{ name: 'xiaohaha', age: 22, hobby: 'eat' }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值