手写call-apply-bind

1. call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* rewrite call
* https://tc39.es/ecma262/#sec-function.prototype.call
*/

Function.prototype._call = function (ctx) {
ctx = Object(ctx) || window
ctx.fn = this

var args = []
for (var i = 1; i < arguments.length; i++)
args.push('arguments[' + i + ']')
var result = eval('ctx.fn(' + args + ')')

delete ctx.fn
return result
}

2. apply

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
/**
* rewrite apply
* https://tc39.es/ecma262/#sec-function.prototype.apply
*/

Function.prototype._apply = function (ctx, args) {
ctx = Object(ctx) || window
ctx.fn = this
var _toString = Object.prototype.toString
var result

// args is primitive value
if (args && typeof args !== 'object') {
throw new TypeError("CreateListFromArrayLike called on non-object")
}

if (!args || _toString.call(args).slice(8, -1) !== 'Array') {
result = ctx.fn()
} else {
result = eval('ctx.fn(' + args + ')')
}

delete ctx.fn
return result
}

3. bind

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
/**
* rewrite bind
* https://tc39.es/ecma262/#sec-function.prototype.bind
*/

Function.prototype._bind = function (ctx) {
var originFn = this,
args = [].slice.call(arguments, 1)

// 原型传递中介函数
// var _tempFn = function () { }

var newFn = function () {
var newArgs = [].slice.call(arguments)
// new newFn(), this -> 实例对象,
// newFn(), this -> ctx
return originFn.apply(this instanceof newFn ? this : ctx, args.concat(newArgs))
}

// 第一种方式
// 与原生bind行为保持一致, newFn原型指向originFn原型
newFn.prototype = originFn.prototype

// 第二种方式
// 与原生bind行为不一致, 利用中介函数_tempFn充当originFn原型
// _tempFn.prototype = originFn.prototype
// newFn.prototype = new _tempFn()

return newFn
}