本人是android开发的,由于最近React Native的火热,再加上自己完全不懂JS的语法,俗话说的好"落后就要挨打",虽然不知道谁说的,不过很有道理.
学习书籍《ECMAScript 6 入门 》
各种类型的扩展
正则的扩展
RegExp构造函数
第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)
var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;
第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。
var regex = new RegExp (/xyz/i);
// 等价于
var regex = /xyz/i;
但是,ES5不允许此时使用第二个参数,添加修饰符,否则会报错。
var regex = new RegExp (/xyz/, 'i');
// Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
ES6改变了这种行为。如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/abc/ig, 'i').flags
// "i"
上面代码中,原有正则对象的修饰符是 ig,它会被第二个参数 i 覆盖。
u修饰符
ES6对正则表达式添加了u修饰符,含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码。
一旦加上u修饰符号,就会修改下面这些正则表达式的行为。
var s = '𠮷';
/^.$/.test(s)
// false
/^.$/u.test(s)
// true
上面代码表示,如果不添加u修饰符,正则表达式就会认为字符串为两个字符,从而匹配失败。
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true
上面代码表示,如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配61个连续的u。
使用u修饰符后,所有量词都会正确识别码点大于0xFFFF的Unicode字符。
/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true
有些Unicode字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
上面代码中,不加u修饰符,就无法识别非规范的K字符。
y 修饰符
除了u修饰符,ES6还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。
y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"]
r2.exec(s) // null
上面代码有两个正则表达式,一个使用g修饰符,另一个使用y修饰符。这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a。由于g修饰没有位置要求,所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。
与y修饰符相匹配,ES6的正则对象多了sticky属性,表示是否设置了y修饰符。
var r = /hello\d/y;
r.sticky // true
ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符。
// ES5的source属性
// 返回正则表达式的正文
/abc/ig.source
// "abc"
// ES6的flags属性
// 返回正则表达式的修饰符
/abc/ig.flags
// 'gi'
目前,有一个提案,引入了一种新的类的写法\p{...}和\P{...},允许正则表达式匹配符合Unicode某种属性的所有字符。
由于Unicode的各种属性非常多,所以这种新的类的表达能力非常强。
const regex = /^\p{Decimal_Number}+$/u;
regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // true
上面代码中,属性类指定匹配所有十进制字符,可以看到各种字型的十进制字符都会匹配成功。
\p{Number}甚至能匹配罗马数字。
// 匹配所有数字
const regex = /^\p{Number}+$/u;
regex.test('²³¹¼½¾') // true
regex.test('㉛㉜㉝') // true
regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
数值的扩展
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。
0b111110111 === 503 // true
0o767 === 503 // true
如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。
ES6引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限。
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true
Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// true
Number.isSafeInteger()则是用来判断一个整数是否落在这个范围之内。
Number.isSafeInteger(3) // true
Number.isSafeInteger(1.2) // false
Number.isSafeInteger(9007199254740990) // true
Number.isSafeInteger(9007199254740992) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false
Math对象的扩展
Math.trunc()方法用于去除一个数的小数部分,返回整数部分。
Math.trunc(4.1)// 4
Math.sign()方法用来判断一个数到底是正数、负数、还是零。
它会返回五种值。
参数为正数,返回+1;
参数为负数,返回-1;
参数为0,返回0;
参数为-0,返回-0;
其他值,返回NaN。
Math.cbrt()方法用于计算一个数的立方根。
Math.clz32()方法返回一个数的32位无符号整数形式有多少个前导0.(JavaScript的整数使用32位二进制形式表示)。
Math.imul()方法返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。
Math.imul(2, 4) // 8
Math.imul(-1, 8) // -8
Math.imul(-2, -2) // 4
Math.fround()方法返回一个数的单精度浮点数形式。
Math.fround(0) // 0
Math.fround(1) // 1
Math.fround(1.337) // 1.3370000123977661
Math.fround(1.5) // 1.5
Math.fround(NaN) // NaN
Math.hypot()方法返回所有参数的平方和的平方根。
Math.hypot(3, 4); // 5
Math.hypot(3, 4, '5'); // 7.0710678118654755
Math.expm1(x)返回ex- 1,即Math.exp(x) - 1。
等......太多了,放链接:
函数的扩展
1.rest参数
ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。下面是一个利用 rest 参数改写数组push方法的例子。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
// 报错
function f(a, ...b, c) {
// ...
}
函数的length属性,不包括 rest 参数。
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
2.扩展运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
任何Iterator接口的对象,都可以用扩展运算符转为真正的数组。
Map结构
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
Generator函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
var go = function*(){
yield 1;
yield 2;
yield 3;
};
[...go()] // [1, 2, 3]
如果对没有iterator接口的对象,使用扩展运算符,将会报错。
3.lambda演算
箭头函数还有一个功能,就是可以很方便地改写λ演算
首先说一下 lambda演算;
lambda演算写法
λx.x + 2
使用JavaScript来写,如下:
(function(x) {
return x + 2;
}) ();
两个lambda合并写法
(λx.x*x)(λx.x+2)
让我们来算算看:
x = 3 时,λx.x*x 的 输出为 9
9 做为 λx.x+2 的 输入,成为 9 + 2,输出为 11
答案就是 11
(λx.x*x)(λx.x+2) 等于 9 + 2。
箭头函数改写lambda演算 如下:
// λ演算的写法
fix = λf.(λx.f(λv.x(x)(v))) (λx.f(λv.x(x)(v)))
// ES6的写法
var fix = f => (x=>f(v=>x(x)(v)))
(x=>f(v=>x(x)(v)));
对象的属性的遍历
ES6一共有5种方法可以遍历对象的属性。
(1)for...in
for...in循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)。
(2)Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含Symbol属性,但是包括不可枚举属性)。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有Symbol属性。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管是属性名是Symbol或字符串,也不管是否可枚举。
以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。
首先遍历所有属性名为数值的属性,按照数字排序。
其次遍历所有属性名为字符串的属性,按照生成时间排序。
最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
Null 传导运算符
编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取message.body.user.firstName,安全的写法是写成下面这样。
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
这样的层层判断非常麻烦,因此现在有一个提案,引入了“Null 传导运算符”(null propagation operator)?.,简化上面的写法。
const firstName = message?.body?.user?.firstName || 'default';
上面代码有三个?.运算符,只要其中一个返回null或undefined,就不再往下运算,而是返回undefined。
“Null 传导运算符”有四种用法。
obj?.prop// 读取对象属性
obj?.[expr]// 同上
func?.(...args)// 函数或对象方法的调用
new C?.(...args)// 构造函数的调用
传导运算符之所以写成obj?.prop,而不是obj?prop,是为了方便编译器能够区分三元运算符?:(比如obj?prop:123)。