1. 程式人生 > >this關鍵字的理解與總結

this關鍵字的理解與總結

1.this關鍵字的含義

簡單說,this就是屬性或方法“當前”所在的物件。

this都有一個共同點:它總是返回一個物件。

舉例:

var A = {
  name: '張三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};
A.describe();
// '姓名:張三'
由於物件的屬性可以賦給另一個物件,所以屬性所在的當前物件是可變的,即this的指向是可變的。
var A = {
  name: '張三',
  describe: function () {
    return '姓名:'+ this
.name; } }; var B = { name: '李四' }; B.describe = A.describe; B.describe() // "姓名:李四"

重構如下

function f() {
  return '姓名:'+ this.name;
}

var A = {
  name: '張三',
  describe: f
};

var B = {
  name: '李四',
  describe: f
};

A.describe() // "姓名:張三"
B.describe() // "姓名:李四"

只要函式被賦給另一個變數,this的指向就會變。

var A = {
  name: '張三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

var name = '李四';
var f = A.describe;
f() // "姓名:李四"

A.describe被賦值給變數f,內部的this就會指向f執行時所在的物件(本例是頂層物件);

一切皆物件,執行環境也是物件,所以函式都是在某個物件之中執行,this就是函式執行時所在的物件(環境)。

2.this的實質

var obj = { foo:  5 };

原始的物件以字典結構儲存;

引擎先從obj拿到記憶體地址,然後再從該地址讀出原始的物件,返回它的foo屬性。

{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}
屬性值是一個函式的情況
var obj = { foo: function () {} };
{
  foo: {
    [[value]]: 函式的地址
    ...
  }
}

由於函式是一個單獨的值,所以它可以在不同的環境(上下文)執行。

它的設計目的就是在函式體內部,指代函式當前的執行環境。

3.使用場合

(1)全域性環境

全域性環境使用this,它指的就是頂層物件window

this === window // true

(2)建構函式

建構函式中的this,指的是例項物件。

var Obj = function (p) {
  this.p = p;
};
var o = new Obj('Hello World!');
o.p // "Hello World!"
 
 

(3)物件的方法

如果物件的方法裡面包含thisthis的指向就是方法執行時所在的物件。該方法賦值給另一個物件,就會改變this的指向。

如果this所在的方法不在物件的第一層,這時this只是指向當前一層的物件,而不會繼承更上面的層。

 
 
 
var a = {
  p: 'Hello',
  b: {
    m: function() {
      console.log(this.p);
    }
  }
};

a.b.m() // undefined
因此,實際執行是下面這個程式碼
var b = {
  m: function() {
   console.log(this.p);
  }
};

var a = {
  p: 'Hello',
  b: b
};

(a.b).m() // 等同於 b.m()
想要達到預期效果是
var a = {
  b: {
    m: function() {
      console.log(this.p);
    },
    p: 'Hello'
  }
};
 

如果這時將巢狀物件內部的方法賦值給一個變數,this依然會指向全域性物件。

var a = {
  b: {
    m: function() {
      console.log(this.p);
    },
    p: 'Hello'
  }
};

var hello = a.b.m;
hello() // undefined

4. 1)避免多層this

var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
}

o.f1()
// Object
// Window

實際執行如下

var temp = function () {
  console.log(this);
};

var o = {
  f1: function () {
    console.log(this);
    var f2 = temp();
  }
}

一個解決方法是在第二層改用一個指向外層this的變數。

that,固定指向外層的this,然後在內層使用that,就不會發生this指向的改變。

var o = {
  f1: function() {
    console.log(this);
    var that = this;
    var f2 = function() {
      console.log(that);
    }();
  }
}

o.f1()
// Object
// Object

4. 2)避免陣列處理方法中的this

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}

o.f()
// undefined a1
// undefined a2

解決方法

1.使用中間變數固定this;

2.另一種方法是將this當作foreach方法的第二個引數,固定它的執行環境。

 
 
 
var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}

o.f()
// hello a1
// hello a2
 

3.避免在回撥函式中使用this;

1.Function.prototype.call()

call方法的引數,應該是一個物件。如果引數為空、nullundefined,則預設傳入全域性物件。

2.Function.prototype.apply()

3.Function.prototype.bind()

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};

var func = counter.inc.bind(counter);
func();
counter.count // 1

如果bind方法的第一個引數是nullundefined,等於將this繫結到全域性物件,函式執行時this指向頂層物件(瀏覽器為window);

function add(x, y) {
  return x + y;
}

var plus5 = add.bind(null, 5);
plus5(10) // 15