函数的扩展
ES6 对函数进行了扩展,新的函数支持定义默认值、扩展运算符、写法更加简洁的箭头函数等。
函数参数的默认值(3 种形式)
1.函数默认值
let fn = function (x = 1, y = 2) {
return x + y;
};
fn(); // 3
// 代替函数体内判断的写法
let fn = function (x, y) {
x = x || 1;
y = y || 2;
return x + y;
};
fn(); // 3
2.函数默认值——解构参数
可以通过解构变量来定义函数参数的默认值,减少参数的个数
let fn = function ({ x = 1, y = 2 }) {
return x + y;
};
fn(); // 3
3.函数默认值与解构赋值结合
注意:前面这两种方法都存在一个问题,如果只使用第二个参数,就必须给第一个参数传入undefined
来指示第一个参数的默认值,通过解构赋值和函数默认值结合的形式可以避免这样的问题。
// 既可以不传入参数,也可以指定参数调用
let fn = function ({ x = 1, y = 2 } = {}) {
return x + y;
};
fn({ y: 2 }); // 3
实例:默认参数与解构赋值混合使用
let fn = function (x = 1, { y = 2, z = 3 } = {}) {
return x + y + z;
};
fn(5); // 10
rest 参数与拓展符
ES6 也支持了函数 rest 形式的参数(使用者不需要考虑参数的个数),rest 参数必须跟在函数最后面,实例如下:
let fn = function (...values) {
return values.join(" ");
};
fn("hello", "i'm", "waynezheng"); // hello i'm waynezheng
扩展运算符的灵活运用
// 数组合并
let arr = [...[1, 2, 3], ...[4, 5, 6]]; // [1, 2, 3, 4, 5, 6]
// 拆分字符串
let str = [..."hello"]; // ['h', 'e', 'l', 'l', 'o']
注意:实现了iterator
接口的对象,都可以实用扩展运算符进行解构。
箭头函数
ES6 引入的箭头函数,使函数的定义方式更加简洁,同时也解决了this
绑定的问题。
基本用法
let fn = (x) => x;
// 等价于
let fn = function (x) {
return x;
};
解决this
指向的问题
let fn = function () {
this.name = "fn";
setTimeout(() => {
console.log(this.name); // fn
}, 1000);
};
fn();
// 代替用that赋值this
let fn = function () {
let that = this;
setTimeout(function () {
this.name = "fn";
console.log(this.name); // undefined
console.log(that.name); // fn
}, 1000);
};
fn();
对象的扩展
ES6 让对象的定义更加简洁,同时引入了一些常见的第三方框架的功能,实例如下:
简洁的写法
1.返回对象简写
let fn = function () {
let x, y;
return { x, y };
};
// 等价于
let fn = function () {
let x, y;
return {
x: x,
y: y,
};
};
2.方法简写
let obj = {
sayName() {
return "wynne";
},
};
3.配合 import,export 的模块化输出简写
let foo = {
sayName() {},
};
let bar = {
sayName() {},
};
exports = { foo, bar };
// 等价于
exports = {
foo: foo,
bar: bar,
};
Object 上的扩展函数
1.严谨的值比较
let a = NaN;
let b = NaN;
// 旧的比较
a === b; // false
0 === -0; // true
Object.is(a, b); // true
Object.is(0, -0); // false
2.对象的浅复制
// 克隆一个对象
let obj = { a: 1 };
let clone = Object.assign({}, obj);
3.扩展默认属性(代替 jQuery 的 extend 功能)
const DEFAULT_PARAM = {
url: "localhost",
method: "get",
};
let getBooks = function (options) {
let param = Object.assign({}, DEFAULT_PARAM, options);
};
getBooks({ data: { a: 1, b: 2 } }); // { url: 'localhost', method: 'get', data: {a: 1, b: 2} }
4.拓展已有对象
// 假设原有代码存在这样一个对象
let oldObj = function() {
... // 几乎不可维护的代码
}
oldObj.prototype = {
method() {
...
}
}
Object.assign(oldObj.prototype, {
newMethod() {
...
}
})
Promise 对象
Promise 是异步编程的解决方案,很简单的说,使用场景就是解决多层嵌套回调函数的问题(简称回调地狱),下面是一些实例:
解决回调地狱问题
有时候可能会遇到这么些需求,第三个请求的参数依赖于第二个请求的结果,第二个请求的参数依赖于第一个请求的结果,而这一过程只能在前端发起,那么可能会写出这样的代码:
$.get(url1, param, function (res) {
let param = res.value;
$.get(url2, param, function (res) {
let param = res.value;
$.get(url3, param, function (res) {
// 到了第三层才是在编写业务逻辑
});
});
});
试想,如果其中一个函数错误,程序就会中断,因此还需要给每个请求加上 error 的回调函数,写出来的代码嵌套了一层又一层。看看用 Promise 来书写这段代码是怎么样的:
// 假设get函数内部实现了promise
$.get(url1, param)
.then(function (res) {
let param = res.value;
return $.get(url2, param); // 返回promise对象
})
.then(function (res) {
let param = res.value;
return $.get(url3, param); // 返回promise对象
})
.then(function (res) {
// 业务逻辑编写
})
.catch(function (error) {
// 统一处理回调发生的错误
});
封装函数支持 promise
编写一个 promise 的方法也很简单,下面上实例:
// 以$.ajax为例
function ajaxGet(url, param) {
return new Promise((resolve, reject) => {
$.ajax(
url,
param,
function (res) {
if (res.state == 200) {
resolve(res);
} else {
reject("请求不成功!");
}
},
function (error) {
reject(error);
}
);
});
}
// 使用
ajaxGet(url, param)
.then((res) => {
// 业务逻辑,处理resolve
})
.catch((err) => {
// 处理reject
console.log(err);
});
// 这样也可以
ajaxGet(url, param).then(
(res) => {
// 业务逻辑,处理resolve
},
(err) => {
// 处理reject
}
);
// 只要返回的是promise对象,则可以做链式调用
ajaxGet(url, param)
.then((res) => {
// 业务逻辑,处理resolve
return ajaxGet(url2, res);
})
.then((res) => {
// 上面的ajaxGet函数的处理
})
.catch((err) => {
// 处理reject
console.log(err);
});
包装多个 promise
Promise.all()
支持将多个Promise
实例,包装成一个新的Promise
,使用场景也很广泛。比如需要多个异步操作返回数据后进行处理时:
let promises = [1, 2, 4, 5].map((post_id) => {
return ajaxGet("post/", { id: post_id });
});
Promise.all(promises)
.then((resultArray) => {
// 对所有数据做处理
})
.catch((error) => {
// 处理第一个出现reject的情况
});
数据降级
合理的利用catch
方法,可以使数据有更好的处理方式。比如说,某一个模块数据的加载,由于第一次请求服务器抖动了,请求失败,这时可以降级从缓存中取得数据;要是缓存不小心也挂了,可以从localStorage
中获取缓存的数据,例子如下:
function getDataFromlocalStorage() {
if (window.localStorage && window.localStorage["DATA"]) {
return new Promise.resolve(window.localStorage["DATA"]);
} else {
return new Promise.reject();
}
}
function getDataFromCache() {
return getDataCache(url);
}
function parseData(data) {
return JSON.parse(data);
}
function getData() {
let promise = getDataFromServer(url)
.then(parseData) // 正常业务逻辑
.catch(function () {
// 请求失败,从服务器缓存取数据
return getDataFromCache().then(parseData);
})
.catch(function () {
// 服务器缓存取数据失败,从localstorage取数据
return getDataFromlocalStorage().then(parseData);
})
.catch(function () {
// 没办法了,什么数据都没有,只能处理异常了
});
}
Class
ES6 的Class
可以视为创建类的语法糖(但本质上实现方式是不一样的),Class
的使用会比以往的prototype
直观得多:
1.类的基本使用方法
class Person {
static is_live = true; // 静态属性
constructor(name, age) {
this.name = name;
this.age = age;
}
get name() {
return this.name;
}
set name(name) {
this.name = name;
}
sayName() {
alert(this.name);
}
static eat() {
alert("eat done");
}
}
let person = new Person("wynne", 23);
person.sayName(); // wynne
Person.eat(); // eat done
2.类的继承
class Man extends Person {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
get name() {
return this.name;
}
set name(name) {
this.name = name;
}
sayName() {
alert(this.name);
}
}
模块化
在 ES6 还没有标准化之前,模块化有 AMD 和 CMD 两种方式,AMD 使用于浏览器端,CMD 使用于服务端,相对应的类库有 requirejs 和 common.js,但是有了 ES6 的模块,当然是使用 ES6 的。
export 命令
export
可以输出常量,变量,函数,对象,使用方式很简单:
export var firstName = 'Michael';
export const A = 1;
export function multiply(x, y) {
return x * y;
};
export {
name: 'wynne'
};
export class q {
constructor() {
this.es6 = 'hello';
}
}
import 命令
import
可以将上面的export
输出的导入到指定文件中使用,方式也很简单:
import { es6 } from "./someModule";
import { lastName as surname } from "./profile"; // 取别名
该系列完结啦!(如有变动会更新) 如有错误请帮我指出下,感激不尽~