增强的 Object 直接量
- 在直接量中定义 __proto__ 指向父对象
- foo:foo,可简写为 foo
- 调用 super
- 表达式动态生成属性名
var father = {
name: 'tellyourmad',
age: 21,
test: function () {
console.log(this.age);
},
};
function intr() {
for (var key in this) {
console.log(key + ':' + this[key]);
}
}
var child = {
__proto__: father, // child 继承 father
intr, // 代替了 intr:intr
saySomething() {
// 不用写成 saySomething:function(){...}
console.log(`my name:${this.name}`);
super.test(); // 调用父类方法
},
};
child.intr();
child.saySomething();
class Father {
constructor() {
this.a = '123';
}
output() {
console.log(this.a);
}
}
var child_1 = {
__proto__: Father.prototype,
say() {
super.output(); // 输出 undefined
},
};
var child_2 = {
__proto__: new Father(),
say() {
super.output(); // 输出123
},
};
解构
- 同时给多个变量赋值的简化写法
- 数组与对象同样适用
- 模式匹配
- 对象的合并
/*数组*/
var [a, b, c] = [1, 2, 3]; // 同时声明多个变量,左侧的中括号不是数组的意思,同理于var a=1,b=2,c=3
// 如果写成 var [a,,c]=[1,2,3] 那么 a=1,c=3 按下标赋值
var [head, ...tail] = [1, 2, 3, 4]; // head为1,tail为[2,3,4]
var [foo, [[bar], baz]] = [1, [[2], 3]]; // “模式匹配”,只要等号两边的模式相同,左边的变量会被赋予对应的值
var [a, [b], d] = [1, [2, 3], 4]; // a为1,b为2,d为4,解构依然成功,但是属于不完全解构
/*对象*/
var { m: month, y: year } = { y: 2016, m: 11 }; // month为11,year为2016,同理也是按下标赋值 //不是按顺序哦~~
var { a, b, ...others } = { a: 'aaa', b: 'bbb', c: 'ccc', d: 'ddd' }; // a为'aaa',b为'bbb',c为{c:'ccc',d:'ddd'}
function func({ name: x, age: y }) {
console.log(x, y);
}
var lilei = { name: 'lilei', age: 11 };
func(lilei);
var obj = { ...others, ...{ a: '123', b: '456' }, ...['a', 'b', 'c'] }; // 拼接,还可以将数组也拼进来
// {c:'ccc',d:'ddd',a:'123',b:'456',0:'a',1:'b',3:'c'}
默认值,rest,spread
- 函数设置参数默认值
function func(gender, name = 'tellyourmad') {
return `性别:${gender},名称:${name}`;
}
func('男'); // `性别:男,名称:tellyourmad`
- 使用 rest 简化批量传参
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(1, 2, 3); // 6
- spread(广播)(不用 apply 也能使函数可以接收数组参数)
function func(a, b) {
return a + b;
}
const data = [1, 4];
myFunction(...data); // 5
模块化
通过使用 import 和 export 运算符来实现
- 只导出一个
export default { a: 'aaa', b: 'bbb' };
import obj from '模块名称.js';
- 导出多个
export var port = 3000;
export function getAccounts(url) {
...
};
import { port, getAccounts } from '模块名称.js';
console.log(port); // 3000
- 冠名(易名)
import * as service from '模块名称.js';
console.log(service.port); // 3000
箭头函数
/*无需bind(this)*/
var a = 'abc';
var obj = {
a: 'aaa',
b: 'bbb',
func: function () {
var test = () => {
console.log(this.a);
};
test(); // 'aaa'
},
};
/*当函数体只有一条语句时,可以省略花括号*/
var func = () => console.log('111');
/*当省略花括号时,该单条语句则作为函数的返回值*/
var f1 = () => a++;
// 等价于
var f2 = function () {
return a++;
}.bind(this);
模板字符串
// 两个撇号'和${}
var a = 'aaaa',
b = 'bbbb';
var str = `
a的值是:${a}
b的值是:${b}
`;
类
/*声明一个类,作为父类*/
class BaseModel {
constructor(options = {}, data = []) {
// 类的构造方法
this.name = 'Base';
this.url = 'http://azat.co/api';
this.data = data;
this.options = options;
}
getName() {
// 类的方法
console.log(`Class name: ${this.name}`);
}
}
/*声明一个类,作为子类,继承BaseModal*/
class AccountModel extends BaseModel {
constructor(o) {
super(o, ['32113123123', '524214691']); //使用super调用父类构造方法
this.name = 'Account Model';
this.url += '/accounts/';
}
}
var obj = new AccountModel('oooo');
console.log(obj.options); // 'oooo'
console.log(obj.data); // []
obj.getName(); // 'Class name: Account Model' // 继承了父类的方法
Promise
- 写法
/*写法一*/
function connect() {
return new Promise(function (resolve, reject) {
if (true) {
resolve('456');
} else {
reject('123');
}
});
}
connect()
.then(function (a) {
console.log(a);
}) // resolve方法会进入then
.fail(function (a) {
console.log(a);
}) // reject方法会进入fail
.catch(function (err) {
console.log(err);
}); // 代码报错会进入catch
- 栗子
function connect() {
console.log('正在连接');
return new Promise(function (callback, onError) {
// 要返回Promise对象,才能用then
onError(new Error('连接出错')); // 设置错误
window.setTimeout(callback, 3000);
});
}
function query() {
console.log('正在查询');
return new Promise(function (callback) {
window.setTimeout(callback, 2000);
});
}
function update() {
console.log('正在更新');
return new Promise(function (callback) {
window.setTimeout(callback, 1000);
});
}
connect()
.then(function () {
// 将function(){console.log("链接成功");return query()}传给connect里面的callback
console.log('链接成功');
return query(); //return下一个Promise
})
.then(function () {
console.log('返回查询结果');
return update();
})
.then(function () {
console.log('更新成功');
})
.catch(function (err) {
// 在最后加catch的话,如果then中某处出现了错误,这不再继续执行下面的语句,直接执行catch,并且将错误信息传给catch
console.log(String(err));
});
let 和 const
let 为变量,const 为常量,其他特性一样,下面只拿 let 来阐述
- 块级作用域
/*通过let声明的变量只在代码块(也就是花括号)内有效*/
{
let test = 'aaaa';
}
console.log(test); // 报错
/*不遵循作用链*/
let a = 1;
+function () {
console.log(a); // 报错
};
- 不会有变量提升(通过 let 声明的变量不会发生“声明提前”)
console.log(a); // undefined
console.log(b); // 报错
var a = 1;
let b = 2;
- 在同一个代码块中,不能重复声明同一个变量
let a = 1;
let a = 2; // 报错
- 声明的变量仅在块级作用域内有效
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); //6
- 在 for 循环中,设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
for (let i = 0; i < 3; i++) {
let i = 'abc'; // 函数内部的变量i与循环变量不在同一个作用域
console.log(i); // 输出三次abc
}
- 暂时性死区
// 只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
// ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
for...of 循环
var arr = [10, 50, 10];
for (let i of arr) {
// 获得数组值
console.log(i); // 10,50,10
}
// for-of只适用于对数组进行遍历(ps:有些浏览器可能适用于对象,但这是不规范的)
// 若要使用for-of来遍历对象的话,可以先将对象转化为数组,如:
var obj = { a: 'aaa', b: 'bbb' };
for (let [k, v] of Object.entries(obj)) {
console.log('对象的key值是:' + k);
console.log('对象的value是:' + value);
}
// 又或者先将对象的n个属性组合成一个数组
for (let k of Object.keys(obj)) {
console.log('对象的key值是:' + k);
console.log('对象的value是:' + obj[k]);
}
for of 不能用来遍历对象,因为对象没有迭代器,即 Symbol.iterator
可以通过自己实现一个迭代器来使得对象能通过 for of 进行循环
Object.prototype[Symbol.iterator] = function() {
const keys = Reflect.ownKeys(this)
let pointer = 0
// 返回遍历器对象
return {
next: () => pointer < keys.length ? { value: this[keys[pointer++]] } : { done: true },
return: () => ({ done: true })
}
}