篇幅稍微有点长,因为记录了近期居家办公期间学习到的前端新特性,查ES6之后版本的资料得知后面的新特性是ES6的补充,不存在ES7、8、9、10等(Emm...),先mark下来再说。下面是笔记源码,包含18个点(新特性例子or用法)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My ES6 study page</title>
<style>
.highlight {
padding: 2px 5px;
background: #00abd5;
color: white;
}
</style>
<script src="https://cdn.bootcss.com/dompurify/1.0.3/purify.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<ul>
<li>Assassin's Creed</li>
<li>Prince of Persia</li>
<li>Devil May Cry</li>
<li>Prototype</li>
<li>Darksiders</li>
</ul>
<div style="margin: 10px 0;">
<button>ok</button>
<button>cancel</button>
<button>reset</button>
<button>submit</button>
<button>redirect</button>
</div>
<div class="content">
</div>
<script type="text/javascript">
// 1. 模板标签的使用
//--------------------------------------------------------------------------------------------------------
function highlight(string, ...values) {
// debugger;
// 变量高亮
const highlighted = values.map(value => `<span class="highlight">${value}</span>`);
const display = string.reduce((prev, curr, i) => `${prev}${curr}${highlighted[i] || ''}`, '');
// 使用第三方库防止XSS跨站脚本攻击
return DOMPurify.sanitize(display);
}
const user = 'Mary';
const topic = 'Learn to use markdown';
const sentence = highlight`${user} has commented on your topic ${topic}`;
document.querySelector('.content').innerHTML = sentence;
// 2. ES6 新增的字符串函数
//--------------------------------------------------------------------------------------------------------
console.log('the topic first string is \'Learn\': ', topic.startsWith('Learn'));
console.log('the topic sixth string is \'to\': ', topic.startsWith('to', 6));
console.log('the topic end string is \'markdown\': ', topic.endsWith('markdown'));
console.log('the topic twelfth end string is \'use\': ', topic.endsWith('use', 12));
console.log('the topic include string \'use\': ', topic.includes('use'));
console.log('repeat string \'哈\': ', '哈'.repeat(10));
// 3. 对象解构常用方式
//--------------------------------------------------------------------------------------------------------
// 对象解构,灵活定义默认参数
function appendChildDiv(option = {}) {
const {
parent = 'body',
width = '100px',
height = '80px',
backgroundColor = 'pink',
marginTop = '10px'
} = option;
const div = document.createElement('div');
div.style.width = width;
div.style.height = height;
div.style.backgroundColor = backgroundColor;
div.style.marginTop = marginTop;
document.querySelector(parent).appendChild(div);
}
appendChildDiv({
width: '200px',
height: '150px',
backgroundColor: 'blue'
});
// 4. 巧用数组解构交换变量值
//--------------------------------------------------------------------------------------------------------
let a = 10;
let b = 20;
[a, b] = [b, a];
console.log('a: ', a);
console.log('b: ', b);
// 5. 数组转化及使用for..of..来迭代操作
//--------------------------------------------------------------------------------------------------------
// 数组转化,并处理每个元素的值
const games_list = document.querySelectorAll('li');
const games = Array.from(games_list, li => li.textContent);
// 使用数组的迭代对象去便捷操作键值
for (let [index, game] of games.entries()) {
console.log('li dom text: ', `${index}. ${game}`);
}
// 6. 自构建Promise对象来实现异步链式操作
//--------------------------------------------------------------------------------------------------------
const humans = [
{id: 1, name: 'Jay Chou', age: 43, job: 'Singer & Actor', nationality: 'China'},
{id: 2, name: 'JJ Lin', age: 40, job: 'Singer', nationality: 'Singapore'},
{id: 3, name: 'Eddie Peng', age: 39, job: 'Actor', nationality: 'Canada'}
];
const resumes = [
{human: 'Jay Chou', works: 'Fantasy', date: '2001.9.14', style: 'pop'},
{human: 'JJ Lin', works: 'Sixology', date: '2008.10.18', style: 'pop'},
{human: 'Eddie Peng', works: 'The Rescue', date: '2020.12.18', style: 'action'}
];
function getHumanById(id) {
return new Promise((resolve, reject) => {
// 使用setTimeout来模拟异步请求耗时
setTimeout(() => {
const human = humans.find(human => human.id === id);
if (human) {
resolve(human);
} else {
reject(Error('Man not found'));
}
}, 5000);
});
}
function comboundResume(human) {
return new Promise((resolve, reject) => {
const resume = resumes.find(resume => resume.human === human.name);
if (resume) {
human.resumes = Array.of(resume);
resolve(human);
} else {
reject(Error('Resume not found'));
}
});
}
// 这里只是异步链式操作的形式,如果需要同步监听批量请求,
// Promise.all() 及 Promise.race() 可实现批量请求,
// 区别是前者为所有Promise返回resolve才会执行then(参数按顺序返回所有的结果)否则执行catch,
// 后者为只要其中一个首先返回resolve就会执行到then(参数只返回resolve的结果),全部返回reject才会执行catch
getHumanById(1).then(human => comboundResume(human)).then(human => {
console.log('The human object: ', human);
}).catch(error => {
console.error(error);
});
// 7. ES6新增的数据类型:Symbol
//--------------------------------------------------------------------------------------------------------
// Symbol可以用来生成唯一标识,同时不可遍历
// 使用Symbol来设置半隐藏属性(私有属性),利用闭包完全隐藏
const myObj = (() => {
const symbol = Symbol('sex');
class myObj {
constructor(name, age, sex) {
this[symbol] = sex;
this.name = name;
this.age = age;
}
getSex() {
return this[symbol];
}
static info() {
return `这是一个包含隐藏属性的类,想获取我的性别,请调用getSex方法`;
}
}
return myObj;
})();
const obj = new myObj("neo", 31, "男");
console.log('The obj object: ', Object.keys(obj)); // 获取不到sex
console.log('obj\'s sex: ', obj.getSex());
// 8. ES6的类定义语法糖
//--------------------------------------------------------------------------------------------------------
// 首先看一下ES5中的继承方式
function Animal(name) {
this.name = name;
}
function Dog(name, age) {
// 调用父类构造方法
Animal.call(this, name);
this.age = age;
}
// 指定原型对象为父类,并且将构造方法指定为函数本身
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
// ES6中继承就很简单了
// 类跟函数的不同在于函数会作用域提升,而类不会
class Cat extends Animal {
constructor(name, age) {
// 同样是调用父类构造方法,ES6中只需要一个super方法就搞定了
super(name);
this.age = age;
}
speak() {
console.log('meow~');
}
static info() {
console.log(`Hi, I'm a cat, my name is ${this.name}`);
}
set color(color) {
this.color = color;
}
get color() {
return this.color;
}
}
const fu = new Dog('fu', 1);
const lucky = new Cat('lucky', 2);
console.log('The fu object: ', fu);
console.log('The lucky object: ', lucky);
// 9. ES6生成器Generator
//--------------------------------------------------------------------------------------------------------
class GamesGenerator {
// 不使用生成器迭代,需要自定义next迭代方法返回value数据及done状态,代码量略多
/*constructor(list) {
this.list = list;
}
[Symbol.iterator]() {
let current = 0;
let that = this;
return {
next() {
return current < that.list.length ? {value : that.list[current++], done : false} : {done: true};
}
};
}*/
constructor(list) {
this.list = list;
// 定义生成器方法,需要在function后加上*,然后yield每次的返回值,并不需要关注迭代本身细节
this[Symbol.iterator] = function*() {
let current = 0;
while (current < this.list.length) {
yield this.list[current++];
}
}
}
}
const game_generator = new GamesGenerator(games);
for (let game of game_generator) {
console.log('GamesGenerator iterator value: ', game);
}
// 10. ES6生成器的应用
//--------------------------------------------------------------------------------------------------------
// 可以使用生成器去控制Ajax工作流
// 首先使用axios去ajax请求链接中的数据,然后将返回值带入到生成器中的next方法中
function ajax(url) {
axios.get(url).then(res => userGen.next(res.data));
}
// 然后在生成器方法中依次yield需要调用的接口,每次请求完的返回值会通过next方法传回来,通过定义变量存储后再利用
function* steps() {
console.log('Generator steps: fetching users');
const users = yield ajax('https://api.github.com/users');
console.log('Generator steps: ', users);
console.log('Generator steps: fetching firstUser');
const firstUser = yield ajax(`https://api.github.com/users/${users[0].login}`);
console.log('Generator steps: ', firstUser);
console.log('Generator steps: fetching followers');
const followers = yield ajax(firstUser.followers_url);
console.log('Generator steps: ', followers);
}
// 最后让生成器开始执行
const userGen = steps();
userGen.next();
// 11. ES6代理Proxy
//--------------------------------------------------------------------------------------------------------
const person = {name: 'fantasticbin', age: 32};
const personProxy = new Proxy(person, {
// 改变获取方式
get(target, key) {
if (key === 'mobile') {
return target[key].replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}
return target[key][0].toUpperCase() + target[key].substr(1);
},
// 改变设置方式
set(target, key, value) {
if (typeof value === 'string') {
target[key] = value.trim();
}
if (key === 'mobile') {
target[key] = value.match(/[0-9]/g).join('');
}
}
});
personProxy.name = ' fantasticbin ';
personProxy.mobile = '138 383 89438';
console.log('personProxy.name: ', personProxy.name);
console.log('personProxy.mobile: ', personProxy.mobile);
// 12. ES6新增的数据类型:Set
//--------------------------------------------------------------------------------------------------------
// Set的特点:和数组类似,值唯一,数字不会被转换成字符串,没有下标,获取长度不是用length而是size
// 可使用add、delete、has、clear等方法来进行操作,也可用for循环进行迭代
// 这里用Set来实现数组去重
const numbers = [1, 2, 3, 4, 5, 5, 5, 2];
const numbersSet = new Set(numbers);
const uniqueNumbers = [...numbersSet];
console.log('uniqueNumbers: ', uniqueNumbers);
// 13. ES6新增的数据类型:Map
//--------------------------------------------------------------------------------------------------------
// Map的特点:存储键值对,键可以是任意类型,获取长度不是用length而是size
// Map的操作方法跟Set差不多,一样有add、delete、has、clear等方法,也可用for循环进行迭代
// 这里用Map进行记录button的点击数
const clickCounts = new Map();
document.querySelectorAll('button').forEach(button => {
clickCounts.set(button, 0);
button.addEventListener('click', function () {
const count = clickCounts.get(this);
clickCounts.set(this, count + 1);
console.log('clickCounts: ', clickCounts);
});
});
// ES6新增的数据类型:WeakSet、WeakMap,跟Set、Map其实差不多,区别在于前者会根据成员是否存在引用而动态清除,但不能手动去改变成员
// WeakSet、WeakMap适用于内存监控、变量监控等场景,因使用的情况不会很多所以不做举例
// 14. ES7(7?)新增的Array.prototype.includes()
//--------------------------------------------------------------------------------------------------------
// ES7之前查找是否存在数组成员的方式
/**
* 方式1:indexOf()
* 不够直观,且无法对NaN进行正确判断
* [NaN].indexOf(NaN) === -1
*/
console.log('games.indexOf Darksiders: ', games.indexOf('Darksiders') !== -1);
/**
* 方式2:find() 和 findIndex()
* 依然不够直观,但相比indexOf好在可以对NaN进行正确判断
*/
console.log('games.find Darksiders: ', games.find(game => game === 'Darksiders') !== undefined);
console.log('games.findIndex Darksiders: ', games.findIndex(game => game === 'Darksiders') !== -1);
// ES7后直接使用includes方法就好
console.log('games.includes Darksiders: ', games.includes('Darksiders'));
// 15. ES7(7?)新增的求幂运算符
//--------------------------------------------------------------------------------------------------------
// ES7之前求幂方式
console.log('10th power of 2: ', Math.pow(2, 10))
// ES7可以使用**
console.log('10th power of 2: ', 2 ** 10);
// 16. ES8(8?)新增的async/await新特性
//--------------------------------------------------------------------------------------------------------
// 上面的例子中有Promise异步链式操作请求的示例,可以用来解决回调地狱的情况
// 但如果请求过多及每个请求依赖的上一个请求,代码还是不够清晰,比如:
/**
* 传入参数 n,表示这个函数执行的时间(毫秒)
* 执行的结果是 n + 200,这个值将用于下一步骤
*/
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
}
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(m, n) {
console.log(`step2 with ${m} and ${n}`);
return takeLongTime(m + n);
}
function step3(k, m, n) {
console.log(`step3 with ${k}, ${m} and ${n}`);
return takeLongTime(k + m + n);
}
// 首先看直接用Promise方式实现依次请求step1、step2、step3
function doItPromise() {
console.time('doItPromise');
let p1 = 300, p2, p3;
step1(p1)
.then((param) => step2(p1, p2 = param))
.then((param) => step3(p1, p2, p3 = param))
.then((param) => {
console.log('result is', param);
console.timeEnd('doItPromise');
});
}
// 再看用async/await实现
async function doIt() {
console.time('doIt');
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time1, time2);
const result = await step3(time1, time2, time3);
console.log('result is', result);
console.timeEnd('doIt');
}
// 其实async/await就是上面例子10-Generator函数的语法糖,区别就是用async替换了函数的*号,await替换了函数体中的yield,而且并不需要关注异步执行细节
// 阮一峰老师说过async函数对Generator函数的改进,并且列举了中途请求失败进入reject方法的处理方式,具体可参考:http://www.ruanyifeng.com/blog/2015/05/async.html
async function ajaxAsync(url) {
return axios.get(url).then(res => {
return new Promise(resolve => {
resolve(res.data);
})
});
}
async function stepsAsync() {
const users = await ajaxAsync('https://api.github.com/users').catch(err => {console.error(err)});
const firstUser = await ajaxAsync(`https://api.github.com/users/${users[0].login}`).catch(err => {console.error(err)});
const followers = await ajaxAsync(firstUser.followers_url).catch(err => {console.error(err)});
console.log('async followers: ', followers);
}
stepsAsync();
// 17. ES8(8?)新增的Object.values() 和 Object.entries()
//--------------------------------------------------------------------------------------------------------
console.log('The humans[0] object of values: ', Object.values(humans[0]));
for (let [attr, value] of Object.entries(humans[0])) {
console.log(`human[${attr}] : ${value}`);
}
// 18. ES8(8?)新增的String.prototype.padStart() 和 String.prototype.padEnd()
//--------------------------------------------------------------------------------------------------------
console.log('普京'.padStart(16, '弗拉基米尔·弗拉基米罗维奇·'));
console.log('基努'.padEnd(6, '·里维斯'));
console.log(personProxy.mobile.padStart(personProxy.mobile.length + 4, '+86 '));
</script>
</body>
</html>