ES6(一)
阅读阮一峰老师的《ECMAScript 6入门》
let 和 const
- temporal dead zone 为了防止运行时错误,防止在变量声明前就使用了这个变量,从而导致意料之外。
- let 块级作用域可以减少很多潜在的危害,比如说 for 循环里定义的 i,还有 switch 里定义的变量,出了这一块作用域就不应该被用到。
1
2
3
4
5
6
7
{
let insane = 'Hello World0'; console.log('0', insane);
{
let insane = 'Hello World1'; console.log('1', insane);
}
console.log('2', insane);
}
ES6 的函数作用域也是块级作用域。
const 命令只是保证变量名指向的地址不变,并不保证该地址的数据不变。 有坑,如果
1
2
3
4
5
6
7
const foo = {};
foo.prop = 123;
const a = [];
a.push("Hello"); // 可执行
a.length = 0; // 可执行
a = ["Dave"]; // 报错
可以用 const foo = Object.freeze({});
对象属性彻底冻结
1
2
3
4
5
6
7
8
let constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, value) => {
if ( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
不能使用圆括号
- 变量生命语句种,不能带有圆括号
- 函数参数中,模式不能带有圆括号
- 赋值语句中,不能将整个模式,或嵌套模式中的一层,防止圆括号之中
变量的解构赋值总结
- 数组的解构赋值
- 对象
- 字符串
- 数值和布尔值
- 函数参数
- 圆括号
- 用途
Unicode
"\u0061"
// "a"
PS: 编码问题是一个比较大的坑,占坑不说 http://es6.ruanyifeng.com/#docs/string
正则
第一个参数是正则对象,第二个参数是指定修饰符。
new RegExp(/abc/ig, 'i').flags
四个字节的 UTF-16 需要使用 u 修饰符。
点(.)字符在正则表达式中,表示的是除了换行符(\n)外的任意单个字符。但是对于码点大于 0xFFFF 的 Unicode 字符,点字符不能识别,必须加上 u 修饰符。
var s = '𠮷';
/^.$/.test(s) // false
/^.$/u.test(s) // true
大括号可以表示 Unicode 字符
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
\s
匹配所有空格字符,\S
预定义模式,匹配所有不是空格的字符,加了 u 修饰符才能正确匹配码点大于 0xFFFF 的 Unicode 字符。
function codePointLength(text) {
var result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
var s = '𠮷𠮷';
s.length // 4
codePointLength(s) // 2
i 修饰符:大小写不敏感的匹配。
y 修饰符:我理解的是变成 /^xx/ 的正则表达式。
// 没有找到匹配
'x##'.split(/#/y)
// [ 'x##' ]
// 找到两个匹配
'##x'.split(/#/y)
// [ '', '', 'x' ]
'##x'.split(/^#/)
// ["", "#x"]
'##x'.split(/^#/g)
// ["", "#x"]
flags属性
// ES5的source属性
// 返回正则表达式的正文
/abc/ig.source
// "abc"
// ES6的flags属性
// 返回正则表达式的修饰符
/abc/ig.flags
// 'gi'
字符串转义
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
}
let str = '/path/to/resource.html?search=query';
escapeRegExp(str)
JavaScript 只支持先行断言与先行否定断言,不支持后行断言(lookbehind)和后行否定断言。 『先行断言(lookahead)』指的是,x 只有在 y 前面才匹配,必须写成 /x(?=y)/。 『先行否定断言(negative lookahead)』/x(?!y)/。
/\d+(?=%)/.exec('100% of US presidents have been male') // ["100"]
/\d+(?!%)/.exec('that’s all 44 of them') // ["44"]
『experiment JavaScript features』开关 enable(地址栏:about:flags) 『后行断言』,x 只有在 y 后面才匹配,必须写成 /(?<=y)x/。
/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill') // ["100"]
/(?<!\$)\d+/.exec('it’s is worth about €90') // ["90"]
数值的扩展
严格模式下,八进制不再允许使用前缀 0 表示,可以用 0o。 二进制,八进制前缀分别为 0b(或0B)和0o(或0O)。 可用 Number 方法转为十进制。
// Number.isFinite
(function (global) {
var global_isFinite = global.isFinite;
Object.defineProperty(Number, 'isFinite', {
value: function isFinite(value) {
return typeof value === 'number' && global_isFinite(value);
},
configurable: true,
enumerable: false,
writable: true
});
})(this);
// Number.isNaN()
(function (global) {
var global_isNaN = global.isNaN;
Object.defineProperty(Number, 'isNaN', {
value: function isNaN(value) {
return typeof value === 'number' && global_isNaN(value);
},
configurable: true,
enumerable: false,
writable: true
});
})(this);
在JavaScript内部,整数和浮点数是同样的储存方法,所以 3 和 3.0 被视为同一个值。
// Number.isInteger()
(function (global) {
var floor = Math.floor,
isFinite = global.isFinite;
Object.defineProperty(Number, 'isInteger', {
value: function isInteger(value) {
return typeof value === 'number' && isFinite(value) &&
value > -9007199254740992 && value < 9007199254740992 &&
floor(value) === value;
},
configurable: true,
enumerable: false,
writable: true
});
})(this);
Number.EPSILON 一个接受误差的范围。
0.1 + 0.2 - 0.3 < Number.EPSILON
// true
误差检查函数
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON;
}
withinErrorMargin(0.1 + 0.2, 0.3)
// true
withinErrorMargin(0.2 + 0.2, 0.3)
// false
JavaScript 能够准确表示的整数范围在 -2^53 到 2^53 之间,(不含端点值)。
Math.pow(2, 53) // 9007199254740992
9007199254740992 // 9007199254740992
9007199254740993 // 9007199254740992
Math.pow(2, 53) === Math.pow(2, 53) + 1
// but
9007199254740994 // 9007199254740994
Math.pow(2, 53) + 2 // 9007199254740994
ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。Number.isSafeInteger() 则是用来判断一个整数是否落在这个范围之内。
Number.isSafeInteger = function (n) {
return (typeof n === 'number' &&
Math.round(n) === n &&
Number.MIN_SAFE_INTEGER <= n &&
n <= Number.MAX_SAFE_INTEGER);
}
Notice:
Number.isSafeInteger(9007199254740993)
// false
Number.isSafeInteger(990)
// true
Number.isSafeInteger(9007199254740993 - 990)
// true
9007199254740993 - 990
// 返回结果 9007199254740002
// 正确答案应该是 9007199254740003
Math 扩展
- Math.trunc 去除一个数的小数部分,返回整数部分。
- Math.sign() 判断一个数到底是正数、负数还是零。
- Math.cbrt 计算一个数的立方根。
- Math.clz32() 返回32 位无符号整数形式有多少个前导 0。
- Math.imul 返回两个数以32位带符号整数形式相乘的结果。(存疑)
- Math.fround 返回一个数的单精度浮点数形式。
- Math.hypot 返回所有参数的平方和的平方根。
对数
- Math.expm1 返回 e^x - 1 即 Math.exp(x) - 1。
- Math.log1p 返回 1 + x 的自然对数即 Math.log(1 + x)。
- Math.log10 返回以 10 为底的 x 的对数。
- Math.log2 返回以 2 为底的 x 的对数。
三角函数
- Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
- Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
- Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
- Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
- Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
- Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)
指数运算
2 ** 2 // 4
2 ** 3 // 8
let a = 2;
a **= 2 // a = a * a
let b = 3;
b **= 3; // b = b * b * b
数组的扩展
Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。 用 Array.of() 来替代 Array() 还有 new Array()。 数组实例 copyWithin,将指定位置的成员复制到其他位置。
find 用于找出第一个符合条件的数组成员。findIndex 返回的是位置。用法相同。
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 9;
}) // 10
识别数组的NaN
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
数组有 entries,keys,values 方法。与 for…of 配合使用。
数组 includes 方法,有语义化。
Map 结构的 has 方法,可以用来查找键名。
Set 结构的 has 方法,用来查找值。
Array(3) 返回一个具有3个空位的数组,空位没有任何值,不是 undefined。
ES5 forEach(), filter(), every() 和some()都会跳过空位。 map()会跳过空位,但会保留这个值 join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
// forEach方法
[,'a'].forEach((x,i) => 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。
由于空位的处理规则非常不统一,所以建议避免出现空位。
函数的扩展
参数设置默认值,ES6 的方式更为友好,简洁。
双重默认值,因为不能省略第二个参数。
function fetch(url, { method = 'GET' } = {}) {
console.log(method);
}
fetch('http://example.com')
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = {x: 0, y: 0}) {
return [x, y];
}
函数的 length 属性将返回没有指定默认值的参数个数。 rest 参数也不会记入 length 属性
(function(...args) {}).length // 0
设置了默认值的参数与其之后的参数不记入 length。
如果函数 A 的参数默认值是函数 B,由于函数的作用域是其声明时所在的作用域,那么函数 B 的作用域不是函数 A,而是全局作用域。
使用 rest 参数(…变量名),就不需要使用 arguments。
// arguments
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest
const sortNumbers = (...numbers) => numbers.sort();
rest 参数只能作为最后一个参数。
// ES5
Math.max.apply(null, [14, 3, 77]);
// ES6
Math.max(...[14, 3, 77]);
Math.max(14, 3, 77);
// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]));
//ES6
new Date(...[2015, 1, 1]);
用来合并数组更简单。
结构函数
const [first, ...rest] = [1, 2, 3, 4, 5];
返回多个值的时候可以用解构函数。
可以将字符串转为真正的数组。
Map & Generator
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()];
var go = function*() {
yield 1;
yield 2;
yield 3;
};
[...go()];
注意:没有 iterator 接口的对象,不能使用扩展运算符(…values)
(new Function).name // "anonymous"
// ES5,ES6 不一样。
var func1 = function() {};
// ES5
func1.name // ''
// ES6
func1.name // 'func1'
// ES5,ES6 一样。
const bar = function baz() {}
// ES5
bar.name // 'baz'
// ES6
bar.name // 'baz'
function foo () {}
foo.bind({}).name; // 'bound foo'
(function(){}).bind({}).name; // 'bound'
箭头函数,注意事项
- 函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用 new 命令。
- 不可以使用 arguments 对象,可以用 rest 参数替代。
- 不可以使用 yield 命令,箭头函数不能用作 Generator 函数。
sample:
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
普通函数,执行时 this 应该指向全局对象 window,这时应该输出 21。但是,箭头函数导致 this 总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是 42。
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出错误。
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
foo(123)
// 123
参数的默认值不是在定义时执行,而是在运行时执行(即如果参数已经赋值,默认值中的函数就不会运行)。可以讲参数默认值设为 undefined,表明参数可省略。
console.log(...[1, 2, 3])
// 1 2 3
扩展运算符取代 apply 方法:
// ES5 写法
Math.max.apply(null, [14, 3, 77]);
// ES6 写法
Math.max(...[14, 3, 77]);
// 等同于
Math.max(14, 3, 77);
push 写法:
// 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);
Date 相关的:
// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]));
// ES6
new Date(...[2015, 1, 1]);
Others
[1, 2].concat(more);
[1, 2, ...more];
注意:扩展运算符用于数组赋值,只能放在参数的最后一位。
// 字符串转数组
[...'hello'];
// 能识别 32 位 Unicode 字符
'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
function name
var func1 = function() {}
// ES5
func1.name // ''
// ES6
func1.name // 'func1'
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
(new Function).name // "anonymous"
function foo() {};
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
continue ….
blog comments powered by Disqus