1 全局环境下
在全局环境下,this 始终指向全局对象(window), 无论是否严格模式
console.log(this.document === document); // true // 在浏览器中,全局对象为 window 对象: console.log(this === window); // true this.a = 37; console.log(window.a); // 37
2 函数上下文中
2.1 函数直接调用
非严格模式下,this 默认指向全局对象window;
严格模式下, this为undefined;
function f1(){ return this; } f1() === window; // true function f2(){ "use strict"; // 这里是严格模式 return this; } f2() === undefined; // true
2.2 对象内部方法中的this
this指向只和调用函数的对象有关;
多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window);
function independent() { return this.prop; } var o = { prop: 37, f: independent }; console.log(o.f()); //37 var a = o.f; console.log(a()): //undefined o.b = { g: independent, prop: 42 }; // 嵌套调用 console.log(o.b.g()); // logs 42
2.3 原型链中方法的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
2.4 getter 与 setter 中的 this
用作 getter 或 setter 的函数都会把 this 绑定到设置或获取属性的对象;
function sum() { return this.a + this.b + this.c; } var o = { a: 1, b: 2, c: 3, get average() { return (this.a + this.b + this.c) / 3; } }; Object.defineProperty(o, 'sum', { get: sum, enumerable: true, configurable: true}); console.log(o.average, o.sum); // logs 2, 6
2.5 构造函数中this
构造函数中的this与被创建的新对象绑定;
(当构造器返回的默认值是一个this引用的对象时,可以手动设置返回其他的对象,如果返回值不是一个对象,返回this)
function C(){ this.a = 37; console.log(this.a) } var c = new C() // 37
2.6 类上下文中的this
this 在 类 中的表现与在函数中类似,因为类本质上也是函数,但也有一些区别和注意事项。
类的构造函数中,this 是一个常规对象,与构造函数的this一样;
类中所有非静态的方法都会被添加到 this 的原型中;
(静态方法不是 this 的属性,它们只是类自身的属性。)
如果是派生类,使用this时需要先在构造函数中调用 super(参数)生成一个 this 绑定;
class Example { constructor() { const proto = Object.getPrototypeOf(this); console.log(Object.getOwnPropertyNames(proto)); } first(){} second(){} static third(){} } new Example(); // ['constructor', 'first', 'second']
2.7 DOM事件处理函数中的this
事件源.onclik = function(){ } this指向事件源
事件源.addEventListener(function(){ }) //this指向事件源
var div = document.getElementById('divId'); div.addEventListener('click', function () { console.log(this); // div }, false); div.onclick = function() { console.log(this); // div }
2.8 内联事件中的this
当this传入内联处理函数时,它的this指向监听器所在的DOM元素;
当this没有传入内联处理函数时,其this指向等同于 函数直接调用的情况,即在非严格模式指向全局对象window, 在严格模式指向undefined;
<button οnclick="console.log(this);" /> // button <button οnclick="(function() { console.log(this); })();" /> // window <button οnclick="(function() { 'use strict' console.log(this); })();" /> // undefined
2.9 setTimeout 和 setInterval中的普通函数this
setTimeout 和 setInterval中的普通函数this指向全局对象window;
(如果传入的函数已绑定this或者是箭头函数,则不适用这条,需要继续往下看)
function Person() { this.age = 0; setTimeout(function() { console.log(this); }, 3000); } var p = new Person();//3秒后返回 window 对象
2.10 箭头函数中的 this
箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值;
function Person() { this.age = 0; setInterval(() => { // 回调里面的 `this` 变量就指向了期望的那个对象了 this.age++; }, 3000); } var p = new Person();
2.11 嵌套函数中的this
this在嵌套函数中不会传递,即直接调用与普通函数一样,非严格模式下为window,严格模式下为undefined;
var obj = { A: function() { function B() { console.log(this); }; B(); // window function C() { 'use strict' console.log(this); } C(); // undefined } }
3 修改this的指向(call、apply、bind)
这三个函数都在Function的原型链上(Function.prototype),函数对象可以直接调用;
非严格模式下第一个参数为null或undefined时,this为window,原始值会被包装;
严格模式下this就是传入的值;
3.1 call、apply函数
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
function Product(name, price) { this.name = name; this.price = price; } function Food(name, price) { Product.call(this, name, price); this.category = 'food'; } console.log(new Food('cheese', 5).name); // expected output: "cheese"
apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
var array = ['a', 'b']; var elements = [0, 1, 2]; array.push.apply(array, elements); console.info(array); // ["a", "b", 0, 1, 2]
3.2 bind函数
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
const obj1 = { a: 1, getA: function() { return this.a; }, sum: function(x, y) { return this.a + x + y; } } const obj2 = { a: 2 }; const obj2GetA = obj1.getA.bind(obj2); obj2GetA(); // const sum2 = obj1.sum.bind(obj2, 2); sum2(2); // 2 + 2 + 2 = 6 // 如果绑定后的函数使用new来调用,原来提供的 this 就会被忽略。 // 作为构造函数使用的绑定函数,可能不应该用在任何生产环境中。 // bind的一个简单实现 if (!Function.prototype.bind) (function(){ var ArrayPrototypeSlice = Array.prototype.slice; Function.prototype.bind = function(otherThis) { if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var baseArgs= ArrayPrototypeSlice.call(arguments, 1), baseArgsLength = baseArgs.length, // fToBind是调用bind的函数本身 fToBind = this, // 绑定后的函数用new调用时,用来作为生成对象的原型的构造函数 fNOP = function() {}, // 最终绑定的函数 fBound = function() { baseArgs.length = baseArgsLength; // reset to default base arguments baseArgs.push.apply(baseArgs, arguments); // 下面的this是fBound执行时的this,如果fBound使用new调用,则使用生成对象的作为this return fToBind.apply( fNOP.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs ); }; if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); return fBound; }; })();
参考
Function.prototype.bind() - JavaScript | MDN
Function.prototype.call() - JavaScript | MDN