1、this
this总是指向一个对象,而具体指向哪个对象是运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
this的指向可以分为以下四种:
- 作为对象的方法调用
- 作为普通函数调用
- 构造器调用
- Function.prototype.apply和Function.prototype.call调用
1.作为对象的方法调用
当函数作为对象方法调用时,this指向该对象。
/**
* 1.作为对象的方法调用
*
* 作为对象方法调用时,this指向该对象。
*/
var obj = {
a: 1,
getA: function() {
console.log(this === obj); // true
console.log(this.a); // 1
}
};
obj.getA();
2.作为普通函数调用
作为普通函数调用时,this总是指向全局对象(浏览器中是window)。
/**
* 2.作为普通函数调用
*
* 不作为对象属性调用时,this必须指向一个对象。那就是全局对象。
*/
window.name = 'globalName';
var getName = function() {
console.log(this.name);
};
getName(); // 'globalName'
var myObject = {
name: "ObjectName",
getName: function() {
console.log(this.name)
}
};
myObject.getName(); // 'ObjectName'
// 这里实质上是把function() {console.log(this.name)}
// 这句话赋值给了theName。thisName在全局对象中调用,自然读取的是全局对象的name值
var theName = myObject.getName;
3.构造器调用
作为构造器调用时,this指向返回的这个对象。
/**
* 3.作为构造器调用
*
* 作为构造器调用时,this指向返回的这个对象。
*/
var myClass = function() {
this.name = "tiany";
};
var obj = new myClass();
console.log(obj.name); // tiany
console.log(obj) // myClass {name: "tiany"}
但是如果构造函数中手动指定了return其它对象,那么this将不起作用。
var myClass = function() {
this.name = "tiany";
// 加入return时,则返回的是别的对象。this不起作用。
return {
name:"ReturnOthers"
}
};
var obj = new myClass();
console.log(obj.name); // ReturnOthers
如果return的是别的数据类型,则没有问题。
var myClass = function() {
this.name = "tiany";
// 如果return的是别的数据类型
return "othername"; //返回string类型
};
var obj = new myClass();
console.log(obj.name); // tiany
4.Call和Apply
Call和Apply的用途一样。都是用来指定函数体内this的指向。
var obj1= {
name : "tiany";
getName : function(){
return this.name;
}
};
var obj2= {
name : "gwt";
};
console.log(obj1.getName() ); // tiany
console.log(obj1.getName().call(obj2) ); // gwt
丢失的this
var obj= {
name : "tiany";
getName : function(){
return this.name;
}
};
console.log(obj.getName() ); // tiany
var getName2 = obj.getName;
console.log(getName2() ); // undefined,此时this 的指向是全局windows,所以undefined
2、Call和Apply的区别
Call:第一个参数为this的指向,要传给函数的参数得一个一个的输入。
Apply:第一个参数为this的指向,第二个参数为数组,一次性把所有参数传入。
如果第一个参数为null,则this指向调用的本身。
1.改变this指向
这是call和apply最常用的用途了。用于改变函数体内this的指向。
var name = "GlobalName"
var func = function() {
console.log(this.name)
};
func(); // "GlobalName"
var obj = {
name: "tiany",
getName: function() {
console.log(this.name)
}
};
obj.getName.apply(window) // "GlobalName" 将this指向window
func.apply(obj) // "tiany" 将this指向obj
2.借用其它对象的方法
(function(a, b) {
console.log(arguments) // 1,2
// 调用Array的原型方法
Array.prototype.push.call(arguments, 3);
console.log(arguments) // 1,2,3
})(1,2)
函数具有arguments属性,而arguments是一个类数组。
但是arguments是不能直接调用数组的方法的,所以我们要用call或者apply来调用Array对象的原型方法。
原理也很容易理解,比如刚才调用的是push方法,而push方法在谷歌的v8引擎中,源代码是这样的:
function ArrayPush() {
var n = TO_UINT32(this.length); // 被push对象的长度
var m = % _ArgumentsLength(); // push的参数个数
for (var i = 0; i < m; i++) {
this[i + n] = % _Arguments(i); // 复制元素
}
this.length = n + m; //修正length属性
return this.length;
}
Array.prototype.push
var a = {};
Array.prototype.push.call(a,'first');
alert(a.length); // 1
alert(a[0]); //first
3、Function.prototype.bind
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Function.prototype.bind = function(context){
var self = this; //保存原函数
return function(){ //返回一个新的函数
return self.apply(context,arguments); //执行新的函数时,会将之前传入的context当做新函数体的this
}
}
var obj = {
name : 'tiany'
};
var func = function(){
alert(this.name); // tiany
}.bind(obj);
func();
对于apply和call两者在作用上是相同的,但两者在参数上有区别的。对于第一个参数意义都一样,但对第二个参数:apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。如 func.call(func1,var1,var2,var3)对应的apply写法为:func.apply(func1,[var1,var2,var3])
相关参考 《JavaScript设计模式与开发实践》
其他一些资料
http://uule.iteye.com/blog/1158829