不同点
fn.bind(obj)返回的是一个函数
fn.call(obj,parm1,parm2)返回的是一个处理结果
fn.apply(obj,[parm1,parm2])返回的是一个处理结果
相同点
它们共同的作用是改变函数里面 this 的指向
举例
var g = 3; // 全局变量 window的属性
let obj1 = {
g:30
}
let obj2 = {
id: Symbol("ids"),
g:300,
c:function() {
return this.g;
}
}
/*常态下调用obj2的c方法*/
console.log(obj2.c(),"obj2.c()");
//调用obj2的c方法在链式调用bind方法
console.log(obj2.c.bind(window),"obj2.c.bind(window)")
console.log(obj2.c.bind(obj1),"obj2.c.bind(obj1)")
//调用obj2的c方法在链式调用call方法
console.log(obj2.c.call(obj1),"obj2.c.call(obj1)")
//调用obj2的c方法在链式调用apply方法
console.log(obj2.c.apply(obj1),"obj2.c.apply(obj1)")
如何传参
var g = 3; // 全局变量 window的属性
let obj1 = {
g:30
}
let obj2 = {
id: Symbol("ids"),
g:300,
c:function(parms1,parms2) {
console.log(parms1+parms2);
}
}
//传参数给bind
obj2.c.bind(obj1)(1,1);
//传参数给call
obj2.c.call(obj1,2,2)
//传参数给apply
obj2.c.apply(obj1,[3,3])
bind的源码实现
Function.prototype.bind_es5 = 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); // 获取bind函数传递过来的参数
var f = function () {
return self.apply(
this instanceof self ? this : context,
args.concat(Array.prototype.slice.call(arguments))
);
}
//f.prototype = this.prototype; //不能直接这样赋值,因为后续你如果修改f.prototype ,那么 this.prototype也会被修改。
// 解决方法:使用空函数来继承原型链
// var Temp = function () {};
// Temp.prototype = this.prototype;
// f.prototype = new Temp();
// 再进行优化的解决方法:使用Object.create();
f.prototype = Object.create(this.prototype);
return f;
}
function bar(a, b) {
this.a = a;
console.log(this.value);
console.log(a);
console.log(b);
}
bar.prototype.add = {
}
let obj = {
value: 123
}
bar.bind(obj, 0)();
let fn = bar.bind_es5(obj, 0);
fn.prototype.doAdd = { // 注意:这里修改了当前fn的原型
}
let F1 = bar.bind_es5(obj, 0);
let f1 = new F1();
console.log(f1) // 发现并没有受到影响。
call的源码实现
// es5存在着属性被修改的可能性,以及使用了eval方法
Function.prototype.call_ES5 = function(context){
var obj = context || window; // 获取对象
var args = Array.prototype.slice.call(arguments,1); // 获取方法参数
obj.fn = this; // 给对象定义此方法
var result = eval('obj.fn('+args.toString()+')');
delete obj.fn;
return result;
}
// 使用es6对属性进行改进,以及避免使用eval方法
Function.prototype.call_ES6 = function(context){
var obj = context || window; // 获取对象
var args = Array.prototype.slice.call(arguments,1); // 获取方法参数
var key = Symbol('key');
obj[key] = this; // 给对象定义此方法
var result = obj[key](...args);
delete obj[key]
return result;
}
function bar(a,b,c) {
return {
value:this.value,
a:a,
b:b
}
}
var foo = {
value: 1
};
console.log(bar.call_ES5(foo,1,2,3));
console.log(bar.call_ES6(foo,1,2,3));
apply的源码实现
apply与call方法相似,只是它的参数问题,反而比call好写,而且不需要用到eval这个危险函数。