回首向来萧瑟处,归去,也无风雨也无晴 — call、apply和bind

授业至今,技艺日有进益,许是看的多了,先时似是而非的东西,今时也多有顿悟,call和apply学的早,用得少,同是作用于this,bind就用的多,但这个函数扩展方法是ECMAscript5里的,IE[6-8]不支持,移动端可以放心用,这里说与某家主公知道。

this之于call、apply和bind,五花肉之于东坡肉、红烧肉和回锅肉,反复思量,深感经世以来比喻之周全者未有过于此者。因其关键,蒙师授业之初费了大周折讲它,终了一句话“.前头谁调用,this就是谁,没有.前头,就是window”醍醐灌顶。

js中this总是指向一个对象,情况有4种                                                                                  

1、作为对象的方法调用
var persion = {
	name : "haha",
	getName : function(){
		console.log(this === persion);//true
		console.log(this.name);//"haha"
	}
};
persion.getName();

2、作为普通函数调用

此时this总是指向全局对象window

window.name = "global";
	var getName = function(){
	return this.name;
}
console.log(getName());//"global"

或者

window.name = "global";
var persion = {
	name : "haha",
	getName : function(){
		return this.name;
	}
}

console.log(persion.getName());//"haha"
var getName = persion.getName;
console.log(getName());//"global"

DOM节点的事件函数内部,有个局部的callback方法,callback被当做普通函数调用时,callback内部的this指向了window,但是我们想让它指向该DOM节点

document.getElementById("btn").onclick = function(){
	console.log(this.id)//"btn"
	var callback = function(){
        console.log(this) //window
		console.log(this.id) //undefined
	}
	callback();
}

常用的解决方案

document.getElementById("btn").onclick = function(){
	console.log(this.id)//"btn"
	var that = this;
	var callback = function(){
		console.log(that.id)//"btn"
	}
	callback();
}

在ECMAScript5的strict严格模式下

document.getElementById("btn").onclick = function(){
    "use strict"
	console.log(this.id)//"btn"
	var callback = function(){
        console.log(this) //undefined
		console.log(this.id) //undefined
	}
	callback();
}

strict这种模式多余的很

3、构造器调用

js函数大多数都可以当做构造器使用,当用new 运算符调用函数时,该函数总会返回一个对象,通常情况下,构造器里的this就指向返回的这个对象

var Persion = function(){
	this.name = "haha";
}

var persion1 = new Persion();
console.log(persion1.name);//"haha"

用new调用构造器时,如果构造器显式地返回了一个object类型的对象,那么运行的结果就是返回的这个对象,不是之前期待的那个this

var Persion = function(){
	this.name = "haha";
	return {
		name : "hehe"
	}
}

var persion1 = new Persion();
console.log(persion1.name);//"hehe"

构造器不显式返回数据或者返回非对象类型的数据,不会造成上述问题

var Persion = function(){
	this.name = "haha";
	return  "hehe";
}

var persion1 = new Persion();
console.log(persion1.name);//"haha"

4、call和apply调用

call和apply可以动态地改变传入函数的this

var persion1 = {
	name : "haha",
	getName : function(){
		console.log(this.name + " from persion1");
	}
};

var persion2 = {
	name : "hehe",
	getName : function(){
		console.log(this.name + " from persion2");
	}
};
console.log(persion1.getName.call(persion2));// "hehe from persion2"

call和apply

call和apply都是改变this指向,区别在于参数传入的形式不同

var func = function(a,b,c){
	console.log([a,b,c]);//[1,2,3]
}
func.call(null,1,2,3);
func.apply(null,[1,2,3]);

call和apply第一个参数传入的都是函数体内的this指向,不同在于call第二参数往后依次传入与函数体形参相对应的实参,apply第二个参数是一个实参数组,数组中的元素同样与函数体的形参相对应。

孰用孰不用,有一种说法apply比call的使用效率更高,因为在js的参数在内部就是用一个数组来表示的,用arguments可以访问到。

使用call和apply的时候传入的第一个参数是null,函数执行的时候this会指向默认的宿主对象,也就是window。

var func = function(a,b,c){
	console.log(this === window);//true
}
func.call(null,1,2,3);

严格模式下情况就不一样了

var func = function(a,b,c){
	console.log(this === null);//true
}
func.call(null,1,2,3);

它们的用途

1、改变this指向

var persion1 = {
	name : "haha",
	getName : function(){
		console.log(this.name + " from persion1");
	}
};

var persion2 = {
	name : "hehe",
	getName : function(){
		console.log(this.name + " from persion2");
	}
};
console.log(persion1.getName.call(persion2));// "hehe from persion2"

一样的代码再来一遍

2、模拟bind方法

bind方法低级浏览器里没有,但可以用apply来封装

Function.prototype.bind = function(context){
	var self = this;
	return function(){
		return self.apply(context,arguments);
	}
}
var obj = {
	name:"haha"
}

var getName = function(){
	console.log(this.name);//"haha"
}.bind(obj);

3、借用其他对象方法

var A = function(name){
	this.name = name;
}
var B = function(){
	A.apply(this,arguments);
}
B.prototype.getName = function(){
	return this.name;
}

var b = new B('hehe');
console.log(b.getName());//"hehe"

上面是的场景是“借用构造函数”,通过这种技术,可以实现出类似继承的效果,此外还有一种借用场景

(function(){
	Array.prototype.push.apply(arguments,3);
	console.log(arguments);//[1,2,3]
})(1,2)

函数的参数列表arguments是一个类数组

(function(){
	console.log(typeof arguments);//object
	Array.prototype.push.call(arguments,3);
	console.log(arguments);//[1,2,3]
})(1,2)

本身不具备push方法,可以使用call或apply借用Array.prototype对象的方法,前提是借用方法的对象要可以存取属性、length属性可读写

《JavaScript设计模式与开发实践》是本好书

 

 

转载于:https://my.oschina.net/iamllb/blog/736360

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值