天天看點

Javascript中的this指向1 全局環境下2 函數上下文中3 修改this的指向(call、apply、bind)參考

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 onclick="console.log(this);" /> // button
<button onclick="(function() { console.log(this); })();" /> // window
<button onclick="(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;
  };
})();           

複制

參考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply

https://www.cnblogs.com/dongcanliang/p/7054176.html