es6新特性
文章目录
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
https://es6.ruanyifeng.com/
ECMAScript是JavaScript的一套标准
ES6(JavaScript语言的下一个版本)
ES6的第一个版本是在2015年6月发布的----ECMAScript2015(ES2015)
一、let 和 const 命令
let的特点
let关键字就是用来声明变量的
let声明的变量具有块级作用域
在一个大括号中,使用let声明的变量才具有块级作用域 var是不具有这个特点的
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
防止循环变量变成全局变量
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
使用let声明的变量没有变量的提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
使用let声明的变量具有暂时性死区的特性
var t = 134
if (true) {
t = 'abc' //Cannot access 't' before initialization
let t
}
不能重复声明
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
const的特点
声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
常量是不能重新进行赋值,如果是基本数据类型不能更改值,复杂数据类型 不能更改地址值
声明const的时候必须要给定值
let ,const,var的区别
var 声明的变量,其作用域为该语句所在的函数内,存在变量提升
let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升
const声明的常量,在后面出现的代码中不能再修改该常量的值
二、变量的解构赋值
数组解构
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
以前,为变量赋值,只能直接指定值。
let a = 1;
let b = 2;
let c = 3;
ES6 允许写成下面这样。
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
// var aa = 2
// var bb = true
// var cc = 'hello'
// 从数组中提取值,按照位置,给变量赋值
// // let [aa, bb, cc, dd] = [2, true, 'hello']
// console.log(aa, bb, cc)
// console.log(dd) //undefined
// let [aa, bb, cc, dd] = [2, true, 'hello', 1, 34]
// console.log(aa, bb, cc)
// 嵌套数组进行解构
// let [a, [b, c]] = [1, ['22', '33']]
// console.log(a, b, c)
// let [a, [[b], c]] = [1, [[2], 3]]
// console.log(a, b, c) //1 2 3
let [a] = []
let [c, b] = [2]
// 以上两种都属于解构不成功 a的值等于undefined b的值为undefined
对象解构
解构不仅可以用于数组,还可以用于对象。
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
// let { name } = { name: '张三' }
// console.log(name)
// let obj = {
// name: 'ls',
// age: 23,
// sex: '男',
// score: 100,
// }
// // 变量名和对象的属性名要一一对象
// let { name, sex, age, score, a } = obj
// console.log(a) //undefined
let obj = {
name: 'ls',
age: 23,
sex: '男',
score: 100,
msg: {
des: 'hello',
},
}
let {
name,
age,
sex,
score,
msg,
msg: { des },
} = obj
console.log(des) //hello
console.log(msg)
三、函数扩展
箭头函数
ES6 允许使用“箭头”(=>
)定义函数。
var f = v => v;
// 等同于var f = function (v)
{
return v;
};
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
// 等同于var f = function ()
{
return 5
};
var sum = (num1, num2) => num1 + num2;
// 等同于var sum = function(num1, num2)
{
return num1 + num2;
};
// --------------函数的代码块有多条语句
// var fn = function (a, b) {
// console.log(a + b)
// return a + b
// }
// var fn = (a, b) => {
// console.log(a + b)
// return a + b
// }
// ----------------代码块有一条语句 返回的是一个对象,对象要用括号括起来
// var fn = function () {
// return { name: '张三', age: 23 }
// }
// var fn = () => ({ name: '张三', age: 23 })
// console.log(fn())
// ;[1, 2, 3].map(function (item) {
// return x * 2
// })
// ;[1, 2, 3].map((item) => item * 2)
// var newArr = [1, 2, 3, 4]
// .filter((item) => item % 2 === 0)
// .map((x) => x * 2)
// console.log(newArr)
函数参数默认值
es6之前,不能直接为函数的参数指定默认值
// function fn(a, b) {
// b = b || '123'
// console.log(a, b)
// }
// fn(1, 2)
// fn(1)
// es6允许为函数的参数设置默认值,直接写在参数定义的后面
// function fn(a, b = '123') {
// console.log(a, b)
// }
// fn(1, 3)
// fn(2)
// fn(4, '')
// 和解构赋值默认值结合使用
// function fn({ x, y = 10 }) {
// console.log(x, y)
// }
// fn({}) //undefined 10
// fn({ x: 12 }) //12 10
// fn({ x: 12, y: 23 }) //12 23
// fn() //annot destructure property 'x' of 'undefined' as it is undefined.
// 默认值的位置
function fn(x = 12, y) {
console.log(x, y)
}
fn() //12 undefined
fn(23) //23 undefined
// fn(,23) 报错
fn(undefined, 12) //12 12
四、数组扩展
扩展运算符
扩展运算符(spread)是三个点(
...
)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
// var arr1 = [1, 2, 3]
// console.log(...arr1) //1 2 3
// var arr2 = [3, 4, 5]
// var newArr = [...arr1, ...arr2]
// arr1.push(...arr2)
// console.log(arr1)
var o = {
name: '张三',
age: 12,
}
var obj = {
...o,
sex: '男',
}
console.log(obj)
// function show(a, b, c) {
// console.log(a, b, c)
// }
// let arr = [11, 22, 33]
// show(...arr)
// 不知道参数的个数
function demo(...args) {
console.log(args)
}
demo(1, 2, 3, 4, 5, 6, 7, 8)
Array.from
Array.from 把伪数组转换为真正的数组
// let arr = {
// 0: 'hello',
// 1: 123,
// 2: true,
// length: 3,
// }
// let arr2 = Array.from(arr)
// console.log(arr2) //['hello', 123, true]
let pArr = document.querySelectorAll('p')
let newArr = Array.from(pArr).filter((item) => {
return item.textContent.length > 3
})
console.log(newArr)
五,字符串控制
模板字符串
<body>
<ul id="oul">
<!-- <li>
<p>姓名:张三</p>
<p>年龄:12</p>
</li> -->
</ul>
<script>
function fn(age) {
var s = age > 20 ? '青年' : '未成年'
return s
}
var arr = [
{ id: 1, name: '张三', age: 12 },
{ id: 2, name: '李四', age: 22 },
{ id: 3, name: '王五', age: 33 },
{ id: 4, name: '赵六', age: 44 },
]
let str = ''
arr.forEach((item) => {
// str +=
// ' <li><p>姓名:' + item.name + '</p><p>年龄:' + item.age + '</p></li>'
str += `
<li>
<p>姓名:${item.name}</p>
<p>年龄:${item.age}</p>
<p>${fn(item.age)}</p>
</li>
`
})
var oul = document.querySelector('#oul')
oul.innerHTML = str
</script>
</body>
字符串的扩展
includes(字符串,start) 判断父串中是否包含子串的内容,返回布尔值
startsWith(字符串,start) 判断父串中是否以指定的子 串开头,返回布尔值
endsWith(字符串,start) 判断父串中是否以指定的子串结尾,返回布尔值
repeat() 重复指定的字符串,返回字符串
var str = 'how do you do'
console.log(str.includes('do', 4)) //true
console.log(str.startsWith('do', 4)) //true
console.log(str.endsWith('do', 6)) //true
var ch = 'o'
console.log(ch.repeat(3)) //"ooo"
console.log(ch.repeat(3.3)) //"ooo"
// console.log(ch.repeat(-3)) //报错 Invalid count value
console.log(ch.repeat(-0.3)) //""
Symbol
ES6新增了第7种基本数据类型Symbol
Symbol符号,象征,标记,记号,在js中独一无二的值
// symbol 是独一无二的
const s = Symbol()
const s1 = Symbol()
// console.log(typeof s) //symbol
// console.log(s === s1) //false
// Symbol方法接收一个参数,表示对生成symbol值的一种描述
// 传入相同的参数,生成的symbol的值也是不相等的
const foo = Symbol('foo')
const bar = Symbol('foo')
console.log(foo === bar) //false
作为属性名的 Symbol
由于每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果a[mySymbol] // "Hello!"
// [重点] 作为对象的属性 一个复杂的对象有多个属性的时候,很容易将某个属性名覆盖,利用symbol值作为属性名可以避免
const name = Symbol('name')
const obj = {
// 对象以变量作为属性名 用到[]
[name]: 'lucy',
}
Set 和 Map 数据结构
Set
基本用法
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成 Set 数据结构。
set 无序 元素不可重复
array 有序 元素可重复
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4
set常用的方法
set.add(value) 添加元素到集合内
set.delete(value) 删除指定的元素
set.clear() 清空集合内元素
set.forEach(callback) 遍历集合内所有元素
set.has(value) 检查集合内是否有某个元素
let set = new Set('hello')
// console.log(set)
set.add(12) //add 给set结构添加成员
console.log(set.has('h')) //true 判断set结构是否有某个成员
console.log(set.size) // 获取长度
set.delete(12) //删除指定的set结构成员
console.log(...set)
set.clear() //删除所有的成员
set的遍历
set集合没有键名 只有键值
let ss = new Set(['red', 'green', 'yellow', 'blue'])
// for (let item of ss.keys()) {
// console.log(item)
// // red
// // green
// // yellow
// // blue
// }
// for (let item of ss.values()) {
// console.log(item)
// // red
// // green
// // yellow
// // blue
// }
// for (let item of ss.entries()) {
// console.log(item)
// // ['red', 'red']
// }
ss.forEach((item) => {
console.log(item)
// red
// green
// yellow
// blue
})
// 实现数组去重
// let arr = new Set([3, 4, 5, 5, 2, 3, 2, 5]).values()
let arr = Array.from(new Set([3, 4, 5, 5, 2, 3, 2, 5]))
console.log(arr) // [3, 4, 5, 2]
map
含义和基本用法
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
es6提供了map数据结构,类似于对象 属性名(键)的范围不限于字符串,各种类型的值都可以作为键
const o = {}
const ele = document.querySelector('div')
o[ele] = 'element' //ele 被自动转换为字符串
const m = new Map()
const oo = { name: 'hello world' }
// 添加成员
m.set(oo, 'content')
console.log(m)
// 获取
console.log(m.get(oo)) //"content"
// 判断是否有该键
console.log(m.has(oo)) //true
// 删除
m.delete(oo)
console.log(m.size) // 0
// 一个key只能对应一个value 多次对一个key放入value 后面的值会把前面的值覆盖
var map = new Map()
map.set('a', '女')
map.set('a', '男')
console.log(map)
map和set的区别
1 具有极快的查找速度
// var arr = new Array()
// var set = new Set()
// var map = new Map()
// for (var i = 0; i < 30; i++) {
// arr[i] = i
// set.add(i)
// map.set(i, arr[i])
// }
// console.log('---------------------------')
// console.time()
// for (var j = 0; j < 30; j++) {
// arr.includes(j)
// }
// console.timeEnd()
// console.time()
// for (var j = 0; j < 30; j++) {
// set.has(j)
// }
// console.timeEnd()
// console.time()
// for (var j = 0; j < 30; j++) {
// map.has(j)
// }
// console.timeEnd()
2 map需要的是一个二维数组 set需要的是一个一维数组
map 和set都不需要键重复
const map = new Map([
['name', '张三'],
['age', 12],
])
const set = new Set([2, 3, 4, 5])
// map是键值对 set没有value只有key
// set去重
let s = new Set([1, 2, 1, 4, 5, 2, 3, 1, 4])
const arr = [...s]
// console.log(arr) //[1, 2, 4, 5, 3]
// map去重
let arr1 = [1, 2, 1, 4, 5, 2, 3, 1, 4]
const mm = new Map()
let res = arr1.filter((item) => !mm.has(item) && mm.set(item, 1))
console.log(res) //[1, 2, 4, 5, 3]
Proxy
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”
Proxy 实例的方法
get()
get
方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
get
方法的用法,上文已经有一个例子,下面是另一个拦截读取操作的例子。
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, propKey) {
if (propKey in target) {
return target[propKey];
} else {
throw new ReferenceError("Prop name \"" + propKey + "\" does not exist.");
}
}
});
proxy.name // "张三"
proxy.age // 抛出一个错误
//上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined
。
set()
set
方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。假定
Person
对象有一个age
属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy
保证age
的属性值符合要求。
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TypeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
// 对于满足条件的 age 属性以及其他属性,直接保存
obj[prop] = value;
return true;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' // 报错
person.age = 300 // 报错
Promise 对象
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。
Promise
对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
下面代码创造了一个Promise实例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
// success
}, function(error) {
// failure
});
then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise
对象的状态变为resolved时调用,第二个回调函数是Promise
对象的状态变为rejected时调用。这两个函数都是可选的,不一定要提供。它们都接受Promise对象传出的值作为参数。
下面是一个Promise对象的简单例子。
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});