call
模拟实现
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call(foo, 'Tom', 20);
// Tom
// 20
// 1
分析:
- call改变了this的指向,指向了foo
- bar函数执行
步骤:
- 函数设置为对象属性
- 执行函数
- 删除函数
模拟实现:
Function.prototype.call2 = function(context) {
var context = context || window;
context.fn = this;
// var args = Array.prototype.slice.call(arguments, 1);
// for(var i = 1, len = arguments.length; i < len; i++) {
// args.push('arguments[' + i + ']')
// }
// var result = eval('context.fn(' + args + ')');
var args = [...arguments].slice(1);
var result = context.fn(...args);
delete context.fn;
return result;
}
// 测试
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call2(null); // 2
console.log(bar.call2(foo, 'Tom', 20));
// 1
// Object {
// value: 1,
// name: 'Tom',
// age: 20
// }
apply
模拟实现
与call区别只是传参不同
Function.prototype.apply2 = function(context) {
var context = context || window;
context.fn = this;
var result;
if(!arguments[1]) {
result = context.fn();
} else {
result = context.fn(...arguments[1])
}
delete context.fn;
return result;
}
// 测试
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
//bar.apply2(null); // 2
console.log(bar.apply2(foo, ['Tom', 20]));
// 1
// Object {
// value: 1,
// name: 'Tom',
// age: 20
// }
bind
模拟实现
分析:
- 返回一个函数
- 可以传入参数
模拟实现版本1:
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(context, args.concat(bindArgs));
}
}
// 测试
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind2(foo);
console.log(bindFoo()); // 1
var bindFoo2 = bar.bind2(foo, 'Tom');
bindFoo2(20);
// 1
// Tom
// 20
此处忽略了bind还有一个特点,就是
一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
也就是说当 bind 返回的函数作为构造函数的时候,bind 时指定的 this 值会失效,但传入的参数依然生效。举个例子:
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind2(foo, 'daisy');
var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
分析:使实例obj可以根据原型链找到构造函数bar的值
1.obj.proto = bindFoo.prototype
2.bindFoo.prototype.proto = bar.prototype
模拟实现版本2:
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
// 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply( this instanceof fNOP ? this : context, args.concat(bindArgs));
}
// 修改返回函数的 prototype,使:fBound.prototype.__proto__ = fNOP.prototype = this.prototype 实例就可以继承绑定函数的原型链中的值
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}
网友评论