您可以通过左右滑屏查看更多笔记内容。。
用于从 Unicode 码点返回对应字符,可以识别码点大于0xFFFF
的字符;
console.log(String.fromCodePoint(0x20BB7)); // "𠮷"
ES6 还为原生的 String 对象;往往用于模板字符串的处理方法。
String.raw`Hi\n${2+3}!`;
// 返回 "Hi\\n5!"
String.raw`Hi\u000A!`;
// 返回 "Hi\\u000A!"
String.raw()`方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。
String.raw({ raw: 'test' }, 0, 1, 2); // 't0e1s2t'
// 等同于
String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);
作用:正确处理 4 个字节储存的字符,返回一个字符的码点。
对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()
方法相同。
var s = "𠮷";
s.length // 2
s.charAt(0) // ''
s.charAt(1) // ''
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
-------------------------------
let s = '𠮷a';
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
codePointAt()
方法返回的是码点的十进制值,如果想要十六进制的值,可以使用toString()
方法转换一下。
let s = '𠮷a';
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61"
字符a
在字符串s
的正确位置序号应该是 1,但是必须向codePointAt()
方法传入 2。解决这个问题的一个办法是使用for...of
循环,因为它会正确识别 32 位的 UTF-16 字符;
let s = '𠮷a';
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61
使用扩展运算符(...
)进行展开运算。
let arr = [...'𠮷a']; // arr.length === 2
arr.forEach(
ch => console.log(ch.codePointAt(0).toString(16))
);
// 20bb7
// 61
测试一个字符是由两个字节还是由四个字节组成的?
function is32Bit(c) {
return c.codePointAt(0) > 0xFFFF;
}
is32Bit("𠮷") // true
is32Bit("a") // false
作用:将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。normalize
方法目前不能识别三个或三个以上字符的合成
案例:``O(\u004F)和
ˇ(\u030C)合成
Ǒ`(\u004F\u030C)
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true
normalize
的方式:NFC
参数返回字符的合成形式,NFD
参数返回字符的分解形式。
NFC
,默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。NFD
,表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。NFKC
,表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”。(这只是用来举例,normalize
方法不能识别中文。)NFKD
,表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。indexOf
方法确定一个字符串是否包含在另一个字符串中方法?
参数:支持两个,第一个参数:要查找的字符串;第二个参数:开始搜索的位置。
let s = 'Hello world!';
console.log(s.startsWith('world', 6)) // true
console.log(s.endsWith('Hello', 5)) // true
console.log(s.includes('Hello', 0)) // true
endsWith的行为与其他两个方法有所不同。它针对前n个字符,而其他两个方法针对从第n个位置直到字符串结束
repeat
方法返回一个新字符串,表示将原字符串重复n
次。参数是负数或者
Infinity,会报错;如果参数是 0 到-1 之间的小数,则等同于 0.
参数NaN
等同于 0;
如果repeat
的参数是字符串,则会先转换成数字。
'hello'.repeat(2) // "hellohello"
'na'.repeat(Infinity) // RangeError
'na'.repeat(-1) // RangeError
'na'.repeat(-0.9) // ""
'na'.repeat('3') // "nanana"
作用:实现字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
参数:第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
如果省略第二个参数,默认使用空格补全长度。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(5, 'ab') // 'xabab'
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
'abc'.padStart(10, '0123456789') // '0123456abc'
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
用途:
为数值补全指定位数。下面代码生成 10 位的数值字符串。
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"
提示字符串格式。
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
作用:trimStart()
消除字符串头部的空格,trimEnd()
消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。
const s = ' abc ';
s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
这两个方法对字符串头部(或尾部)的 tab 键、换行符等不可见的空白符号也有效。
ES5 中的构造函数:
第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)。
var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;
第二种情况是,参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。
var regex = new RegExp(/xyz/i);
// 等价于
var regex = /xyz/i;
ES6中不允许使用第二个参数添加修饰符,直接报错;
如果RegExp
构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
new RegExp(/abc/ig, 'i').fla
gs // "i"原有正则对象的修饰符是ig,它会被第二个参数i覆盖。
match()
、replace()
、search()
和split()
String.prototype.match
调用 RegExp.prototype[Symbol.match]
String.prototype.replace
调用 RegExp.prototype[Symbol.replace]
String.prototype.search
调用 RegExp.prototype[Symbol.search]
String.prototype.split
调用 RegExp.prototype[Symbol.split]
作用:含义为“Unicode 模式”,用来正确处理大于\uFFFF
的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。
点字符(.
)在正则表达式中,含义是除了换行符以外的任意单个字符。
对于码点大于0xFFFF
的 Unicode 字符,点字符不能识别,必须加上u
修饰符。
var s = '𠮷';
/^.$/.test(s) // false
/^.$/u.test(s) // true
/^.$/u.test("1") // true
Unicode 字符表示法:用大括号表示 Unicode 字符
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true
量词:使用u
修饰符后,所有量词都会正确识别码点大于0xFFFF
的 Unicode 字符。
/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true
预定义模式:u
修饰符也影响到预定义模式,能否正确识别码点大于0xFFFF
的 Unicode 字符。
/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷') // true
\S
是预定义模式,匹配所有非空白字符。只有加了u
修饰符,它才能正确匹配码点大于0xFFFF
的 Unicode 字符。
案例:返回字符串的长度的函数:
var s1 = '𠮷';
console.log(/^.$/.test(s1) ); // false
console.log(/^.$/u.test("1")); // true
function codePointLength(text) {
var result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
let s = '𠮷𠮷';
console.log(s.length); //4
console.log(codePointLength(s)); //2
i修饰符 \u004B与
\u212A都是大写的
K
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
u
修饰符。const r1 = /hello/;
const r2 = /hello/u;
r1.unicode // false
r2.unicode // true
全局匹配,后一次匹配从上一次匹配成功的下一个位置开始,确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
exec():用于检索字符串中的正则表达式的匹配。
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
console.log(r1.exec(s)) // ["aaa"]
console.log(r2.exec(s)) // ["aaa"]
console.log(r1.exec(s)) // ["aa"]
console.log(r1.exec(s)) // ["a"]
console.log(r1.exec(s)) // ["null"]
console.log(r2.exec(s)) // null
表示是否设置了y
修饰符
var r = /hello\d/y;
r.sticky // true
返回正则表达式的修饰符;
// ES5 的 source 属性
// 返回正则表达式的正文
/abc/ig.source
// "abc"
// ES6 的 flags 属性
// 返回正则表达式的修饰符
/abc/ig.flags
// 'gi'
行终止符,就是该字符表示一行的终结,以下四个字符属于“行终止符”。
\n
)\r
)dotAll
模式,即点(dot)代表一切字符。所以,正则表达式还引入了一个dotAll
属性,返回一个布尔值,表示该正则表达式是否处在dotAll
模式。
const re = /foo.bar/s;
// 另一种写法
// const re = new RegExp('foo.bar', 's');
re.test('foo\nbar') // true
re.dotAll // true
re.flags // 's'
/s
修饰符和多行修饰符/m
不冲突,两者一起使用的情况下,.
匹配所有字符,而^
和$
匹配每一行的行首和行尾。
“后行断言”正好与“先行断言”相反,x
只有在y
后面才匹配,必须写成/(?<=y)x/
。比如,只匹配美元符号之后的数字,要写成/(?<=\$)\d+/
。“后行否定断言”则与“先行否定断言”相反,x
只有不在y
后面才匹配,必须写成/(?<!y)x/
。比如,只匹配不在美元符号后面的数字,要写成/(?<!\$)\d+/
。
/\d+(?=%)/.exec('100% of US presidents have been male') // ["100"]先行断言
/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill') // ["100"]
/(?<!\$)\d+/.exec('it’s is worth about €90') // ["90"]
\p{...}
和\P{...}
,允许正则表达式匹配符合 Unicode 某种属性的所有字符。const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π') // true \p{Script=Greek}指定匹配一个希腊文字母,所以匹配π成功。
Unicode 属性类要指定属性名和属性值。
\P{…}
是\p{…}
的反向匹配,即匹配不满足条件的字符。
1.匹配所有十进制字符
const regex = /^\p{Decimal_Number}+$/u;
regex.test('𝟏𝟐𝟑𝟜𝟝𝟞𝟩𝟪𝟫𝟬𝟭𝟮𝟯𝟺𝟻𝟼') // true
2.匹配罗马数字。
// 匹配所有数字
const regex = /^\p{Number}+$/u;
regex.test('²³¹¼½¾') // true
regex.test('㉛㉜㉝') // true
regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
3.// 匹配所有空格
\p{White_Space}
// 匹配各种文字的所有字母,等同于 Unicode 版的 \w
[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配 Emoji
/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
// 匹配所有的箭头字符
const regexArrows = /^\p{Block=Arrows}+$/u;
regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true
允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31
如果具名组没有匹配,那么对应的groups
对象属性会是undefined
let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
one // foo
two // bar
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false
二进制和八进制数值的新的写法,分别用前缀0b
(或0B
)和0o
(或0O
)表示。
0b111110111 === 503 // true
0o767 === 503 // true
将0b
和0o
前缀的字符串数值转为十进制,要使用Number
方法。
Number('0b111') // 7
Number('0o10') // 8
Number.isFinite()
用来检查一个数值是否为有限的(finite),即不是Infinity
(无限)。
参数类型不是数值,Number.isFinite
一律返回false
。
Number.isNaN()
用来检查一个值是否为NaN
。
参数类型不是NaN
,Number.isNaN
一律返回false
。
isFinite(25) // true 传统方法先调用Number()将非数值的值转为数值,再进行判断,
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false
isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
作用:用来判断一个数值是否为整数。
JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。
JavaScript 中数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位);
如果数值的精度超过这个限度,第54位及后面的位就会被丢弃,这种情况下,Number.isInteger
可能会误判。
Number.isInteger(3.0000000000000002) // true
如果一个数值的绝对值小于Number.MIN_VALUE
(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0。这时,Number.isInteger
也会误判.
Number.isInteger(5E-324) // false
Number.isInteger(5E-325) // true
Math.pow(x,y) //x 的 y 次幂。
一个极小的常量Number.EPSILON
:它表示 1 与大于 1 的最小浮点数之间的差。
Number.EPSILON
实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。
Number.EPSILON
的实质是一个可以接受的最小误差范围。
例如:误差范围设为 2 的-50 次方(即Number.EPSILON * Math.pow(2, 2)
),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。
//误差检查函数。
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
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
JavaScript 能够准确表示的整数范围在-2^53
到2^53
之间(不含两个端点),超过这个范围,无法精确表示这个值。
Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
这两个常量,用来表示这个范围的上下限。
Number.isSafeInteger()
则是用来判断一个整数是否落在这个范围之内。
需要注意。验证运算结果是否落在安全整数的范围内,不要只验证运算结果,而要同时验证参与运算的每个值。
function trusty (left, right, result) {
if (
Number.isSafeInteger(left) &&
Number.isSafeInteger(right) &&
Number.isSafeInteger(result)
) {
return result;
}
throw new RangeError('Operation cannot be trusted!');
}
trusty(9007199254740993, 990, 9007199254740993 - 990)
// RangeError: Operation cannot be trusted!
trusty(1, 2, 3)
// 3
Math.trunc():用于去除一个数的小数部分,返回整数部分;
Math.sign
:用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
返回五种值。
+1
;-1
;0
;-0
;NaN
。Math.cbrt
:用于计算一个数的立方根。
Math.clz32()
:将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。
Math.imul
:返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
Math.fround
:将64位双精度浮点数转为32位单精度浮点数。如果小数的精度超过24个二进制位,返回值就会不同于原值,否则返回值不变(即与64位双精度值一致)。
Math.hypot
方法返回所有参数的平方和的平方根。如果参数不是数值,Math.hypot
方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。
对数方法:
Math.expm1(x)
返回 e^x - 1,即Math.exp(x) - 1
。Math.log1p(x)
方法返回1 + x
的自然对数,即Math.log(1 + x)
。如果x
小于-1,返回NaN
。Math.log10(x)
返回以 10 为底的x
的对数。如果x
小于 0,则返回 NaN。Math.log2(x)
返回以 2 为底的x
的对数。如果x
小于 0,则返回 NaN。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)指数运算符(**
)
多个指数运算符连用时,是从最右边开始计算的。
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
指数运算符可以与等号结合,形成一个新的赋值运算符(**=
)。
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
let b = 4;
b **= 3;
// 等同于 b = b * b * b;
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
let
或const
再次声明。与解构赋值默认值结合使用
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com') //省略了第二个参数;直接得到默认值;
// "GET"
// 写法一:写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二:函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
------------------------------------------------
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
参数默认值的位置:通常情况下,定义了默认值的参数,应该是函数的尾参数。
如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
// 例二
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]
函数的length
属性,将返回没有指定默认值的参数个数。
也就是说,指定了默认值后,length
属性将失真。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
如果设置了默认值的参数不是尾参数,那么length
属性也不再计入后面的参数了。
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
作用域:一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。
var x = 1;
function foo(x){
console.log("1x="+x);
var x = 3;
console.log("2x="+x);
}
foo();
//调用函数f00时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是1x输出undefined;
// 1x=undefined
// 2x=3
var x = 1;
function foo(x, y = function() { x = 2; console.log("1x="+x)}) {
var x = 3;
y();
console.log("2x="+x);
}
foo();
console.log("3x="+x) ;
// 1x=2
// 2x=3
// 3x=1
应用:利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
如果调用的时候没有参数,就会调用默认值throwIfMissing
函数,从而抛出一个错误。
rest 参数(形式为...变量名
),用于获取函数的多余参数,这样就不需要使用arguments
对象了。
// arguments变量的写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
//arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。
// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
函数的length
属性,不包括 rest 参数。
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
严格模式:规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
函数的name
属性,返回该函数的函数名。
将一个匿名函数赋值给一个变量,ES5 的name
属性,会返回空字符串,而 ES6 的name
属性会返回实际的函数名。
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
将一个具名函数赋值给一个变量,则 ES5 和 ES6 的name
属性都返回这个具名函数原本的名字。
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
(new Function).name // "anonymous" Function构造函数
bind返回的函数,
name属性值会加上
bound`前缀。
foo.bind({}).name // "bound foo"
(function(){}).bind({}).name // "bound "
“箭头”(=>
)函数:
// rest 参数与箭头函数结合的例子。
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]
const headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5)
// [1,[2,3,4,5]]
使用注意:
(1)函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new
命令,否则会抛出一个错误。
(3)不可以使用arguments
对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield
命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this
对象的指向是可变的,但是在箭头函数中,它是固定的。
var id = 21;
function foo() {
var id = 21;
setTimeout(() => {
var id = 21;
console.log('id:', this.id);
}, 100);
}
foo.call({ id: 42 });
//id: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
前者的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);
}
};
这个箭头函数里面的this,总是指向handler对象。否则,回调函数运行时,this.doSomething这一行会报错,因为此时this指向document对象。
由于箭头函数使得this
从“动态”变成“静态”,下面两个场合不应该使用箭头函数:
this
;this
的时候,也不应使用箭头函数。function insert(value) {
return {into: function (array) {
return {after: function (afterValue) {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}};
}};
}
insert(2).into([1, 3]).after(1); //[1, 2, 3]
==>
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
尾调用优化
尾调用(Tail Call):是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。
尾递归:函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
function Fibonacci (n) {
if ( n <= 1 ) {return 1};
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
Fibonacci(10) // 89
Fibonacci(100) // 超时
Fibonacci(500) // 超时
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
if( n <= 1 ) {return ac2};
return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}
Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity
toString()
方法返回函数代码本身,不会省略注释和空格。
function /* foo comment */ foo () {}
foo.toString()
// "function /* foo comment */ foo () {}"
//以前明确要求catch命令后面必须跟参数,接受try代码块抛出的错误对象。
try {
// ...
} catch (err) {
// 处理错误
}
==>
//允许catch语句省略参数。
try {
// ...
} catch {
// ...
}