原生call、apply、bind的使用
这三者出现的原因都是与JavaScript
中非常非常重要的this
有关。
例1
var name = '小王', age = 17;
var obj = {
name: '小张',
objAge: this.age,
myFun: function() {
console.log( this.name + "年龄" + this.age );
}
}
obj.objAge; // 17
obj.myFun(); // 小张年龄 undefined
例2
var fav = '剑圣';
function shows() {
console.log(this.fav);
}
shows(); // 剑圣
比较一下这两者的this
指向的差别, 第一个打印里面的this
指向obj
, 第二个全局声明的shows()
函数this
是window
;
call、apply、bind都是用来重新定义this这个对象的
如:
var name = '小王', age = 17;
var obj = {
name: '小张',
objAge: this.age,
myFun: function() {
console.log( this.name + '年龄' + this.age );
}
}
var db = {
name: '盲僧',
age: 99
}
obj.myFun.call(db); // 盲僧年龄99
obj.myFun.apply(db); // 盲僧年龄99
obj.myFun.bind(db)(); // 盲僧年龄99
以上除了bind
方法后面多了个()
外, 结果返回都一致!
由此得出结论, bind
返回的是一个新的函数, 你必须调用它才会被执行.
对比call、bind、apply传参情况
var name = '小王', age = 17;
var obj = {
name: '小张',
objAge: this.age,
myFun: function(fm, t) {
console.log( this.name + '年龄' + this.age, '来自' + fm + '来自' + t );
}
}
var db = {
name: '锐雯',
age: 99
}
obj.myFun.call(db,'成都','上海'); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.apply(db,['成都','上海']); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,'成都','上海')(); // 德玛 年龄 99 来自 成都去往上海
obj.myFun.bind(db,['成都','上海'])(); // 德玛 年龄 99 来自 成都, 上海去往 undefined
微妙的差距!
从上面四个结果不难看出:
call 、bind 、 apply
这三个函数的第一个参数都是 this
的指向对象,第二个参数差别就来了:
call
的参数是直接放进去的,第二第三第 n
个参数全都用逗号分隔,直接放到后面obj.myFun.call(db,'成都', ... ,'string' )
。
apply
的所有参数都必须放在一个数组里面传进去obj.myFun.apply(db,['成都', ..., 'string' ])
。
bind
除了返回是函数以外,它 的参数和 call
一样。
当然,三者的参数不限定是 string
类型,允许是各种类型,包括函数 、 object
等等!
手写call、apply、bind
call
改变this指向用的,可以接受多个参数
Function.prototype.myCall = function(ctx) {
ctx = ctx || window; // ctx 就是 obj
let fn = Symbol();
ctx[fn] = this; // this就是foo
let result = ctx[fn](...arguments);
delete ctx[fn];
return result;
}
let obj = { name: 'CreatorRay' };
function foo() { return this.name };
// 就是把foo函数里的this指向, 指向obj
console.log(foo.myCall());
/**
* 用Symbol是因为他是独一无二的, 避免和obj里的属性重名
* 原理就是把foo添加到obj里, 执行foo拿到返回值, 再从obj里把foo删掉
*
*/
apply
原理同上,只不过apply接受第二个参数是数组,不支持第三个参数
Function.prototype.myApply = function(ctx) {
ctx = ctx || window;
let fn = Symbol();
ctx[fn] = this;
let result;
if(arguments[1]) {
result = ctx[fn](...arguments[1]);
}else {
result = ctx[fn]();
}
delete ctx[fn];
return result;
}
bind
Function.prototype.myBind = function(ctx) {
const self = this;
const fn = function() {};
const bind = function() {
const _this = this instanceof fn ? this : ctx;
return self.apply(_this, [...args, ...arguments]);
}
fn.prototype = this.prototype;
bind.prototype = new fn();
return bind;
}
/**
* bind 不会立即执行, 会返回一个函数
* >>> 函数可以直接执行并且传参, 如foo.myBind(obj, 1)(2, 3), 所以需要
* [...args, ...arguments] 合并参数
* >>> 函数也可以new, 所以要判断原型 this instanceof fn
*
*/
call、apply、bind的区别
- 都可以改变this指向
- call和apply会立即执行,bind不会,而是返回一个函数
- call和bind可以接收多个参数,apply只能接收两个,第二个是数组
- bind参数可以分多次传入