JavaScript中的this

本来不想写this的东西,因为实在是头晕啊,讲不清楚,JavaScript中的this真是让人抓狂,好在我们调试的时候发现错误,修改就可以了。但是一位美女程序员给我推荐了一些文章,我就在这里总结一下吧。

所有的知识来自于这两个牛人的博客和MDN

  1. 廖雪峰的官方网站
  2. 王福民的博客
  3. 我的最爱MDN

由于两人都讲的很好,但是又都有一些是对方没讲到的,所以在这里汇总一下。如果能答对下面题目的,可以略过此文了。

测试题目

有时候面试的时候就会有人问到很讨厌的情况,我很想骂一声,还能不能好好写代码。

测试题目1

请问下面打印出什么?

var obj = {
    x: 10,
    fn: function() {
        function f() {
            console.log(this);
            console.log(this.x);
        }
        f();
    }
};
obj.fn();

答案是window。
fn里面的函数f才是打印this的,所以fn中this还是obj,但是f在准备打印this的时候,this不是obj。提供一个写解析器的思路:如果是全局环境,就是window,如果是对象调用,就是obj,如果不知道上下文环境,还是window吧。哈哈,不知道这样说,大家觉得直观不。这一个case我觉得还是很奇怪的,只有调试出错了才好想起来。

测试题目2

下面打印什么?

var fullname = 'John Doe';
var obj = {
    fullname: 'Colin Ihrig',
    prop: {
        fullname: 'Aurelio De Rosa',
        getFullname: function() {
            return this.fullname;
        }
    }
};

var test = obj.prop.getFullname;
console.log(test());

答案是打印'John Doe'。
这个明显可以套用万能公式。因为test()的函数在全局环境中。console.log(obj.prop.getFullname());打印的是Aurelio De Rosa

测试题目3

下面的this是什么?

<button onclick="alert((function(){return this}}()));"> Show inner this</button>

<button onclick="alert(this.tagName.toLowerCase());"> Show this</button>

这一题在下面也有答案。两个不同哦。

测试题目4

下面打印什么?

function a(xx) {
    this.x = xx;
    return this
};
var x = a(5);
var y = a(6);
console.log(x.x);
console.log(y.x);

很多人会说6......呵呵,x名称在window下面被覆盖了,x是6,但是x.x是undefined。答案是undefined,6。

this知识的总结

在JavaScript中,函数的this关键字的行为与其他语言相比有很多不同。在JavaScript的严格模式和非严格模式下也略有区别。
在绝大多数情况下,函数的调用方式决定了this的值。可以简单理解为谁调用该函数,this就是谁;或者被调用时,函数上下文是谁,谁就是this(这两句总结是我瞎掰的,对不对我们看看下面的例子)

全局上下文中的this

在全局运行上下文中(在任何函数体外部),this 指代全局对象,无论是否在严格模式下。

console.log(this.document === document); // true
// 在浏览器中,全局对象为 window 对象:
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37

万能公式:谁调用,谁是this,全局中调用,全局对象是this。

函数上下文

javascript除了全局作用域,就是函数作用域了(新的标准会有块作用域let),但是作用域和执行上下文又不一样,这个可以换一篇文章来说,this和执行上下文有关系吗?当然有关系,但是执行上下文是啥玩意啊,有一篇文章写的特别好,推荐一下简述【执行上下文】,简单描述一下,当函数准备运行的时候,准备下面这些数据:

  1. 变量、函数表达式变量“提升”声明,默认赋值为undefined;
  2. this赋值;
  3. 函数声明“提升”;
  4. 参数赋值
  5. arguments 赋值
  6. 自由变量(非本地变量)的取值作用域赋值,这里参见闭包或者作用域的知识。

数据的准备情况我们称之为“执行上下文”或者“执行上下文环境”。this的值就是在做这些准备工作的时候创建的。但是this赋值的规则是什么呢,我们看看下面。

构造函数中的this

所谓构造函数就是用来new对象的函数。其实严格来说,所有的函数都可以new一个对象,但是有些函数的定义是为了new一个对象,而有些函数则不是。另外注意,构造函数的函数名第一个字母大写(规则约定)。例如:Object、Array、Function等。

function Foo(){
    this.name = '王福民';
    this.year = 1988;
    console.log(this);
}

var f1 = new Foo();
构造函数中的this

那么我们看看构造函数怎么工作的

  1. 当一个函数被作为一个构造函数来使用(使用new关键字),它的this与即将被创建的新对象绑定。
  2. 没有返回值的时候,返回的是this,有返回值的时候,就是返回的对象。所以默认情况下,构造函数就是返回this的。
//构造函数是这么工作的:
function MyConstructor(){
  // Actual function body code goes here.  
  // Create properties on |this| as
  // desired by assigning to them.  E.g.,
  this.fum = "nom";
  // et cetera...
  // If the function has a return statement that
  // returns an object, that object will be the
  // result of the |new| expression.  Otherwise,
  // the result of the expression is the object
  // currently bound to |this|
  // (i.e., the common case most usually seen).
}

上面的代码我们可以这样使用

f1.name;
f1.year;

对于这个规则,是不是很难记住啊。那就套用万能公式:
谁调用函数,函数中的this就是谁:

new来调用构造函数,返回的this是创建的对象本身(如果你没有指定返回值),内部的this就是正在被创建的那个对象。

直接调用函数中的this

注意,以上仅限new Foo()的情况,即Foo函数作为构造函数的情况。如果直接调用Foo函数,而不是new Foo(),情况就大不一样了。

  1. 非严格模式this就是全局对象


    不是构造函数
  2. 严格模式this是undefined


    严格模式

万能公式:谁调用函数,函数this就是谁:

直接调用函数,this就是window了(非浏览器环境就是全局对象)。严格模式就是undefined

对象“方法”中的this

当以对象调用函数时,this 是调用该函数的对象.
下面的例子中,当 o.f() 被调用时,函数内的this将绑定到o对象。

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // logs 37

注意,不在对象中定义函数也没关系,因为this是在准备执行上下文中指定的(动态),和你在哪定义这种静态的代码位置没有关系。

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // logs 37

this 的绑定只受最靠近的成员引用的影响。

o.b = {
  g: independent,
  prop: 42
};
console.log(o.b.g()); // logs 42

原型链中的 this也是一样。

var o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

getter 与 setter 中的 this也一样

function modulus(){
  return Math.sqrt(this.re * this.re + this.im * this.im);
}

var o = {
  re: 1,
  im: -1,
  get phase(){
    return Math.atan2(this.im, this.re);
  }
};

Object.defineProperty(o, 'modulus', {
  get: modulus, enumerable:true, configurable:true});

console.log(o.phase, o.modulus); // logs -0.78 1.4142

这么多,怎么记呢。万能公式:谁调用函数,this就是谁。对象调用函数,对象就是this

call 和 apply

当一个函数的函数体中使用了this关键字时,通过所有函数都从Function对象的原型中继承的call()方法和apply()方法调用时,它的值可以绑定到一个指定的对象上。

当一个函数的函数体中使用了this关键字时,通过所有函数都从Function对象的原型中继承的call()方法和apply()方法调用时,它的值可以绑定到一个指定的对象上。

function add(c, d){
  return this.a + this.b + c + d;
}

var o = {a:1, b:3};

// The first parameter is the object to use as 'this', subsequent parameters are passed as 
// arguments in the function call
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

// The first parameter is the object to use as 'this', the second is an array whose
// members are used as the arguments in the function call
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

使用 call 和 apply 函数的时候要注意,如果传递的 this 值不是一个对象,JavaScript 将会尝试使用内部 ToObject 操作将其转换为对象。因此,如果传递的值是一个原始值比如 7 或 'foo' ,那么就会使用相关构造函数将它转换为对象,所以原始值 7 通过new Number(7)被转换为对象,而字符串'foo'使用 new String('foo') 转化为对象,例如:

function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]

王炸:bind 方法

ECMAScript 5 引入了 Function.prototype.bind。调用f.bind(someObject)会创建一个与f具有相同函数体和作用域的函数,但是在这个新函数中,this将永久地被绑定到了bind的第一个参数,无论这个函数是如何被调用的。

function f(){
  return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty

DOM事件处理函数中的 this

当函数被用作事件处理函数时,它的this指向触发事件的元素

// 被调用时,将关联的元素变成蓝色
function bluify(e){
  console.log(this === e.currentTarget); // 总是 true

  // 当 currentTarget 和 target 是同一个对象是为 true
  console.log(this === e.target);        
  this.style.backgroundColor = '#A5D9F3';
}

// 获取文档中的所有元素的列表
var elements = document.getElementsByTagName('*');

// 将bluify作为元素的点击监听函数,当元素被点击时,就会变成蓝色
for(var i=0 ; i<elements.length ; i++){
  elements[i].addEventListener('click', bluify, false);
}

内联事件处理函数中的 this

当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素:

<button onclick="alert(this.tagName.toLowerCase());"> Show this</button>

上面的alert会显示button。注意只有外层代码中的this是这样设置的:

<button onclick="alert((function(){return this}}()));"> Show inner this</button>

在这种情况下,没有设置内部函数的 this
,所以它指向 global/window
对象(即非严格模式下调用的函数未设置 this 时指向的默认对象)。

总结

上面例举了很多情况,我们看看万能公式还能用否。谁调用函数,谁就是this。下面的的谁调用,也可以理解为所有者,或者环境。

|谁调用|this是谁|this是否是调用者|
|---|---|
|全局上下文调用函数或者使用this|window或者全局对象|是|
|new 构造函数|构造的对象|是|
|直接调用函数|window或者undefied|除了strict模式,都是|
|对象方法中的this|对象|是|
|call或者apply|第一个参数|不是|
|bind方法|第一个参数|不是|
|DOM中的this|触发操作的元素|外层this是监听器的元素,算是,内层不是|

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

推荐阅读更多精彩内容