对阮一峰老师-ECMAScript 6 入门的学习笔记
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
// let 声明的变量,只在命令所在的代码块内有效。
{
{
let a = 10;
var b = 1;
}
//a // ReferenceError: a is not defined.
//b // 1
// let不像var那样会发生“变量提升”现象。所以,变量一定要在声明后使用,否则报错。
{
console.log(foo); // 输出undefined
//console.log(bar); // 报错ReferenceError
var foo = 2;
let bar = 2;
}
// const 作用域与let相似,一旦声明不能重新赋值
{
const PI = 3.1415;
PI
}
}
{
{
// es6中变量可以这样声明,这种方式称为解构(Destructuring)。
let [c, d, e] = [1, 2, 3];
console.log(c+","+d)
}
{
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
}
{
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
}
{
let [x, , y] = [1, 2, 3];
x // 1
y // 3
}
{
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
}
{
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
}
{
//let [foo] = 1;
//let [foo] = false;
//let [foo] = NaN;
//let [foo] = undefined;
//let [foo] = null;
//let [foo] = {};
}
{
// 允许默认值
var [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
// [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
console.log(x + "," + y);
}
{
// 可以解构对象
var { bar, foo } = { foo: "aaa", sec: "bbb", bar: "bbb" };
foo // "aaa"
bar // "bbb"
var { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
}
{
let { log, sin, cos } = Math;
}
{
// 函数参数也可以解构
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
}
/* 解构实用功能 */
{
// 变换变量的值
let [x, y] = [1, 2];
[x, y] = [y, x];
console.log(x + "," + y);
}
{
// 函数返回多个值
// 返回一个数组
/*
function example() {
return [1, 2, 3];
}
var [a, b, c] = example();
*/
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = example();
}
{
// 提取JSON数据
var jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
}
{
// 遍历map结构
var 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
}
{
// 加载模块时,往往需要指定输入那些方法。解构赋值使得输入语句非常清晰。
// const { SourceMapConsumer, SourceNode } = require("source-map");
}
}
/* 字符串的扩展 */
{
// 如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript会理解成\u20BB+7。由于\u20BB是一个不可打印字符
// ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
{
"\u{20BB7}"
// "��"
"\u{41}\u{42}\u{43}"
// "ABC"
let hello = 123;
hell\u{6F} // 123
'\u{1F680}' === '\uD83D\uDE80'
// true
}
// codePointAt
{
var s = '��a';
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
}
{
String.fromCodePoint(0x20BB7)
// "��"
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true
}
// 除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。
{
var text = String.fromCodePoint(0x20BB7);
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
// " "
// " "
for (let i of text) {
console.log(i);
}
// "��"
}
// ES5对字符串对象提供charAt方法,返回字符串给定位置的字符。该方法不能识别码点大于0xFFFF的字符。
{
// 'abc'.at(0) // "a"
// '��'.at(0) // "��"
}
{
var s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
console.log(s.startsWith('Hello'));
console.log(s.endsWith('!'));
console.log(s.includes('o'));
}
// repeat方法返回一个新字符串,表示将原字符串重复n次。
{
'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) //
console.log('x'.repeat(3));
console.log('x'.repeat(-0.5)); // "" (-1,0]
//'na'.repeat(Infinity)
// RangeError
// 'na'.repeat(-1)
// RangeError
}
// ES7推出了字符串补全长度的功能。
// 如果某个字符串不够指定长度,会在头部或尾部补全。padStart用于头部补全,padEnd用于尾部补全。
{
// 'x'.padStart(5, 'ab') // 'ababx'
// 'x'.padStart(4, 'ab') // 'abax'
// 'x'.padEnd(5, 'ab') // 'xabab'
// 'x'.padEnd(4, 'ab') // 'xaba
}
{
// 普通字符串
`In JavaScr
ipt '\n'is a line-feed.`
console.log(`In JavaScript this is
not legal.`);
// 多行字符串
//`In JavaScript this is
// not legal.`
console.log(`string text line 1
string text line 2`);
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
console.log(`Hello ${name}, how are you ${time}?`);
}
{
var x = 1;
var y = 2;
`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"
console.log(`${x} + ${y} = ${x + y}`);
`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"
console.log(`${x} + ${y * 2} = ${x + y * 2}`);
var obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// 3
}
// 模板之中还可以调用函数
{
function fn() {
return "Hello World";
}
`foo ${fn()} bar`
// foo Hello World bar
}
{
// 写法一
let str = 'return ' + '`Hello ${name}!`';
let func = new Function('name', str);
func('Jack') // "Hello Jack!"
// 写法二
// let str = '(name) => `Hello ${name}!`';
// let func = eval.call(null, str);
// func('Jack') // "Hello Jack!"
}
// 模板嵌套
{
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
}
{
var template = `
<ul>
<% for(var i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
/*
echo('<ul>');
for(var i=0; i < data.supplies.length; i++) {
echo('<li>');
echo(data.supplies[i]);
echo('</li>');
};
echo('</ul>');
*/
var evalExpr = /<%=(.+?)%>/g;
var expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
console.log(template);
/*
// 封装成一个方法返回
var script =
`(function parse(data){
var output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;*/
}
// “标签”指的就是函数,紧跟在后面的模板字符串就是它的参数。
{
// alert`123`
// 等同于
// alert(123);
}
// 如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数。
{
var a = 5;
var b = 10;
//tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
//tag(['Hello ', ' world ', ''], 15, 50);
/*
* tag函数的第一个参数是一个数组,
* 该数组的成员是模板字符串中那些没有变量替换的部分,
* 也就是说,变量替换只发生在数组的第一个成员与第二个成员之间、
* 第二个成员与第三个成员之间,以此类推。
*/
function tag(stringArr, value1, value2){
// ...
}
// 等同于
function tag(stringArr, ...values){
// ...
}
}
{
var message =
SaferHTML`<p>& has sent you a message.</p>`;
function SaferHTML(templateData) {
var s = templateData[0];
for (var i = 1; i < arguments.length; i++) {
var arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
console.log(message);
}
// String.raw方法可以作为处理模板字符串的基本方法,
// 它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。
{
String.raw`Hi\n${2+3}!`;
// "Hi\\n5!"
String.raw`Hi\u000A!`;
// 'Hi\\u000A!'
}
}
{
//返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
{
var regex = new RegExp(/xyz/, 'i');
}
}
// Array.from方法用于将两类对象转为真正的数组:
// 类似数组的对象(array-like object)和可遍历(iterable)的对象
{
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
}
// 对于还没有部署该方法的浏览器,可以用Array.prototype.slice方法替代。
{
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();
}
{
let spans = document.querySelectorAll('.name');
// map()
let names1 = Array.prototype.map.call(spans, s => s.textContent);
// Array.from()
let names2 = Array.from(spans, s => s.textContent)
console.log(names1)
}
// 将数组中布尔值为false的成员转为0。
{
Array.from([1, , 2, , 3], (n) => n || 0)
}
// Array.of方法用于将一组值,转换为数组
{
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]
}
/**
target(必需):从该位置开始替换数据。
start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
*/
{
// 将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
}
// 数组实例的find()和findIndex()
{
[1, 4, -5, 10].find((n) => n < 0)
// -5
/*[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10*/
/*// 返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2*/
}
// 数组实例的fill()
{
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
// fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
//['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
}
{
for (let index of ['a', 'b'].keys()) {
console.log(index);
};
// 0
// 1
/*for (let elem of ['a', 'b'].values()) {
console.log(elem);
}*/
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
};
// 0 "a"
// 1 "b"
}
// Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似
{
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
[1, 2, NaN].includes(NaN); // true
console.log([1, 2, 3].includes(6))
}
{
// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true); // ['a','b']
// every方法
[,'a'].every(x => x==='a'); // true
// some方法
[,'a'].some(x => x !== 'a'); // false
// map方法
[,'a'].map(x => 1); // [,1]
// join方法
[,'a',undefined,null].join('#'); // "#a##"
// toString方法
[,'a',undefined,null].toString(); // ",a,,"
//ES6 明确将空位转为undefined。
Array.from(['a',,'b']);
[ "a", undefined, "b" ];
}
// ES6 参数可以使用默认值
{
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
}
{
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined, 5
foo({x: 1}) // 1, 5
foo({x: 1, y: 2}) // 1, 2
//foo() // TypeError: Cannot read property 'x' of undefined
}
//双重默认值
{
function fetch(url, { method = 'GET' } = {}) {
console.log(method);
}
fetch('http://example.com')
// "GET"
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
m1({x: 3}) // [3, 0]
}
// 作用域
{
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
var z = 1;
function f(z, y = z) {
console.log(y);
}
f(2) // 2
}
// 省略参数,抛出错误
{
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
//foo()
// Error: Missing parameter
}
// ES6引入rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了
{
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数的写法
const sortNumbers2 = (...numbers) => numbers.sort();
console.log(sortNumbers([2,3,1]))
console.log(sortNumbers2([2,3,1]))
}
// 扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。
{
console.log(...[1, 2, 3]);
// 1 2 3
console.log(1, ...[2, 3, 4], 5);
// 1 2 3 4 5
[...document.querySelectorAll('div')];
// [<div>, <div>, <div>]
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
var numbers = [4, 38];
add(...numbers) // 42
}
// 由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
{
// ES5的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f(...args);
}
// 简化求出一个数组最大元素的写法。
{
// ES5的写法
Math.max.apply(null, [14, 3, 77])
// ES6的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
}
{
// ES5的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6的写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);
}
// 合并数组
{
let more = [5];
// ES5
[1, 2].concat(more);
// ES6
[1, 2, ...more];
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
}
{
const [first, ...rest] = [1, 2, 3, 4, 5];
first; // 1
rest; // [2, 3, 4, 5]
const [first2, ...rest2] = [];
first2; // undefined
rest2; // []:
const [first3, ...rest3] = ["foo"];
first3; // "foo"
rest3; // []
}
// 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
{
//const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错
//const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错
}
{
[...'hello']
// [ "h", "e", "l", "l", "o" ]
var nodeList = document.querySelectorAll('div');
var array = [...nodeList];
}
//只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
{
// 报错
/*function doSomething(a, b = a) {
'use strict';
// code
}*/
}
</script>
</head>
<body>
<span class="name">注释的代码表示会报错,或者当前浏览器不支持</span>
</body>
</html>