Skip to main content

JavaScript-this

在JS中的this是函式中的關鍵字(keyword),且和其他語言有些許的不同、在不同模式中(strict/non-strict)也有不同。多數時候this的值(value)是被呼叫的方式所決定。

Global context

在全域的執行環境中,this指的是全域物件(global object)。

*在網頁瀏覽器中,window object = global object

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

a=66;
console.log(this.a);//66

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

Functional context

在函式中,this的值由呼叫方式決定。

  1. 在非嚴格模式中(可以未宣告就使用變數),並且在此this的值不是在呼叫時設定,因此this被預設為全域變數

    function f1(){
    return this;
    }
    //in a browser
    f1()===window;//true
    //in nodejs
    f1()===globaThis;//true
  2. 在嚴格模式,會變成undefined

    function f2(){
    'use strict';//strict mode
    return this;
    }
    f2()===undefined;//true
  3. 在函式中設定this的值可以使用callapply,兩者主要不同在於傳送的Argument不同。

    call:argument list

    //call
    function Product(name, price) {
    this.name = name;
    this.price = price;
    }

    function Food(name, price) {
    Product.call(this, name, price);
    this.category = 'food';
    }

    function Toy(name, price) {
    Product.call(this, name, price);
    this.category = 'toy';
    }

    const cheese = new Food('feta', 5);
    const fun = new Toy('robot', 40);

    apply:single array of argument

    //apply
    const numbers = [5, 6, 2, 3, 7];

    const max = Math.max.apply(null, numbers);

    console.log(max);
    // expected output: 7

    const min = Math.min.apply(null, numbers);

    console.log(min);
    // expected output: 2

    Use call and apply in function

    function add(c, d) {
    return this.a + this.b + c + d;
    }
    var o = {a: 1, b: 3};
    add.call(o, 5, 7); // 16
    add.apply(o, [10, 20]); // 34
  4. bind設定

    在ES5中加入的。bind會建立一個和指定函數相同作用域、相同內容的新函數,但傳入函數的argument會永久作用於原始的函數,且之後的新function也會被第一個使用bind時的arguments設定所影響。

    function f() {
    return this.a;
    }

    var g = f.bind({a: 'azerty'});
    console.log(g()); // azerty

    var h = g.bind({a: 'yoo'}); // bind only works once!
    console.log(h()); // azerty

    var o = {a: 37, f: f, g: g, h: h};
    console.log(o.a, o.f(), o.g(), o.h()); // 37,37, azerty, azerty
  5. Arrow function箭頭函式

    在箭頭函式中,this的值是在被定義時就決定了(也就是說會維持在原先的enclosing lexical context),並不會因為呼叫的方式而有所改變,就算使用callapplybind也一樣。

    // Call as a method of an object
    var obj = {func: foo};
    console.log(obj.func() === globalObject); // true

    // Attempt to set this using call
    console.log(foo.call(obj) === globalObject); // true

    // Attempt to set this using bind
    foo = foo.bind(obj);
    console.log(foo() === globalObject); // true
    • 設定一object,裡面設定method 稱為bar,bar會回傳一個function,而此function會回傳this的值。因為此function是箭頭函式,因此它的this值在建立時就被綁定在它的enclosing function內。  

      var obj = {
      bar: function() {
      var x = (() => this);
      return x;
      }
      };

         呼叫作為obj的method的bar,並以fn接收回傳的function

      var fn = obj.bar();
      console.log(fn()); //[function:bar]

          僅以fn2指向bar這個method而沒有呼叫

      var fn2 = obj.bar;
      console.log(fn2()());//global object

Class context

因為class也算是一種函式,所以行為類似,僅有些許不同。

在class的constructor中,this是一般物件(regular object)。所有non-static的method([method](Method - MDN Web Docs Glossary: Definitions of Web-related terms | MDN (mozilla.org)) 分為:Instance Methods及 Static Methods)都會被加入this這個prototype。而static method則是屬於class本身的一種屬性(property)。

class Example {
constructor() {
const proto = Object.getPrototypeOf(this);
console.log(Object.getOwnPropertyNames(proto));
}
first(){}//non-static method
second(){}//non-static method
static third(){}
}

new Example(); // ['constructor', 'first', 'second']

補充:strict mode

因為JS是很自由的語言,因此不嚴謹的寫法是被允許的,而在ES5中新增了strict mode,使用方法為在js檔案的最前面加入use strict,總的來說就是可以讓你的程式更嚴謹也更安全。在嚴格模式中以下情況是不被允許的:

  • 未宣告變數就使用

    x=3.14;
    //objects are variables too
    x={one:10,two:20}
  • 使用delete刪除變數或函式

    let x = 3.14;
    function y(a,b){};
    delete x;
    delete y;
  • 重複變數

    function y(a,b){};
    function y(a,b){};
  • 使用8進位值、字

    let x = 0101;
    let x = "\010";
  • Writing to a read-only (get-only)property is not allowed:

    const obj = {};
    Object.defineProperty(obj, "x", {value:0, writable:false});

    obj.x = 3.14; // This will cause an error
    const obj = {};
    Object.defineProperty(obj, "x", {value:0, writable:false});

    obj.x = 3.14; // This will cause an erorr
  • 使用with

  • eval、arguments不可作為變數名稱

  • 為ES6留的保留字

  • this不可指向全域

reference:

JavaScript "use strict" (w3schools.com)

[JavaScript] Javascript 的嚴格模式 (Strict Mode):不讓你錯 - itsems_frontend - Medium