About
本文只记录了一些我比较感兴趣的新增方法,以及特性,更加详细的介绍请参考阮一峰的《ECMAScript 6 入门》
一、数值的扩展
1. 进制表示法
- 0b 二进制
- 0o 八进制
- 0d 十进制
- 0x 十六进制
如果要将其它进制字符串数值转为十进制,要使用Number方法。
Number('0b111') // 7
Number('0o10') // 8
Number(0x11) // 17
2. Number.parseInt() 和 Number.parseFloat()
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
3. Number.isInteger()
Number.isInteger()
用来判断一个数值是否为整数。JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
如果传入的参数不是数值,这里不会发生自动转换,而是直接返回false
:
Number.isInteger() // false
Number.isInteger(null) // false
Number.isInteger('15') // false
Number.isInteger(true) // false
4. Number.EPSILON
Number
对象新增了属性EPSILON
,这个属性其实是一个数字即2的-52次方,用来表示JavaScript的最小精度。我们可以把它用在浮点运算中用来设置运算精度。
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2) // 误差范围2的-50次方
}
0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true
1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true
二、Math对象上的扩展
ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。
1. Math.trunc()
Math.trunc方法用于去除一个数的小数部分,返回整数部分。
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // -4
Math.trunc(-4.9) // -4
Math.trunc(-0.1234) // -0
如果参数不是数值,那么会先使用Number
方法将其转为数值,然后在取整,如果无法转为数值,或者参数为空,就返回NaN
,对于没有部署这个方法的环境,可以用下面的代码模拟。
Math.trunc = Math.trunc || function(x) {
return x < 0 ? Math.ceil(x) : Math.floor(x);
};
2. Math.hypot()
Math.hypot方法返回所有参数的平方和的平方根。用来做勾股定理不要太爽~
Math.hypot(3, 4); // 5
Math.hypot(3, 4, 5); // 7.0710678118654755
如果传入的参数有一个无法转为数字就会返回NaN
3. Math.log10(),Math.log2()
分别是求以10为底的对数和以2为底的对数,可以用以下方法替代:
Math.log2 = Math.log2 || function(x) {
return Math.log(x) / Math.LN2;
};
Math.log10 = Math.log10 || function(x) {
return Math.log(x) / Math.LN10;
};
4. 新增指数运算符
ES2016 新增了一个指数运算符(**)。
2 ** 2 // 4
2 ** 3 // 8
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
三、函数的扩展
1. 函数参数的默认值
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。比如在函数体内通过typeOf
检测形参是否为空,如果为空就赋给形参默认值,这样写起来不仅代码非常臃肿且十分丑陋,其他语言都能给参数默认值,而在JavaScript中却不能,所以ES6 允许我们给函数参数传入默认值。
function print (x, y = 'world') {
console.log(`${x} ${y}`)
}
print('hello') // hello world
1.1 使用参数默认值时,变量不能重名
例如:
function foo(x, x, y) {
console.log(x,x,y)
}
foo(1,2,3) // 2 2 3
function foo(x, x, y = 3) {
console.log(x,x,y)
}
foo(1,2,3) // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
1.2 参数默认值是惰性求值的
参数默认值不是传值的,而是每次都重新计算默认值表达式的值。
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
形参p
虽然有默认值,但是并非定义时就已经计算出来,而是等到调用时才计算该默认值。
1.3 与解构赋值结合使用
如果想在形参上使用结构赋值,那么必须要给他们赋一个默认值{}
function foo({x, y}) {
console.log(x, y)
}
foo() // 报错
function foo({x, y} = {}) {
console.log(x, y)
}
foo() // undefined undefined
foo({x:1, z:2}) // 1 undefined
foo({x:1, y:2}) // 1 2
为进一步加深理解,我们来看这样一个例子:
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
写法二与一不同的地方在于:写法一中虽然也使用了解构赋值,但是在解构之前形参x
,y
都已经有默认值为0
了,即使在调用函数时只传了一个值,那么另一个值也不会为undefined
而是为0
;而写法二中形参x
,y
采用了惰性求值,也就是说其并没有真正的默认值,如果调用m2时,没有传入参数,那么启动解构赋值,形参x
,y
为0
,但是如果只传了1个参数,那么就不会启动形参内的解构赋值,这样的话,另一个参数就为undefined
。
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
1.4 带有默认值的参数位置
通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
1.5 函数的length属性
函数的length
返回该函数的形参数量,但不包括带有默认值的参数,总的来说就是调用该函数时必须传入多少个参数。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
1.5 默认参数的应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
function missparams () {
throw new Error('Missing a paramater')
}
function foo (x = missparams(), y = 1) {
console.log(x, y)
}
foo() // Error: Missing a paramater
2. rest参数
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
这其实很好理解,在其他语言中,无论是C
还是Python
中我们会在定义形参时为了提升函数的稳定性,会在形参后定义*args
,如果有多的参数就会被放在数组中传入函数,而不会引发错误。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
值得注意的是,...rest
只能是作为形参的最后一个参数,正如*args
一样,必须放在最后,否则会报错。
3. name属性
这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
4. 箭头函数
我们先看个例子:
// 不使用箭头函数
function greet () {
console.log(`hello ${this.name}`)
}
var name = 'yan' //即 window.name = 'yan'
person = {
'name': 'bing',
'greet': greet
}
greet() // hello yan
person.greet() // hello bing
//使用箭头函数
var greet = () => console.log(`hello ${this.name}`)
person = {
'name': 'bing',
'greet': greet
}
greet() // hello yan
person.greet() // hello yan
greet.call(person) // hello yan
通过上面的例子我们可以得知箭头函数的this
的指向是不会改变的,它在哪里定义,哪里就是它的执行上下文,由此我们可以得到使用箭头函数的注意点:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
出现上述情况的根本原因是箭头函数没有自己的this
,它的this
指向外层的this
。
不适用的场合:
- 定义对象的方法:
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
因为对象不构成单独的作用域,导致jumps
箭头函数定义时的作用域就是全局作用域。
- 需要动态this的时候:
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
因为button
的监听函数是一个箭头函数,导致里面的this
就是全局对象。如果改成普通函数,this
就会动态指向被点击的按钮对象。
5. Function.prototype.toString()
toString()方法返回函数代码本身,以前会省略注释和空格。
function /* foo comment */ foo () {}
foo.toString()
// "function /* foo comment */ foo () {}"
参考链接
作者:阮一峰
链接:http://es6.ruanyifeng.com/#docs/destructuring