一、ES6新特性
1.1 let关键字
1.1.1背景:
var myname = "时间"
function showName(){
console.log(myname);//undefined
if(0){
var myname = "极邦"
}
console.log(myname);//undefined
}
showName()
原因解释:var的创建和初始化被提升,赋值不会被提升,let的创建被提升(创建之后没有初始化就调用会报错)初始化和赋值不会被提升,函数的创建、初始化、赋值都会被提升
执行函数过程中,会优先从当前的执行上下文中查找变量,由于变量提升当前函数中包含变量myname=undefined,所以获取的是undefined。
解决方案:使用let关键字限制在块级作用域内部
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
原因解释:第一步编译并创建执行上下文,编译阶段会将var声明的变量放到变量环境中,let声明的变量放到词法环境中,而在函数作用域块内部通过let声明的变量没有放到词法环境
第二步: 执行到代码块里面时,变量环境中的a被赋值,词法环境中的b也赋值.进入函数的作用域块时,作用域块中通过let声明的变量会被放到词法环境的单独区域中.词法环境内部是一个栈结构,作用域执行完就会被弹出
第三步:在作用域内部查找a时首先沿着词法环境栈顶向下查询,找到就返回没找到就到环境变量中查找
var elements = [{},{},{}]
for(var i =0;i<elements.length;i++){
elements[i].onclick = function(){
console.log(i)
}
}
elements[0].onclick()// 输出3 是由于 onclick是异步,console打印的i是全局作用域中的i,在循环时已经累加到3。可以将var写成let实现
1.2关键字const
定义以后不可修改,定义时候必须赋初值.并且不会将作用域提前。可修改对象中的内容
const obj = {}
obj.name = "zs";//允许更改指针指向的对象内部的值
obj = {} //不允许,修改指针的指向
1.3解构:从数组或者对象中获取指定成员的方式
1.3.1数组的解构
const arr = [100,200,300]
const [foo,bar,baz] = arr ;
console.log(foo,bar,baz) //100 200 300
const [ , ,baa] = arr ;
console.log(baa);//300
const [aaa,... rest] = arr;
console.log(rest) // 输出结果为数组,[200,300]
const [faa] = arr ;
console.log(faa) // 100
const[faa,fbb,fcc,fdd] = arr;
console.log(fdd) //undefined
const [abb,acc,add=50,aee =400 ] =arr;
console.log(add)//50
console.log(aee)//400
1.3.2对象的解构
对象的解构是通过属性名来区分
const obj = {name:'zs', age: 18}
const {age} = obj
const name = 'jack'
const {name: newName} =obj; //解构已有对象名时添加新的变量
console.log(name) //jack
console.log(newName) //zs
1.4模板字符串
用法:用" ` "将字符串包裹,可以在定义的字符串里添加换行等字符和插值表达式
const name = "tom"
const s = `name
age `
const str = `e,${name} ${1+1} ${Math.random()} `
console.log(str) //he,tom2 1.2262
模板字符串标签函数
1.6 字符串扩展方法
startsWith():判断是否是以某字符串开头
endsWith():判断是否是以某字符串结尾
includes():是否包含指定字符
const msg = `Error : foo is not defined. `
console.log(msg.startsWith('Error'));//true
console.log(msg.endsWith('.'));//true
console.log(msg.includes('foo'));//true
1.7参数默认值
可以为函数中的传入的参数设置一个默认值,有默认值的参数写在最后。
function foo(enable){
enable = enable || true ;
//当要给enable传入false值的时候无法实现
//应该使用 enable = enable === undefined ? true : enable;
}
//参数默认值: function foo(enable = true){}
在没有使用实参或者实参是undefined时被使用,多个参数时默认值必须写在最后 function foo(str,enable=true)
1.8剩余操作符
函数的实参可以用argument关键字接收,接收以后以类数组形式展现。可以加…操作符表示剩余的参数.每个函数中只可使用一次且必须放在最后
function foo(n,...args){
console.log(args);//[2,3,4,5]
}
foo(1,2,3,4,5)
1.8.1展开数组操作
const arr = ['ccc','ddd','bbb'] ;
console.log.apply(console,arr)//ccc ddd bbb
console.log[...arr]//ccc ddd bbb
可以通过展开运算符过去数组中的每个值和新值组成新的数组代替数组的push方法
arr = [1,2,3,4]
arr1 = [...arr,0] //arr1=[1,2,3,4,0]
1.9箭头函数
函数传参时可以直接赋值,不会改变this指向
//只有一个参数时,可以省略括号,函数只有一条语句可以省略大括号
const fun = a => a+1 ;
fun(2)//3
//普通写法
const plus = (a,b) =>{
return a + b ;
}
plus(2,3)//5
//作用同
function plus(a,b){
return a + b;
}
1.10 对象字面量增强
对象中的变量名和属性名相同是可以直接省略属性名
const bar = 'bar';
const age = 'age';
const obj = {
name : "tom",
bar , //bar : "bar"
sayHi(){ //等同于 sayHi:function(){}
console.log('hi');
}
[age]:18 //等同于$1语句
}
// $1 obj[age] = 19
1.11对象操作方法
1.11.1Object.assing 方法
将一个对象中的属性复制到另一个对象中
obj1 = {a:1,b:2}
obj2 = {a:3,d:0}
obj = object.assing(obj1 ,obj2);//将obj2对象中的属性复制到obj1中
console.log(obj) //obj = {a:3 d:0 b:2}
console.log(obj === obj1) //true
1.11.2应用:在option对象参数接收时简化书写
function Block(option){
//this.windth = option.width
Object.assign(this,options)
}
const block1 = new Block({width:100,height:100,x:50,y:50})
console.log(bolock1)
1.11.3Object.is方法
比较两个变量是否相等,使用“”判断时会将两边的数据转化成相同的类型,导致0fasel返回结果为true,使用===
来无法判断以下数据所以引入Object.is方法
Object.is(NAN,NAN) //true
Object.is(+0,-0) //false
1.12class类
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
sayHi(){
console.log(`my name is ${this.name}`)
}
}
const p1 = new Person("tom",18);
console.log(p1);//Person(name:'tom',age:18)
p1.sayHi()//my name is tom
1.13静态方法
1.13.1static 关键词
类中的静态方法中的this指向 的是类本身
class Person {
constructor (name,age){
this.name = name ;
this.age = age;
}
sayHi() {
console.log(`hi,my name is ${this.name}`);
}
static create (name,age){
return new Person(name,age)
}
}
const p1 = new Person("tom",18);
console.log(p1);
1.14继承
使用extends关键字继承,用super关键字引用父类属性和方法
class Person {
constructor (name,age){
this.name = name ;
this.age = age;
}
sayHi() {
console.log(`hi,my name is ${this.name}`);
}
}
class Student extends Person{
constructor(name,age ,number) {
super(name,age);//调用父类属性
this.number = number;
}
hello(){
super.sayHi()//引用父类方法
console.log(`学号是${this.number}`)
}
}
const s1 = new Student("tom",18,101);
s1.hello();
1.15 Set数据结构
通过类型构造实例存放不重复的数据,因此需要new关键字创建
const s = new Set()
s.add(1).add(2).add(3).add(3)
console.log(a)//Set(3){1,2,3}
s.forEach(i => console.log(i))//依次便利每项数据
for(let i of s){ //遍历方式2
console.log(i);
}
console.log(s.size)// 长度 3
console.log(s.has(2))//是否包含2,true
console.log(s.delete(3))//删除成功返回true
s.clear()//清除所有项
应用:数组去重
const arr = [1,2,3,6,4,4,4,2,5,3,7]
const b = Array.form(new Set(arr))//将集合转为数组
const b1 = [...new Set(arr)]//或用...展开
console.log(b)//(7)[1,2,3,6,4,5,7]
1.16 Map数据结构
普通对象设置的键只能为字符串类型,使用Map数据结构可以将任意类型(对象)数据作为对象的键
const obj = {}
obj[true] = "boolean"
obj[123] = "number"
obj[{a:1}] = "object"
console.log(Object.keys(obj))// ["123", "true", "[object Object]"],对象数据无法匹配
const map = new Map()
const a = {a:1}
map.set(a,100)
console.log(map)//Map(1){{a : 1} => 100}
map.has()
map.delete()
map.clear()同set方法
1.17 Symbol数据类型:为对象添加独一无二的属性标识
不同的js文件中可能有相同的对象名和键名导致替换。第三方库中的对象名和自己代码中命名冲突
解决方法:使用Symbol数据类型
const s = Symbol()
console.log(s)//Symbol()
console.log(Symbol() === Symbol())//false,说明通过Symbol创建的对象是唯一的
//普通数据类型的对象重名会覆盖
const cache = {};
cache['foo'] = Math.random
cache['foo'] = '12'
console.log(cache)//12会覆盖之前的值
//Symbol类型的对象不会覆盖
const obj = {[Symbol()] :789}
obj[{Symbol()}] = 123
obj[{Symbol()}] = 456
console.log(obj)//{Symbol():789,Symbol():123,Symbol():456}
console.log(obj[Symbol()])//undefined
Symbol中的for方法接收字符串作为参数,通过字符串与Symbol建立对应关系
const a = Symbol.for('foo')
const b = Symbol.for('foo')
console.log(a === b)//true
const obj = {}
console.log(obj.toString())//[object Object]
const obj = {
//利用symbol中的常量修改tostring标签
[Symbol.toStringTag]:"xObject"
}
console.log(obj.toString())//[object xObject]
可以使用Symbol创建对象的私有成员
const name = Symbol()
const person = {
[name]: 'zs',
say(){ console.log(this[name])} //只有通过say方法才能获取到name的值
}
person.say()
Symbol无法被普通方法遍历和获取,获取Symbol对象使用Object.getOwnPropertySymbols 方法。该方法仅可以获取Symbol类型的属性
const obj = {
[Symbol()]:"symbol Value"
foo:"foo value"
}
for(var k in obj){
console.log(k) //无法遍历到Symbol()对象
}
console.log(Object.keys[obj])//无法获取到Symbol
console.log(JSON.stringify(obj))//无法获取到Symbol
console.log(Object.getOwnPropertySymbols(obj))//[Symbol()]
迭代器
ES2015在语言层面实现迭代模式。通过实现iterator方法实现对外提供统一遍历接口,外部无需关心内部数据结构。
原理
const obj={
store:['foo','bar','zzz'],
learn:{'语文','数学','外语'}
}
[Symbol.iterator]: function(){
const all = [].concat(this.store,this.learn)
let index = 0
return {
next: function(){
const result = {
value: all[index],
done: index++>=all.length
}
}
}
}
//外部可以调用for...of循环统一遍历
for(const item of obj){
console.log(item)
}
1.18 循环总结
- for of
for of 提供了iterator迭代器的方法,能够实现所有数据类型的遍历symbols Symbol.iterator
const set = new Set(['foo','bar','zzz'])
const iterator = set[Symbol.iterator]()
console.log(iterator.next()) // {value:"foo", done:false} done表示迭代是否结束
console.log(iterator.next()) //{value:"bar", done:false}
console.log(iterator.next()) //{value:"foo", done:false}
console.log(iterator.next()) //{value:undefined, done:true}
arr = [10,20,30,40,50]
for(const item of arr){
console.log(item);
if(item >20){
break;
}
}
//forEach 方法无法中断循环
forEach(item =>{
console.log(item);
})
//遍历set数据;类型
const s = new Set(["foo",""bar,"baz"])
for(const item of s){
console.log(item)
}
//遍历Map数据类型
const m = new Map()
m.set("foo",1)
m.set("bar",2)
for(const item of m){
console.log(item) //['foo',1] ['bar',2]
}
- for in
for in会遍历出所有的键以及proto中的键,只适用于遍历对象 - forEach
forEach无法提前跳出循环,break和continue、return无法生效。
1.19 条件式属性访问
如果一个对象为Null或undefined,获取其内部属性值时会报TypeError,通过?.可以避免该错误发生
let a={b:null, c: undefined}
console.log(a.b.c) // Uncaught TypeError
console.log(a.b?.c) //undefined
console.log(a.b?.c?.d) //undefined
1.20 Object.defineProperty()
Proxy属性代理器,监视对象读写,同时可以监听使用数组的方法操作数组,使用defineProperty()方法时需要重写数组的方法
const person={
name:'zs',
age:29
}
const list = []
const personProxy = new Proxy(person,{
//监视属性的访问get(监视的对象,访问的属性名)
get(target,property){
console.log(target,property)
return 100
}
//set(代理目标对象,写入的属性名称,写入的属性值)
set(target,propety,value){
console.log(target,propety,value)
}
})
personProxy.gender = true //通过set方法写入属性
console.log(personProxy.name)
const listProxy = new Proxy(list,{
set(target,property,value){
console.log('set',property,value)
list[property] = value
return true
}
})
listProty.push(199)//使用数组的方法操作数组时会执行到set方法内的代码
1.21 Reflect统一提供一套用于操作对象的API
属于静态类,无法使用new的方式构建实例对象,只能调用静态类中的 静态方法
const obj = {
name: 'zs',
age: 18
}
//判断对象中是否存在某一成员
console.log('name' in obj) //true
console.log(Reflect.has(obj,'name'))//true 可使用Reflect中的has方法判断
//删除对象中的成员
console.log(delete obj['name']) //true
console.log(Reflect.deleteProperty(obj,'name'))//true 可使用deleteProperty方法
//输出对象中的所有成员
cnosole.log(Object.keys[obj]) //[age]
console.log(Reflect.ownKeys[obj])