官方文档
一些老版本浏览器对es6不识别,所以可以用babel去吧es6转化为es5,
安装转化工具
npm install -g babel-cli
转化指定目录
npx babel src --out-dir lib
或者可以在package.json的script中使用
"build": "babel src --out-dir dist"
新建.babelrc
{
"presets": [
"es2015"
],
"plugins": []
}
使用命令
npm run build
作用:
与var类似, 用于声明一个变量;
var 特点:声明的变量会被提升;
特点:
1.不会预处理, 不存在提升;
2.在块作用域 { } 内有效;
定义一个常量, 不能修改,保存不用改变的数据(常量)
从数组或对象中提取值, 对应赋值给多个变量
let [a,b] = [1, 'atguigu'];
console.log(a, b) // 1, 'atguigu'
let person = {
name: '吴杰',
age: 20
}
let {name, age, sex} = person
console.log(name, age, sex) // 吴杰 20 undefined
如果对象变量名与属性名不一致,这样写
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
`名字:${obj.name} 年龄:${obj.age} 'sdsads'`
省略同名的属性值
let obj = {
a, //省略同名的属性值
b,
setA (x) { //省略function关键字
this.a = x;
}
}
console.log(obj);
特点
1、简洁
2.它不像其他函数似的有原型对象,他没有,但他有隐式原型
const foo=()=>{}
console.log(foo.prototype);//undifind
console.log(foo.__proto__);//ƒ () { [native code] }
3、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在**定义
**的时候处在的对象就是它的this,而正常的函数则是在调用时才能确定他的this。
4、扩展理解: 箭头函数的this看外层的是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,则this是window。
let obj = {};
function foo1() {
let fn9 = () => console.log(this); //window
fn9();
}
foo1();
function foo2() {
let fn9 = () => console.log(this); //obj
fn9();
}
foo2.call(obj);
let fn10 = () => console.log(this); //window
function foo3() {
fn10();
}
foo3.call(obj);
let fn11 = () => {
console.log(this); //window
let fn12 = function () {
console.log(this); //obj
let fn13 = () => {
console.log(this); //obj
let fn14 = () => {
console.log(this); //obj
}
fn14();
}
fn13.call(window)
}
fn12.call(obj);
}
fn11()
let obj1 = {
sayName(name) {
let fn = () => {
console.log(this); //obj1
}
fn();
}
}
obj1.sayName('bob');
function fn(...values) { // values为真数组,arguments为伪数组
}
fn(1, 2, 3)
// 数组
const arr1 = [1,2,3]
const arr2 = [4, ...arr1,6]
console.log(arr2) // [4, 1, 2, 3, 6]
// 对象
var obj={name: 'zhaohui', age: 18}
var newObj = {...obj, sex: 'NU'}
console.log(newObj) // {name: "zhaohui", age: 18, sex: "NU"}
let fn = (x = 1, y = 2) => x + y;
console.log(fn()); //3
将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称’回调地狱’)
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(res => {}).catch(() => {}).finally()
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,
并且会首先打印出来。这是因为立即 resolved 的Promise 是在本轮事件循环的
末尾执行,总是晚于本轮循环的同步任务。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
function promise1() {
return new Promise((resolve, reject) => {
resolve('你好,我是promise1成功')
})
}
function promise2() {
return new Promise((resolve, reject) => {
reject('我是失败的promise2')
})
}
Promise.all([promise1(), promise2()]).then(res => {
console.log(res)
}).catch(err => {
console.log('失败', err) // 失败 我是失败的promise2
})
function promise1() {
return new Promise((resolve, reject) => {
resolve('你好,我是promise1成功')
})
}
function promise2() {
return new Promise((resolve, reject) => {
reject('我是失败的promise2')
})
}
Promise.allSettled([promise1(), promise2()]).then(res => {
console.log(res) // [{status: 'fulfilled', value: '你好,我是promise1成功'},{status: 'rejected', reason: '我是失败的promise2'}]
}).catch(err => {
console.log('失败', err)
})
function promise1() {
return new Promise((resolve, reject) => {
resolve('你好,我是promise1成功')
})
}
function promise2() {
return new Promise((resolve, reject) => {
reject('我是失败的promise2')
})
}
Promise.any([promise1(), promise2()]).then(res => {
console.log(res) // 你好,我是promise1成功
}).catch(err => {
console.log('失败', err)
})
const p = Promise.race([p1, p2, p3]);
如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。
const p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
前言:
ES5中对象的属性名都是字符串,容易造成重名,污染环境
ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、大整数(BigInt)、对象(Object))
作用:
由于以 Symbol 值作为键名,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
特点:
1、Symbol属性对应的值是唯一的,解决命名冲突问题; 对象中的symbol属性必须通过 [ ],取/赋值;
let symbolName = Symbol("name");
let symbolAge = Symbol("age");
let person = { name: "lala" };
person[symbolName] = "Bob";
person[symbolAge] = 18;
console.log(person, person[symbolName], person[symbolAge], person.name);
// {name: 'lala', Symbol(name): 'Bob', Symbol(age): 18} 'Bob' 18 'lala'
2、Symbol值不能与其他数据进行计算,包括同字符串拼串
let symbolName = Symbol("name")
console.log(symbolName + 'lala') // 报错Uncaught TypeError: Cannot convert a Symbol value to a string
3、for in, for of遍历时不会遍历symbol属性。(虽然symble中的那种接口叫symbol.iterator遍历,但是它不给自己用啊)
let symbolName = Symbol("name")
let person = { sex: "nan" }
person[symbolName] = "Bob"
for (let key in person) {
console.log(key); //只会打印sex
}
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
const s1 = Symbol.for('name');
const s3 = Symbol.for('name'); //如果全局没有name标识名称的symbol,会新建一个,如果有,使用之前的
console.log(s1==s3)//true;
const s1 = Symbol('name');
const s3 = Symbol('name');
console.log(s1==s3)//false;
const sym = Symbol('foo');
sym.description // "foo"
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
概念:
iterator(指针对象或者遍历器对象)是一种接口机制,为各种不同的数据结构提供统一的访问机制:任何一种数据结构,只要部署了symbol.iterator 接口,就可以利用for…of命令完成遍历操作。(我理解的就是一个遍历机制)
作用:
1、为各种数据结构,提供一个统一的、简便的访问接口;
2、使得数据结构的成员能够按某种次序排列;
3、ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。
工作原理:
创建一个指针对象(遍历器对象),指向数据结构的起始位置。
第一次调用next方法,指针自动指向数据结构的第一个成员
接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
每调用 next() 方法:返回的是一个包含value和done的对象,
{value: 当前成员的值,done: 布尔值}
value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
当遍历结束的时候返回的 value 值是 undefined,done 值为 true
eg: …三点运算符就是通过调用 symbol.iterator 接口实现的
打印数组原型上有symbol.iterator,对象没有,所以对象不能用for of 遍历–但是后期可以借助generator函数实现。
项目中不会用到这个,已经被async替代了
概念:
1、ES6提供的解决异步编程的方案之一
2、Generator函数是一个状态机,内部封装了不同状态的数据,
3、用来生成遍历器对象
4、可暂停函数(惰性求值), yield 可暂停,next 方法可启动。
调用 next 方法函数内部逻辑开始执行,遇到 yield 表达式停止,
能够将iterator接口部署到对象上去–直接for of 对象会报错,借助Generator就可以使对象能用for of 了
const person = {
name: 'jack',
age: 18,
sex: '男'
}
person[Symbol.iterator] = function* () {
for (let key in this) {
yield this[key];
}
}
console.log(person);
for (let value of person) {
console.log(value);
}
async function foo(){
await 异步操作;--promise对象
await 异步操作;;--promise对象
}
示例
目前: 打印结果为
【fn函数开始调用了
a数据请求完毕了~
失败 456】
若promise1 为resolve,打印结果
【fn函数开始调用了
a数据请求完毕了~
result 456
fn函数结束调用了
成功 123】
综上可以看到如果是reject,函数下面就不执行了,直接到.catch了, err就为reject里的值----- 毕竟都不会走到最下面return,所以跟return无关,,,若为resolve,那result为resolve的值, .then里面的res就为return的值
const promise1 = new Promise((resolve, reject) => {
setTimeout(function () {
console.log('a数据请求完毕了~');
reject(456);
}, 2000)
})
async function fn() {
console.log('fn函数开始调用了');
const result = await promise1;
console.log('result', result);
console.log('fn函数结束调用了')
return 123;
}
const promise = fn();
promise
.then(res => {
console.log('成功', res);
})
.catch(err => {
console.log('失败', err);
})
写法
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
// 上面这种写法,等价于下面class类的写法
// 类的所有方法都是相当于定义在原型上
class Point {
//constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
//一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会
//被默认添加。里面这个this.**是直接定义在属性上的
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
getName() {
console.log(Person.name) // Person
}
}
function Point(name) {
this.name = name
}
Point.prototype.setLa = function() {
console.log(this.name) // a
}
// const s = new Point、Person('a')
console.log(Object.keys(Person.prototype), Object.keys(Point.prototype)) // [] ['setLa']
console.log(Object.getOwnPropertyNames(Person.prototype)) // ['constructor', 'getName']
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
let inst = new MyClass();
console.log(inst.getClassName()); // Me
Me.name // ReferenceError: Me is not defined
定义函数也可以访问到name
class Point {}
Point.name // "Point"
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
console.log(Foo.classMethod()) // 'hello'
var foo = new Foo();
console.log(foo.classMethod())// TypeError: foo.classMethod is not a function
注意:如果静态方法包含this关键字,这个this指的是类,而不是实例。
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod() // 'hello'
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
setName() {
console.log('lalala')
}
}
class ColorPoint extends Point {
constructor(x, y, color) { // 如果只是继承Point 的x,y属性,没有多余的就直接不写这个constructor
// 也行,但是如果有多余的就得写constructor了,super就是继承的属性,别的就直接自己写this.color = color
// this.color = color; // ReferenceError, 报错,这里不能用this
super(x, y);
this.color = color; // 正确
}
// 继承的也直接继承了上面那个setName的方法,但是这个方法是在原型的原型上才有
// 咱们在自己这写的run方法就在原型上
run() {
console.log('run')
}
}
let cp = new ColorPoint(25, 8, 'green');
console.log('cp', cp)
//实例对象cp同时是ColorPoint和Point两个类的实例
console.log(cp instanceof ColorPoint) // true
console.log(cp instanceof Point) // true
// Object.getPrototypeOf一个类是否继承了另一个类。
Object.getPrototypeOf(ColorPoint) === Point// true
class IncreasingCounter {
#count = 0;
increment() {
console.log(this.#count)
}
}
const counter = new IncreasingCounter();
counter.increment() //这个可以正常打印
console.log(counter.#count)// 这个会报错
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text) {
console.log(text);
}
}
const logger = new Logger();
// 这样写会报错 // TypeError: Cannot read property 'print' of undefined
// const { printName } = logger;
// printName()
// 这样才会正常打印
logger.printName() // Hello there
1.拦截读取
var proxy = new Proxy({name: '张三', age: 18}, {
get: function (target, property) {
return 35;
}
});
console.log(proxy.name, proxy.age)// 35, 35
2.如果handler没有设置任何拦截,那就等同于直接通向原对象。
var target = {a: 'b'};
var handler = {};
var proxy = new Proxy(target, handler);
console.log(proxy.a) // "b"
// 上面代码中,handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target。
let template = `
<% for(let i=0; i < data.supplies.length; i++) { %>
- <%= data.supplies[i] %>
<% } %>
`
说明:该模板使用<%...%>放置 JavaScript 代码,使用<%= ... %>输出 JavaScript 表达式。
Set容器 : 无序不可重复的多个value的集合体 === 【对标数组】
方法:
特点:
[...new Set(arr)]
无序的, key不重复的多个key-value的集合体 === 对标对象
其实就是因为对象属性必须是字符串类型,但是这个map相当于扩充了,key可以为任何类型
方法:
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
等同于上方
const firstName = message?.body?.user?.firstName || 'default';
如果某个属性的值是null或undefined, 指定默认值。
这种做法属性的值如果为空字符串或false或0,默认值也会生效。
const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;
若只想只有运算符左侧的值为null或undefined,才会返回右侧的值
const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;
// 老的写法
user.id = user.id || 1;
// 等同于
// 新的写法
user.id ||= 1;
// 与赋值运算符
x &&= y
// 等同于
x && (x = y)
// Null 赋值运算符
x ??= y
// 等同于
x ?? (x = y)