bind / call / apply 可用于this的显式绑定
this绑定的是 call,apply,bind 的第一个参数
call()方法
var a = {
user: 'fx',
fn: function () {
console.log(this.user) // fx
}
}
var b = a.fn
b.call(a)
通过在call方法,第一个参数表示要把b添加到哪个环境中,简单来说,this就会指向那个对象。
call方法除了第一个参数以外还可以添加多个参数,如下
var a = {
user: 'fx',
fn: function (x, xx) {
console.log(this.user) // fx
console.log(x + xx) // 520
}
}
var b = a.fn
apply()方法
apply方法和call方法有些相似,它也可以改变this的指向
var a = {
user: 'fx',
fn: function () {
console.log(this.user) // fx
}
}
var b = a.fn
b.apply(a)
同样apply也可以有多个参数,但是不同的是,第二个参数必须是一个数组,如下:
var a = {
user: 'fx',
fn: function (x, xx) {
console.log(this.user) // fx
console.log(x + xx) // 520
}
}
var b = a.fn
bind()方法
bind方法和call、apply方法有些不同,但是不管怎么说它们都可以用来改变this的指向。
先看下面一段代码:
var a = {
user:"fx",
fn:function(){
console.log(this.user);
}
}
var b = a.fn;
b.bind(a);
我们发现代码没有被打印,对,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。不会立即调用,而是将函数返回
var a = {
user:"fx",
fn:function(){
console.log(this.user); // fx
}
}
var b = a.fn;
var c = b.bind(a);
c();
如果要调用的话必须还要加上()
同样bind也可以有多个参数,并且参数可以执行的时候再次添加,但是要注意的是,参数是按照形参的顺序进行的。
var a = {
user:"fx",
fn:function(e,d,f){
console.log(this.user); // fx
console.log(e,d,f); // 10 1 2
}
}
var b = a.fn;
var c = b.bind(a,10);
c(1,2);
回调函数的中使用bind
var obj = {
name: 'fx'
}
setTimeout(function () {
console.log(this) // Object {name: "fx"}
}.bind(obj), 1000)
========
var obj = {
name: 'fx'
}
setTimeout(function () {
console.log(this) // Window
}, 1000)
如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:
var a = {
user:"fx",
fn:function(){
console.log(this); // Window
}
}
var b = a.fn;
b.apply(null);
总结:call和apply(参数为数组)都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别。
bind方法
- 对于普通函数,绑定this指向
- 对于构造函数,要保证原函数的原型对象上的属性不能丢失
Function.prototype.bind = function (context, ...args) {
// 异常处理
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
// 保存this的值,它代表调用 bind 的函数
var self = this;
var fNOP = function () {};
var fbound = function () {
self.apply(this instanceof self ?
this :
context, args.concat(Array.prototype.slice.call(arguments)));
}
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
}
也可以这么用 Object.create 来处理原型:
Function.prototype.bind = function (context, ...args) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var fbound = function () {
self.apply(this instanceof self ?
this :
context, args.concat(Array.prototype.slice.call(arguments)));
}
fbound.prototype = Object.create(self.prototype);
return fbound;
}
模拟实现call方法
Function.prototype.call = function (context) {
let context = context || window;
let fn = Symbol('fn');
context.fn = this;
let args = [];
for(let i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
let result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
ES6 的语法
Function.prototype.call = function (context, ...args) {
let context = context || window;
let fn = new Symbol('fn');
context.fn = this;
let result = eval('context.fn(...args)');
delete context.fn
return result;
}
apply方法
Function.prototype.apply = function (context, args) {
let context = context || window;
context.fn = this;
let result = eval('context.fn(...args)');
delete context.fn
return result;
}