JavaScript中的this

小编推荐:Fundebug专注于JavaScript、微信小程序、微信小游戏,Node.js和Java实时BUG监控。真的是一个很好用的bug监控费服务,众多大佬公司都在使用。

本来不想写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是监听器的元素,算是,内层不是|

作者:沈寅
链接:https://www.jianshu.com/p/a2dd59b5302e

关于Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了9亿+错误事件,得到了Google、360、金山软件、百姓网等众多知名用户的认可。欢迎免费试用!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值