此文包含
数值的扩展
、函数的扩展
,总结了一些我认为有可能会用到的,而去除了一些稍稍复杂一时难以去学习理解且目前不想去研究去总结的知识点。
一、数值的扩展
0.二进制和八进制表示法
ES6 提供了二进制和八进制数值的新的写法,分别用前缀
0b
(或0B)和0o
(或0O)表示。
1.Number.isFinite(), Number.isNaN()
S6 在Number对象上,新提供了
Number.isFinite()
和Number.isNaN()
两个方法。
Number.isFinite()
用来检查一个数值是否为有限的(finite),即不是Infinity
。Number.isNaN()
用来检查一个值是否为NaN
。
2.Number.parseInt(), Number.parseFloat()
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
3.Number.isInteger()
Number.isInteger()用来判断一个数值是否为整数。
4.Math 对象的扩展
0️⃣、Math.trunc()
Math.trunc
方法用于去除一个数的小数部分,返回整数部分。也就是说正数的话相当于Math.floor
方法,负数的话相当于Math.ceil
方法。
1️⃣、Math.sign()
Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
2️⃣、双曲函数方法
ES6 新增了 6 个双曲函数方法。
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)
5.指数运算符
ES2016 新增了一个指数运算符(),需要注意的是:这个运算符的一个特点是右结合,而不是常见的左结合。多个指数运算符连用时,是从最右边开始计算的。
例如:2 ** 3 ** 2 相当于2(3**2)=2的9次方 = 512
二、函数的扩展
0.函数参数的默认值
0️⃣、基本用法
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。且注意:参数变量是默认声明的,所以不能用
let
或const
再次声明。
1️⃣、与解构赋值默认值结合使用 §
2️⃣、参数默认值的位置
通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。
3️⃣、函数的 length 属性
指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。即:函数length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
特别注意:如果设置了默认值的参数不是尾参数,那么length属性也不再计入后面的参数了。
4️⃣、作用域
ar x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2。
5️⃣、应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
1.rest 参数
ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
下面是一个 rest 参数代替arguments变量的例子。
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
// 报错
function f(a, ...b, c) {
// ...
}
函数的length属性,不包括 rest 参数。
2.严格模式
从 ES5 开始,函数内部可以设定为严格模式。ES2016 做了一点修改,规定只要
函数参数
使用了默认值
、解构赋值
、或者扩展运算符
,那么函数内部就不能显式设定为严格模式
,否则会报错
。
原因是:函数内部严格模式,同时适用函数体和函数参数,而在执行的时候先执行的是参数,而在函数体内才知道是不是严格模式,这样是不合理的。
两种方法可以规避这种限制。第一种是设定全局性的严格模式,这是合法的。
'use strict';
function doSomething(a, b = a) {
// code
}
第二种是把函数包在一个无参数的立即执行函数里面。
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
3.箭头函数
- 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
- 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
- 箭头函数的一个用处是简化回调函数。
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
- 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
- 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
关于this作用域的问题,下面给出一个例子:
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
解析:上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。
再来一个例子:
var handler = {
id: '123456',
init: function() {
document.addEventListener('click',
event => this.doSomething(event.type), false);
},
doSomething: function(type) {
console.log('Handling ' + type + ' for ' + this.id);
}
};
上面代码的init方法中,使用了箭头函数,这导致这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。
接着来一个例子,请问下面的代码之中有几个this?
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
上面代码之中,只有一个this,就是函数foo的this,所以t1、t2、t3都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this.
长期以来,JavaScript 语言的this对象一直是一个令人头痛的问题,在对象方法中使用this,必须非常小心。箭头函数”绑定”this,很大程度上解决了这个困扰。
不适用场合
由于箭头函数使得this从“动态”变成“静态”,下面两个场合不应该使用箭头函数。
第一个场合是定义函数的方法,且该方法内部包括this。
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。
第二个场合是需要动态this的时候,也不应使用箭头函数。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
上面代码运行时,点击按钮会报错,因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。
另外,如果函数体很复杂,有许多行,或者函数内部有大量的读写操作,不单纯是为了计算值,这时也不应该使用箭头函数,而是要使用普通函数,这样可以提高代码可读性。