this

72 阅读3分钟

一:概念

  • thisJavaScript 关键字;
  • this 是当前执行期上下文对象的一个属性;
  • this 在不同的环境、不同作用下表现是不同的;

二:this的表现

1. 全局上下文

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

console.log(this === window); // true

var a = 1;
var b = function () {
  return 'function'
};
console.log(window.a === a); //true
console.log(window.b === b); //true

如何获取不同环境下的全局对象?

  • web浏览器:windowselfframesthis
  • Node:global
  • worker: self
  • 通用:globalThis
// web
var a = 'global-a';
var obj = {
  a:'obj-a',
  test: function () {
    console.log(this.a); //obj-a
    console.log(window.a); //global-a
    console.log(self.a); //global-a
    console.log(frames.a); //global-a
    console.log(globalThis.a); //global-a
  }
};
obj.test();

// node
var a = 'global-a';
global.b = 'global-b';
var obj = {
  a:'obj-a',
  test: function () {
    console.log(this.a); //obj-a
    console.log(global.a); //undefined
    console.log(globalThis.a); //undefined
    console.log(global.b); //global-b
    console.log(globalThis.b); //global-b
  }
};
obj.test();

2. 函数上下文

严格模式:如果进入执行环境时没有设置 this的值(直接调用的),this会为undefined

非严格模式:如果进入执行环境时没有设置 this的值(直接调用的),this会为window

'use strict';
function test() {
  return this;
}
console.log(test()); //undefined

function test() {
  'use strict';
  return this;
}
console.log(test()); //undefined

// 谁调用函数,函数内部的执行默认就是谁(默认绑定)
window.test = function () {
  'use strict';
  return this;
};
console.log(window.test());

3. 类上下文(new 绑定)

类本质上也是函数; 类可以理解为一个容器/模块/壳子/作用域;

// 以下两种方式是等价的;
class Test{
  constructor() {}
  say(){}
  static do(){}
}

const Test = (function(){
  function Test(){}
  Test.prototype.say = function(){}
  Test.do = function(){}
  window.Test = Test;
}());

this绑定:new出来的实例对象;

class Test {
  constructor(){
    // 实例话的时候会调用constructor函数,生成一个this对象;把属性或者方法放进去;
    // this = {
    //  test: function(){...}
    // }
    this.test = function () {
      console.log('11111:' + this); // 11111:[object Object]
    }
  }

  // 这里添加的方法是在构造函数的原型上添加的
  // Test.prototype = {
  //    test: function(){...}
  // }
  // 因为在自己的this中能找到test方法,就不会来找原型上的。
  test(){
    console.log('2222:' + this);
  }
}
var obj = new Test();
obj.test();

4. 显示绑定:call、apply、bind(只生效一次)

var a = 2;
var obj = {
  a: 1,
};
var obj2 = {
  a: 100
};
function test() {
  console.log(this.a)
}
test(); //2
test.call(obj); //1
test.apply(obj); //1

// bind只会生效一次
var test1 = test.bind(obj);
test1(); //1
var test2 = test1.bind(obj2);
test2(); //1

var t = test.bind(obj2).bind(obj);
t(); //100

5. 箭头函数

  • 严格模式下也指向window
'use strict';
const test = ()=>{
  console.log(this); //window
};
test();

const test = ()=>{
  'use strict';
  console.log(this); //window
};
test();
  • 忽略任何形式的显示绑定更改this
var obj = {a:1};
var a = 2;
const test = ()=>{
  console.log(this.a)
};
test(); //2
test.call(obj); //2
test.apply(obj); //2
test.bind(obj)(); //2
  • 箭头函数一定不是一个构造函数;
new test(); //Uncaught TypeError: test is not a constructor
  • 指向外层非箭头函数的作用域的this指向;
var obj = {
  a: 1
};
obj.test = function () {
  var t1 = () => {
    var t2 = () => {
      // this -> obj
      console.log(this);
    };
    t2();
  };
  t1();
};
obj.test();

obj.test = function () {
  var t1 = function(){
    var t2 = () => {
      // this -> window
      console.log(this);
    };
    t2();
  };
  t1();
};
obj.test();

6. 对象的方法、原型链、getter setter

取最近的引用;

var o = {prop: 37};

function independent() {
  return this.prop; 
}
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42 函数中的`this`将指向`o.b`
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
var obj = {};
Object.defineProperty(obj, 'a', {
  get: function () {
    console.log(this); //obj
  }
});
obj.a;

7. 事件处理函数

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

var OBtn = document.getElementById('btn');
OBtn.onClick = function () {
  console.log(this); // button的dom元素
};

OBtn.addEventListener('click', function () {
  console.log(this); // button的dom元素
}, false);

内联事件:

<!--dom元素-->
<button onclick="console.log(this)">test</button>
<!--window-->
<button onclick="(function(){console.log(this)}())">test1</button>

优先级:new绑定 > 显示绑定(call、apply、bind) > 隐式绑定(谁调用指向谁)> 默认绑定(非严格模式window)