Function
# 1. Function(args..., functionBody)
- 构造函数创建匿名函数。前面参数会成为匿名函数的形参,最后一个参数会成为匿名函数的可执行体(Vue 中的 render 函数的形成方式)。
Function("console.log('执行代码')")() // => '执行代码'
Function('name', 'console.log(name)')('徐扶墙') // => '徐扶墙'
2
3
- 模板处理函数。
function foo(tpl, ...values) {
console.log(tpl) // => ['try call ', ' ', '', raw: Array(3)]
console.log(values) // => ['foo', '徐扶墙']
}
foo`try call ${foo.name} ${'徐扶墙'}`
2
3
4
5
6
- 自执行匿名函数。
// prettier-ignore
(function () {
return '徐扶墙'
}())
(function () {
return '徐扶墙'
})()
void (function () {
console.log('徐扶墙')
})()
2
3
4
5
6
7
8
9
10
11
12
# 2. getter & setter
获取/设置对象属性值时触发的函数。
可以结合使用 getter 和 setter 来创建一个伪属性。不可能在具有真实值的属性上同时拥有一个 getter 或 setter 器(MDN)。
const foo = {
name: '徐扶墙',
age: 17,
harem: [],
get firstWife() {
return this.harem[0] || '裆下有些忧郁呀!'
},
set marryWife(wife) {
this.harem.push(wife)
},
}
foo.marryWife = '姜泥'
foo.marryWife = '裴南苇'
foo.firstWife // => '姜泥'
foo // => {name: '徐扶墙', age: 17, harem: ['姜泥', '裴南苇']}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3. new.target
- new.target 属性允许你检测函数或构造方法是否是通过 new 运算符被调用的。
function Foo(name) {
if (!new.target) throw new Error('must be called with new')
this.name = name
}
Foo() // => Uncaught Error: must be called with new
const bar = new Foo('徐扶墙') // => {name: '徐扶墙'}
2
3
4
5
6
7
8
# 4. arguments
- 保存所有传递的参数(是一个伪数组)。
function foo() {
console.log(arguments) // => Arguments(2) ['徐扶墙', '裴南苇', length: 2]
}
foo('徐扶墙', '裴南苇')
2
3
4
5
# 5. call(this, arg...) & apply(this, [arg...]) & bind(this, arg...)
第一个参数均为 this 的指向。call、bind 的参数传递通过逗号分割,apply 的参数通过数组传递。
bind 不会直接执行,会返回一个函数,参数可分别传递(bind 是柯里化函数)。
若 this 为 undefined 或 null,则指向全局。若为 [number | string | boolean],则会隐式使用构造函数包裹后返回,俗称装箱。
const foo = {
name: '徐扶墙',
say(arg1, arg2) {
console.log(`${this.name}:${arg1}${arg2}${foo.name}!`)
},
}
const bar = {
name: '裴南苇',
}
foo.say.call(bar, '天下', '第二')
foo.say.apply(bar, ['天下', '第二'])
foo.say.bind(bar, '天下')('第二')
2
3
4
5
6
7
8
9
10
11
12
13
反柯里化函数。
Function.prototype.call.apply(self, [obj, ...args]) 相当于 self.call(obj, ...args) 相当于 obj.self(...args)。转换到下方代码示意为 foo.push(6)。
Function.prototype.uncurrying = function () { const self = this return function () { return Function.prototype.call.apply(self, arguments) } } Array.push = Array.prototype.push.uncurrying() const foo = { 0: 4, 1: 3, 2: 9, length: 3, } Array.push(foo, 6) // foo: {0: 4, 1: 3, 2: 9, 3: 6, length: 4}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 6. Generator
function* 这种声明方式(function 关键字后跟一个星号)会定义一个生成器函数 (generator function),它返回一个 Generator 对象。生成器函数在执行时能暂停,后面又能从暂停处继续执行。
function* foo() { yield 'jack' yield 'pony' return 'coderljw' } for (const res of foo()) console.log(res) // 依次打印:'jack' -> 'pony' function* bar() { yield* foo() yield 'Evan You' yield 'Dan' return 'coderljw' } for (const res of bar()) console.log(res) // 依次打印:'jack' -> 'pony' -> 'Evan You' -> 'Dan'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16next。
function* foo(arg) { console.log(arg) // => 'nothing' const Alibaba = yield 'jack' console.log(Alibaba) // => 'Alibaba' const Tencent = yield 'pony' console.log(Tencent) // => 'Tencent' return 'coderljw' } const gen = foo('nothing') gen.next() // => {value: 'jack', done: false} gen.next('Alibaba') // => {value: 'pony', done: false} gen.next('Tencent') // => {value: 'coderljw', done: true} gen.next() // => {value: undefined, done: true}
1
2
3
4
5
6
7
8
9
10
11
12
13
14return 会结束生成器函数。
function* foo() { const Alibaba = yield 'jack' const Tencent = yield 'pony' return 'coderljw' } const gen = foo() gen.next() // => {value: 'jack', done: false} gen.return('Evan You') // => {value: 'Evan You', done: true} gen.next() // => {value: undefined, done: true}
1
2
3
4
5
6
7
8
9
10throw 会结束生成器函数并抛出错误,可用 try...catch 捕获错误。
- 内部捕获。
function* foo() { try { const Alibaba = yield 'jack' } catch (err) { console.log(err) // => 4396 } const Tencent = yield 'pony' return 'coderljw' } const gen = foo() gen.next() // => {value: 'jack', done: false} const res = gen.throw('4396') console.log(res) // => {value: 'pony', done: false} gen.next() // => {value: 'coderljw', done: true} gen.next() // => {value: undefined, done: true}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16- 外部捕获。
function* bar() { const Alibaba = yield 'jack' const Tencent = yield 'pony' return 'coderljw' } const gen = bar() gen.next() // => {value: 'jack', done: false} try { gen.throw(new Error('2200')) } catch (err) { console.log(err) // => Error: 2200 } gen.next() // => {value: undefined, done: true}
1
2
3
4
5
6
7
8
9
10
11
12
13
14实现状态机。
function* foo() { while (true) { yield 'on' yield 'off' } } const gen = foo() gen.next() // => {value: 'on', done: false} gen.next() // => {value: 'off', done: false} gen.next() // => {value: 'on', done: false}
1
2
3
4
5
6
7
8
9
10
11搭配 Promise 函数(嘿嘿!另一种形式的组合函数)。
function foo(ms) { return new Promise((resolve, reject) => { setTimeout(_ => resolve(4396), ms) }) } function bar(ms) { return new Promise((resolve, reject) => { setTimeout(_ => resolve(1557), ms) }) } function* hoge(promises, res) { let n = 0 while (n < promises.length && n < 4) { res = yield promises[n](res) n++ } return res } function co(gen) { return new Promise((resolve, reject) => { function next(data) { const result = gen.next(data) if (result.done) return resolve(result.value) result.value.then( data => next(data), err => reject(err), ) } next() }) } co(hoge([foo, bar], 2200)).then(console.log) // => 1557
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
27
28
29
30
31
32
33
34
35
36
37
# 7. 继承
原型链继承。
存在引用值共享问题。
function Super() { this.harm = [4, 3, 9] } Super.prototype.getHarm = function () { return this.harm } function Sub() {} // 继承 Super Sub.prototype = new Super() const clearlove7 = new Sub() const xiaohu = new Sub() clearlove7.harm.push(6) clearlove7.getHarm() // => [4, 3, 9, 6] xiaohu.getHarm() // => [4, 3, 9, 6]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18盗用构造函数继承(对象伪装/经典继承)。
解决了引用值共享问题,但不能访问父类原型上的方法。
function Super() { this.harm = [4, 3, 9] } Super.prototype.getHarm = function () { return this.harm } function Sub() { Super.call(this) } const clearlove7 = new Sub() const xiaohu = new Sub() clearlove7.harm.push(6) clearlove7.getHarm() // => Uncaught TypeError xiaohu.getHarm() // => Uncaught TypeError clearlove7.harm // => [4, 3, 9, 6] xiaohu.harm // => [4, 3, 9]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20组合继承(伪经典继承)。
弥补了原型链继承和盗用构造函数继承的不足,但存在效率问题,最主要的效率问题就是父类构造函数始终会被调用两次。
function Super() { this.harm = [4, 3, 9] } Super.prototype.getHarm = function () { return this.harm } function Sub() { // 初始化(ES6 super) Super.call(this) // 第二次调用 Super() } // 继承原型 Sub.prototype = new Super() // 第一次调用 Super() const clearlove7 = new Sub() const xiaohu = new Sub() clearlove7.harm.push(6) clearlove7.getHarm() // => [4, 3, 9, 6] xiaohu.getHarm() // => [4, 3, 9]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21原型式继承。
- 2006 年,Douglas Crockford 写了一篇文章:《JavaScript 中的原型式继承》 (opens new window)。本质上,object() 是对传入的对象执行了一次浅复制。
- ECMAScript 5 通过增加 Object.create() 方法将原型式继承的概念规范化了。
function object(o) { function F() {} F.prototype = o return new F() } const Super = { harm: [4, 3, 9], } const clearlove7 = object(Super) const xiaohu = object(Super) clearlove7.harm.push(6) clearlove7.harm // => [4, 3, 9, 6] xiaohu.harm // => [4, 3, 9, 6]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17寄生式继承。
- 寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。
- 寄生式继承同样适合主要关注对象,而不在乎类型和构造函数的场景。 object() 函数不是寄生式继承所必需的,任何返回新对象的函数都可以在这里使用。
function object(o) { function F() {} F.prototype = o return new F() } function createAnother(original) { // 通过调用函数创建一个新对象 const clone = object(original) // 以某种方式增强这个对象 clone.getHarm = function () { console.log(this.harm) } return clone } const Super = { harm: [4, 3, 9], } const clearlove7 = createAnother(Super) const xiaohu = createAnother(Super) clearlove7.harm.push(6) clearlove7.getHarm() // => [4, 3, 9, 6] xiaohu.getHarm() // => [4, 3, 9, 6]
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
27寄生式组合继承。
- 寄生式组合继承通过盗用构造函数继承属性,但使用混合式原型链继承方法。
- 寄生式组合继承可以算是引用类型继承的最佳模式。
/* Sub.prototype = Object.create(Super.prototype) Sub.prototype.constructor = Sub 《JavaScript 高级程序设计(第四版)》写法,等同于上面两行代码。 function object(o) { function F() {} F.prototype = o return new F() } function inheritPrototype(subType, superType) { const prototype = object(superType.prototype) // 创建对象 prototype.constructor = subType // 增强对象 subType.prototype = prototype // 赋值对象 } inheritPrototype(Sub, Super) */ function Super() { this.harm = [4, 3, 9] } Super.prototype.getHarm = function () { return this.harm } function Sub() { // 初始化(ES6 super) Super.call(this) } // 继承原型 Sub.prototype = Object.create(Super.prototype) // 纠正 constructor 指向 Sub.prototype.constructor = Sub const clearlove7 = new Sub() const xiaohu = new Sub() clearlove7.harm.push(6) clearlove7.getHarm() // => [4, 3, 9, 6] xiaohu.getHarm() // => [4, 3, 9]
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44