this指向入门完整版 - 王云飞

this指向

与其他语言相比,函数的this关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。

在绝大多数情况下,函数的调用方式决定了this的值。this是在运行时基于函数的执行环境绑定的:在全局函数中this等于window,而当函数被作为某个对象方法调用时,this等于那个对象。ES5引入了bind方法来设置函数的this值,而不用考虑函数如何被调用的。

全局环境

无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。

// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"

函数(运行内)环境

在函数内部,this的值取决于函数被调用的方式。
普通函数内部的this分两种情况,严格模式和非严格模式。

  • 非严格模式下,this 默认指向全局对象window
function f1(){
  return this;
}
//在浏览器中:
f1() === window;   //在浏览器中,全局对象是window

//在Node中:
f1() === global;

然而,在严格模式下,this将会默认为undefined

function f2(){
  "use strict"; // 这里是严格模式
  return this;
}

f2() === undefined; // true

作为对象的方法

当函数作为对象里的方法被调用时,它们的this是调用该函数的对象。

  1. 函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。
  2. 多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window)。
    下面的例子中,当o.f()被调用时,函数内的this将绑定到o对象。
var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

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

请注意,这样的行为,根本不受函数定义方式或位置的影响。在前面的例子中,我们在定义对象o的同时,将函数内联定义为成员 f 。但是,我们也可以先定义函数,然后再将其附属到o.f。这样做会导致相同的行为:

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

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

同样,this的绑定只受最靠近的成员引用的影响。在下面的这个例子中,我们把一个方法g当作对象o.b的函数调用。在这次执行期间,函数中的this将指向o.b。事实证明,这与他是对象 o的成员没有多大关系,最靠近的引用才是最重要的。

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

原型链中的this

对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么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

可以看出, 在p中没有属性f,当执行p.f()时,会查找p的原型链,找到 f 函数并执行,但这与函数内部this指向对象 p 没有任何关系,只需记住谁调用指向谁。
以上对于函数作为getter & setter 调用时同样适用。

作为构造函数

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

虽然构造器返回的默认值是this所指的那个对象,但它仍可以手动返回其他的对象(如果返回值不是一个对象,则返回this对象)。

/*
 * 构造函数这样工作:
 *
 * function MyConstructor(){
 *   // 函数实体写在这里
 *   // 根据需要在this上创建属性,然后赋值给它们,比如:
 *   this.fum = "nom";
 *   // 等等...
 *
 *   // 如果函数具有返回对象的return语句,
 *   // 则该对象将是 new 表达式的结果。 
 *   // 否则,表达式的结果是当前绑定到 this 的对象。
 *   //(即通常看到的常见情况)。
 * }
 */

function C(){
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37


function C2(){
  this.a = 37;
  return {a:38};
}

o = new C2();
console.log(o.a); // logs 38

在刚刚的例子中(C2),因为在调用构造函数的过程中,手动的设置了返回对象,与this绑定的默认对象被丢弃了。(这基本上使得语句 “this.a = 37;”成了“僵尸”代码,实际上并不是真正的“僵尸”,这条语句执行了,但是对于外部没有任何影响,因此完全可以忽略它)。

作为一个DOM事件处理函数

当函数被用作事件处理函数时,它的this指向触发事件的元素(一些浏览器在使用非addEventListener的函数动态添加监听函数时不遵守这个约定)。

// 被调用时,将关联的元素变成蓝色
function bluify(e){
    this.style.backgroundColor = '#A5D9F3';
}

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

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

作为一个内联事件处理函数

  1. 当代码被内联处理函数调用时,它的this指向监听器所在的DOM元素
  2. 当代码被包括在函数内部执行时,其this指向等同于 ****函数直接调用****的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined
<button onclick="console.log(this)"></button> // button
<button onclick="(function() { console.log(this) })()"></button> // window
<button onclick="(function() {'use strict'; console.log(this) })()"></button> // undefined

setTimeout & setInterval

对于延时函数内部的回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)

//默认情况下代码
function Person() {  
    this.age = 0;  
    setTimeout(function() {
        console.log(this);
    }, 3000);
}

var p = new Person();//3秒后返回 window 对象
//通过bind绑定
function Person() {  
    this.age = 0;  
    setTimeout((function() {
        console.log(this);
    }).bind(this), 3000);
}

var p = new Person();//3秒后返回构造函数新生成的对象 Person{...}

箭头函数中的 this

箭头函数的this定义:箭头函数的this是在定义函数时绑定的,不是在执行过程中绑定的。简单的说,函数在定义时,this就继承了定义函数的对象。
一句话,箭头函数内的this就是箭头函数外的那个this为什么?因为箭头函数没有自己的this。

所以,这会很好的解决匿名函数和setTimeout和setInterval的this指向问题。我们不用再去给其用that变量存储this。

// demo1
var obj = {
    func: function () {
        setTimeout(function () {
            console.log(this) // window
        }, 100)
        // console.log(this)
    },
    say:function () {
        setTimeout(() => {
          console.log(this) // obj
        }, 1000)
    }
  }
obj.func()
obj.say()
// demo2
var a = 0
var obj =  {
  a: 1,
  b1: this.a,
  b2: () => this.a,
};

console.log(obj.b1);  // 0
console.log(obj.b2());  // 0

根据结果可以看到obj对象中的属性b1的值来自全局变量a,而不是obj对象的属性a,也就是说b1: this.a,中的this指向window。而b2: () => this.a,中的this同样指向window,返回全局变量a的值。

// demo3
var a = 0;
function fn(){
    var a = 2;
    console.log(this.a);  // 0
    const subf = ()=>this.a;
    console.log(subf()); // 0
}
fn()
/*
箭头函数中有this就当做这个箭头函数不存在
fn函数 -> window.fn()
*/
// demo4
var a = 0
var obj2 =  {
    a: 3,
    b1: function(){ return this.a;},
    b2: function(){ return ()=>this.a},
};
console.log(obj2.b1());  // 3
console.log(obj2.b2()());  // 3
/*
箭头函数中有this就当做这个箭头函数不存在
this.a 指向的事当前的对象,因此结果都是:3
*/
// demo6
var obj3 =  {
  a: 4,
  b1: function(){ return this.a;},
  b2: function(){ return ()=> () => () => this.a},
};
console.log(obj4.b1());  // 4
console.log(obj4.b2()()()());  // 4
/*
这里箭头函数返回箭头函数再返回箭头函数,最后一个箭头函数返回this.a。这个this是谁?就是最后箭头函数外面的外面的外面的那个this,也就是obj3.b1中的那个this,也就是obj3.a。:)  
实情是这样:箭头函数没有自己的this,所以不管嵌套多少层,都不影响this是谁。
*/
// demo7
var a = 0
function f0() {
    var a = 5;
    setTimeout(function() {
        var b1 = this.a;
        console.log(b1);  // 0
    }, 100);
    setTimeout(function() {
        var b2 = ()=> this.a;
        console.log(b2());  // 0
    }, 200);
}

f0(); // 0  0
/*
setTimeout中的function运行于全局环境下,所以第一个就是:0
第二个setTimeout中的函数是箭头函数,箭头函数里面的this和setTimeout所处环境一直,因此指向的也是window下的a结果就是0

*/

更多干货请点击 - this指向完整版

同学们听我说

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值