call 原生call
1 2 3 4 5 6 7 8 9 function test (arg1, arg2 ) { console .log(arg1, arg2) console .log(this .a, this .b) } run.call({ a: 'a' , b: 'b' }, 1 , 2 )
在实现的过程有个关键:
如果一个函数作为一个对象的属性,那么通过对象的.运算符调用此函数,this 就是此对象
1 2 3 4 5 6 7 8 9 10 11 let obj = { a: 'a' , b: 'b' , test: function (arg1, arg2 ) { console .log(arg1, arg2) console .log(this .a, this .b) } } obj.test(1 , 2 )
手动模拟call 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 Function .prototype.call2 = function (context ) { if (typeof this !== 'function' ) { throw new TypeError ('Error' ) } context = context || window const { fn } = context context.fn = this const args = [...arguments].slice(1 ) const result = context.fn(...args) context.fn = fn return result } function test (arg1, arg2 ) { console .log(arg1, arg2) console .log(this .a, this .b) } test.call2({ a: 'a' , b: 'b' }, 1 , 2 )
bind bind
有两个特点:
本身返回一个全新的函数,所以需要考虑new的清空
可以保留参数,内部实现了参数的拼接
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 Function .prototype.bind2 = function (context ) { if (typeof this !== 'function' ) { throw new TypeError ('Error' ) } const that = this const args = [...arguments].slice(1 ) return function F ( ) { if (this instanceof F) { return new that(...args, ...arguments) } return that.apply(context, args.concat(...arguments)) } } function test (arg1, arg2 ) { console .log(arg1, arg2) console .log(this .a, this .b) } const test2 = test.bind2({ a : 'a' , b : 'b' }, 1 ) test2(2 )
apply apply和call实现类似,只是传入的参数形式是数组形式,而不是逗号分隔的参数序列。
因此,借助es6提供的…运算符,就可以很方便的实现数组和参数序列的转化。
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 Function .prototype.apply2 = function (context ) { if (typeof this !== 'function' ) { throw new TypeError ('Error' ) } context = context || window const { fn } = context context.fn = this let result if (Array .isArray(arguments [1 ])) { result = context.fn(...arguments[1 ]) } else { result = context.fn() } context.fn = fn return result } function test (arg1, arg2 ) { console .log(arg1, arg2) console .log(this .a, this .b) } test.apply2({ a: 'a' , b: 'b' }, [1 , 2 ])