(一)call原理解析
如下所示为一个call的使用栗子:
function add(c,d){
return this.a + this.b + c + d;
}
const obj = {a:1, b:2}
add.call(obj, 3, 4) //10
call方法改变了add方法中的this指向,指向了第一个参数obj,上述代码等同于下列形似:
const obj = {
a: 1,
b: 2,
add: function (c, d) {
return this.a + this.b + c + d;
}
}
1.定义一个对象obj,并添加方法add
2.obj.add(5,6)等同于add.call(obj,5,6),add方法中的this指向的是调用者obj
3.但是该属性是多余的,所以在执行完再删除
所以,简单来说是如下三步:
//1.将函数设置为对象的属性
obj.fn = bar
//2.执行该函数
obj.fn()
//3.删除该函数
delete obj.fn
用es6实现方法如下:
Function.prototype.es6Call = function (context) {
var context = context || window
context.fn = this //定义指向this
var args = []
for(var i=1; i<arguments.length; i++){
args.push(arguments[i])
}
var result = context.fn(...args)
delete context.fn
return result
}
add.es6Call(obj, 5, 6) //14
(二)apply原理解析
apply与call非常类似,区别只是传入的第二个参数为数组,因此,实现如下:
Function.prototype.es6Apply = function (context, arr) {
var context = context || window
context.fn = this
var result
if (!arr) {
result = context.fn()
} else {
if (! arr instanceof Array) throw new Eroor('params must be array')
result = context.fn(...arr)
}
return result
}
add.es6Apply(obj, [7, 8]) //18
(三)bind原理解析
先看一个bind使用的例子
function foo(c, d) {
this.b = 100
console.log(this.a)
console.log(this.b)
console.log(c)
console.log(d)
}
// 我们将foo bind到{a: 1}
var func = foo.bind({a: 1}, '1st');
func('2nd'); // 1 100 1st 2nd
// 即使再次call也不能改变this。
func.call({a: 2}, '3rd'); // 1 100 1st 3rd
// 当 bind 返回的函数作为构造函数的时候,
// bind 时指定的 this 值会失效,但传入的参数依然生效。
// 所以使用func为构造函数时,this不会指向{a: 1}对象,this.a的值为undefined。如下
// new func('4th') //undefined 100 1st 4th
bind的es6实现方法如下:
Function.prototype.es6Bind = function (context, ...rest) {
if (typeof this !== 'function') throw new TypeError('invalid invoked!')
var self = this
return function F (...args) {
if (this instanceof F) {
return new self(..rest, ..args)
}
return self.apply(context, rest.concat(args))
}
}
var func = foo.es3Bind({a: 1}, '1st');
func('2nd'); // 1 100 1st 2nd
func.call({a: 2}, '3rd'); // 1 100 1st 3rd
new func('4th'); //undefined 100 1st 4th