类库
- lodash:是一个通过 Lodash 限制操作频率的函数。
- axios: Ajax 库。
代码片段
查看对象类型
使用 Object.prototype.toString
方法,通过第二个参数返回的构造函数判断对象类型
1
2
3
4
5
6
7
8
| Object.prototype.toString.call(2); // "[object Number]"
Object.prototype.toString.call(""); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"
Object.prototype.toString.call(undefined); // "[object Undefined]"
Object.prototype.toString.call(null); // "[object Null]"
Object.prototype.toString.call(Math); // "[object Math]"
Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
|
JavaScript 对象的元属性
1
2
3
4
5
6
7
8
| {
value: 123,
writable: false,
enumerable: true,
configurable: false,
get: undefined,
set: undefined
}
|
将类似于数组的对象转换为数组
slice()
方法的一个重要应用,是将类似数组的对象转为真正的数组。
1
2
3
4
5
| Array.prototype.slice.call({ 0: "a", 1: "b", length: 2 });
// ['a', 'b']
Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);
|
构造函数
为了保证构造函数必须与 new
命令一起使用,一个解决办法是,构造函数内部使用严格模式,即第一行加上 use strict
。这样的话,一旦忘了使用 new
命令,直接调用构造函数就会报错。
1
2
3
4
5
6
7
8
| function Fubar(foo, bar) {
"use strict";
this._foo = foo;
this._bar = bar;
}
Fubar();
// TypeError: Cannot set property '_foo' of undefined
|
另一个解决办法,构造函数内部判断是否使用 new 命令,如果发现没有使用,则直接返回一个实例对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
| function Fubar(foo, bar) {
if (!(this instanceof Fubar)) {
return new Fubar(foo, bar);
}
this._foo = foo;
this._bar = bar;
}
Fubar(1, 2)._foo(
// 1
new Fubar(1, 2)
)._foo; // 1
|
如果构造函数内部有 return 语句,而且 return 后面跟着一个对象,new 命令会返回 return 语句指定的对象;否则,就会不管 return 语句,返回 this 对象。
函数内部可以使用 new.target
属性。如果当前函数是 new
命令调用,new.target
指向当前函数,否则为 undefined
。
防止脚本篡改
为了防止攻击者篡改外部脚本,script
标签允许设置一个 integrity
属性,写入该外部脚本的 Hash
签名,用来验证脚本的一致性。
1
2
3
4
| <script
src="/assets/application.js"
integrity="sha256-TvVUHzSfftWg1rcfL6TIJ0XKEGrgLyEq6lEpcmrG9qs="
></script>
|
使用正则表达式提取解构值
用正则表达式的 exec()
方法匹配字符串会返回一个数组,该数组第一个值是完全匹配正则表达式的字符串,然后的值是匹配正则表达式括号内内容部分。解构赋值允许你轻易地提取出需要的部分,忽略完全匹配的字符串——如果不需要的话。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| function parseProtocol(url) {
var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
if (!parsedURL) {
return false;
}
console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"]
var [, protocol, fullhost, fullpath] = parsedURL;
return protocol;
}
console.log(
parseProtocol("https://developer.mozilla.org/en-US/Web/JavaScript")
); // "https"
|
匹配 Unicode 属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // 匹配所有数字
const regex = /^\p{Number}+$/u;
regex.test('²³¹¼½¾') // true
regex.test('㉛㉜㉝') // true
regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
// 匹配所有空格
\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
//解构赋值
let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
one // foo
two // bar
|
尾递归优化技巧,蹦床函数(trampoline)
1
2
3
4
5
6
| function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}
|
上面就是蹦床函数的一个实现,它接受一个函数f作为参数。只要 f
执行后返回一个函数,就继续执行。注意,这里是返回一个函数,然后执行该函数,而不是函数里面调用函数,这样就避免了递归执行,从而就消除了调用栈过大的问题。
然后,要做的就是将原来的递归函数,改写为每一步返回另一个函数。
1
2
3
4
5
6
7
| function sum(x, y) {
if (y > 0) {
return sum.bind(null, x + 1, y - 1);
} else {
return x;
}
}
|
上面代码中,sum 函数的每次执行,都会返回自身的另一个版本。现在,使用蹦床函数执行 sum,就不会发生调用栈溢出。
警告
本文最后更新于 September 18, 2020,文中内容可能已过时,请谨慎使用。