Sun, 24 Apr 2016 15:35:12 +0000

阅读阮一峰老师的《ECMAScript 6入门》

let 和 const

  1. temporal dead zone 为了防止运行时错误,防止在变量声明前就使用了这个变量,从而导致意料之外。
  2. 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