傲娇:
新es是js的进步,是编程的进步,es6已经过去了5年了,兼容率达到了90%,还是有10%的手机不兼容,那到底应不应该去照顾那些跟不上的人,我觉得是不应该的,新es能5行写出来的功能,我为什么要用旧的写50行,还那么难理解,难维护,但我还是兼容了,人在屋檐下,不得不低头
Label是es向下兼容的工具,这个在node分类下提及,这里不说
所有的内容都来自 阮一峰大神的博客
2019年11月09日中午12点,博客里的内容从【0前言到31参考链接】
经常用的api有
- let和const
- 解构赋值 和 拓展运算符
- 字符串拓展
- 数字的拓展
- 函数的拓展 和 拓展运算符
- 数组的拓展 和 Iterator
- 对象的拓展
- 新类型Set 和 Iterator
- 新类型Map 和 Iterator
- Reflect
- Proxy观察者
- Promise
- async
- class
- module
let和const
- let代替了var,let没有变量提升,也可以理解为解析js代码流程中,遇到let会自动跳过,在执行过程中才能识别
- const不是变量,而是固定常亮,如果是基础数据类型第一次赋值后不能被修改值,如果是复杂数据类型,可以修改内部的内容,但是不能修改数据类型,而且一个函数作用域里只能声明一次,也就是不能声明两次aa,会报错,能保护这个值不轻易被另一个程序员重新声明或者改了
还有些详细内容放在下一篇写
解构赋值
解构赋值分为数组解构和对象解构
数组解构,是按顺序来的,并且左边用数组解构,右边一定要是数组
// 基础应用
// 可以理解为右边的数组是ajax请求来的res
let [a, b, c] = [1, 2, 3];
console.log(a,b,c)
// 跳过使用
let [ , , c] = ["foo", "bar", "baz"];
console.log(c) // "baz"
// 二级数组
let [a, b, d] = [1, [2, 3], 4];
console.log(b) //[2,3]
// 拓展运算符
// 拓展运算符一次赋值只能用一次,而且只能用在最后一位
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
对象解构
对象解构是用key赋值的,没有顺序
对象解构比数组解构用得多得多
// 基础应用
// 可以理解为右边的数组是ajax请求来的res
let { bar,foo,ceshi} = { foo: 'aaa', bar: 'bbb' };
console.log(bar,foo,ceshi) // 'bbb','aaa',undefined
// 上面的写法其实是对象的省略写法,因为key-value同名所以缩写
// 如果不缩写是 let { bar:bar,foo:foo,ceshi:ceshi} = { foo: 'aaa', bar: 'bbb' };
// 所以不缩写可以重命名
let { bar:newBar,foo:newFoo,ceshi:newCeshi} = { foo: 'aaa', bar: 'bbb' };
console.log(newBar,newFoo,newCeshi) // 'bbb','aaa',undefined
// 默认值
var {x = 1} = {};
console.log(x) //1
// 上面的默认值也是省略写法
var {x:newX = 1} = {x: 5};
console.log(newX) //5
//清除对象里不要的key
let {_internal, tooBig, ...cleanObject} = {el1: '1', _internal:"secret", tooBig:{}, el2: '2', el3: '3'};
console.log(cleanObject); // {el1: '1', el2: '2', el3: '3'}
字符串拓展
上代码自己理解
var basket = {
count: 10,
onSale: "aa"
}
// 原先的字符串拼接
$('#result').append(
'There are <b>' + basket.count + '</b> ' +
'items in your basket, ' +
'<em>' + basket.onSale +
'</em> are on sale!'
);
// 新版的字符串拼接,两端是``斜点
// 数据是${...},
// 可以用默认值 ${ name || "pdt" }
// 可以用简单的方法${ time2date(time) }
$('#result').append(`
There are <b>${basket.count}</b> items
in your basket, <em>${basket.onSale}</em>
are on sale!
`);
数字的拓展
之前把字符串转数字用的是减零
Number("123")
// 转不了的就是NaN
Number("123a") //NaN
函数的拓展
有参数默认值,参数解构,参数拓展运算符,箭头函数
// 参数默认值
// 以往的参数默认值是这样的
function init(name){
var name = name || "pdt"
}
// es6可以
function init(name = "pdt"){ ... }
// 参数解构
// 老版本
function init(arr,obj){ ... }
init([1,2],{name:"pdt",age:18})
// 新写法
function init([,b],{name="无名"}){
//比如我只需要数组的第二个和name属性
console.log(a,b,name)
}
init([1,2],{name:"pdt",age:18})
//参数必填
const required = () => {throw new Error('Missing parameter')};
const add = (a = required(), b = required()) => a + b;
add(1, 2) //3
// 箭头函数,是function的缩写
// 但是不能作为对象的value,不能作为数组的值,不能作为构造函数
// 箭头函数没有自己的this
// 箭头函数没有三个改变this的方法
// 箭头函数没有arguments
// 声明
var init = () => { ... }
// 作为参数
function init(cb){
cb(111)
}
// 旧版
init(function(num){ console.log(num )})
// 箭头函数
init((num)=>{ console.log(num })
箭头函数是用来留住this的,具体查看下一篇
数组的拓展
新es给给数组增加了一个叫Iterator
的东西,这个查看《遍历总结》
新增了大量的遍历方式,这个查看《遍历总结》
还有拓展运算符代替function的apply方法(apply是什么查看《上下文和作用域》)
// ES5取数组的最大值
Math.max.apply(null, [14, 3, 77])
// ES6取数组的最大值
Math.max(...[14, 3, 77])
// ES5合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6合并数组
[...arr1, ...arr2, ...arr3]
// ES5把伪数组转成数组
Array.prototype.slice.apply(null,ArrayLike)
// ES6把伪数组转成数组
[...ArrayLike]
// 或者新api
Array.from(ArrayLike)
// ES5查看是否含有,返回第一个出现的位置,没有是-1
arr.indexOf("xx")
// ES6查看是否含有,返回true/false
arr.include("xx")
对象的扩展
// 最后一个key-value可以加逗号了
var obj = {
name: "name",
age: 18, //以前这里结束加逗号是不行的
}
// key-value同名可以省略
// 比如要ajax传个参数
var name = "name",age = 18;
ajax({
// 以前
// data:{ name: name, age: gae }
// 现在可以
data:{name,age}
})
// 对象的value是function的写法优化
// 老写法
var obj = {
say: function(){ ... }
}
// 新写法
var obj = {
say(){ ... }
}
// 对象的遍历,查看《遍历的总结》
// 增加可Obj.vaules() 和 Object.entries()
// 对象的合并assign,这个api是浅拷贝
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
// 也可以
let target = {...target, ...source1}
新类型Set【集合】
Set跟数组差不多,同样是一个畸形的对象
他会自动去重,但只能去重基础数据类型
// 需要通过new去创建,参数是一个数组或者伪数组
let set = new Set([undefined,undefined,NaN,NaN,"",""]); // set{"undefined",NaN,""}
// set的长度属性
set.size //3
// 添加某个值,返回set本身,所以可以无限add()
set.add(value)
// 删除某个值,返回一个布尔值,表示删除是否成功
set.delete(value)
// 返回一个布尔值,表示该值是否为Set的成员
set.has(value)
// 清除所有成员,没有返回值
set.clear()
// Set没有办法取值,只能转数组再取值
[...set]
// 或者
Array.from(set)
// Set的遍历,set.keys(),set.values(),set.entries()
// 具体查看《遍历总结》
// 还有一个WeakSet,不重要
// 他跟Set一样,只是他只能存对象,并且带有垃圾回收功能,面试题来着
const ws = new WeakSet();
集合的几个特性
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));//ES6
var intersect = new Set([...a].filter(function(x){
return b.has(x);
}))
// set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
新类型Map【字典】
Map 结构提供了value-value的对应,相比于Object,他的key可以是任一类型
// 通过new创建,参数是一个数组,数组里的值必须都是两个长度的数组
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
// map的长度属性
map.size // 2
// 添加某个值,返回set本身,所以可以无限add()
map.set(key, value)
// 返回一个布尔值,表示该值是否为Set的成员
set.has(key)
//
map.get(key)
// 删除某个值,返回一个布尔值,表示删除是否成功
map.delete(key)
// 清除所有成员,没有返回值
map.clear()
// Map的遍历,map.keys(),map.values(),map.entries()
// 具体查看《遍历总结》
// Map转数组,其实map挺好用的,没必要转,除非是要传给后端
[...map]
// 还有一个WeakMap,不重要
// 只接受对象作为键名(null除外),不接受其他类型的值作为键名
const wm = new WeakMap();
其实Set和Map也非常的稀有,因为数组和对象就已经很好用了,而且还要去兼容,而且还要去转格式转来转去的
Reflect
Reflect是ES6为了操作对象而新增的API,配合proxy对数据进行操作简直完美
Reflect.get(target, name, receiver)
Reflect.set(target,name,value,receiver)
Reflect.apply(target,thisArg,args)
Reflect.construct(target,args[, newTarget])
Reflect.defineProperty(target,name,desc)
Reflect.deleteProperty(target,name)
Reflect.has(target,name)
Reflect.ownKeys(target)
Reflect.preventExtensions(target)
Reflect.isExtensible(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
Proxy观察者
这个一般来说是用不到的,是属于架构层面的api了,Vue3.0就是以这个为基础架构的,跟Object.defineProperty一样,这个放在《vue的原理设计》说
// 贴个基础代码
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
Promise
查看《promise篇》
Promise里装的都应该是异步函数,如果是同步的反而是产生BUG
// 贴个基础代码
new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
}).then((value)=>{ ... },(error)=>{ ... })
Async
查看《promise篇》
await后面跟着的都是promise异步函数,如果是同步的反而是产生BUG
// 贴个基础代码
async function main() {
try {
const val1 = await firstStep();
const val2 = await secondStep(val1);
const val3 = await thirdStep(val1, val2);
console.log('Final: ', val3);
}
catch (err) {
console.error(err);
}
}
class
查看《class》篇
class就是一个新型的构造函数
// 贴个基础代码
class A {
constructor(name) {
// 静态属性/方法
this.name = name;
}
// 相当于原型方法,class不支持原型属性
print() {
console.log(this.name);
}
// 带有static是私有方法/属性,通过A.myMethod()执行
static myMethod() {
console.log('static');
}
}
class B extends A {
constructor(name,age) {
//继承的
super();
this.name = name;
this.age= age;
}
m() {
console.log(this.age);
}
}
let b = new B("pdt",18);
b.m() // 18
b.print() // "pdt"
module
查看《模块化》篇
引入js文件的新api但是不被支持,浏览器没支持连nodejs都没支持
// 开放多个,只能全是export,不能有export default
// fs.js
export function A(){ ... }
export B = { ... }
export C = []
export D = "D"
// 引入并且解构赋值
import { A, B, C, D } from 'fs';
// 只能出现一个export default,并且不能有其他export
// default就不需要名字了
// fs.js
export default function(){ ... }
// 或者对象,其他格式都行
export default {}
// 引入并且解构赋值
import fs from 'fs';
除了上面常用的
还有Symbol,Decorator【装饰器】
- Method Decorator 函数装饰器
- Property Decorators 熟悉装饰器
- Class Decorator 类装饰器
- Parameter Decorator 参数装饰器
将来装饰器会变成新的框架,会被制造出像Spring
的框架
说几句
这些api升级了js的难度,生产出了大量的玩法和框架,他让前端不再是个jq打天下的菜鸡,他需要学的东西越来越多,这些api高仿自很多的语言,特别是java
比如说拓展运算符,Iterator遍历器,Set,Map,await的原生就是一个yeild,这个也是java的,还有class,model的import,Decorator就是java的注解以及NodeJs的stream流等等