JavaScript中的this,call,apply使用及区别详解

1、this

this总是指向一个对象,而具体指向哪个对象是运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
this的指向可以分为以下四种:

  1. 作为对象的方法调用
  2. 作为普通函数调用
  3. 构造器调用
  4. 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容