JavaScript 關於this的幾道面試題及介紹

原文連接javascript

與其餘語言相比,函數的this關鍵字在JavaScript中的行爲略有不一樣。而且它在嚴格模式和非嚴格模式之間也有一些區別。html

在絕大多數狀況下,函數的調用方式決定了this的值。this不能在執行期間被賦值,在每次函數被調用時this的值也可能會不一樣。ES5引入了bind方法來設置函數的this值,而不用考慮函數如何被調用的。java

先來作幾道面試題:

第一道:

var name = "caibaojian.com"; 
var person = {
  name: "kang",
  pro: {
    name: "Michael",
    getName: function() {
      return this.name;
    }
  }
};
console.log(person.pro.getName()); // Michael
var pepole = person.pro.getName;
console.log(pepole()); // caibaojian.com

第二道:

var name = "caibaojian.com";
var person = {
  name: "kang",
  pro: {
    name: "Michael",
    getName: function() {
      console.log(this);
      return this.name;
    }
  }
};
console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael
var pepole = person.pro.getName;
console.log(pepole()); // Window, caibaojian.com

第三道:

'use strict';
var name = "caibaojian.com"; 
var person = {
  name: "kang",
  pro: {
    name: "Michael",
    getName: function() {
      console.log(this);
      return this.name;
    }
  }
};
console.log(person.pro.getName()); // Object { name: "...", getName: () }, Michael
var pepole = person.pro.getName;
console.log(pepole()); // undefined

第四道:

var name = "caibaojian.com",
    person = {
      name : "kang",
      getName : function(){
       return function(){
         return this.name;
       };
      }
    };

console.log(person.getName()()); // caibaojian.com

下面一塊兒看看關於this的用法:

1、全局上下文

在全局運行上下文中(在任何函數體外部),this指代全局對象,不管是否在嚴格模式下。在瀏覽器中,全局對象爲window對象。面試

console.log(this.document === document); // true
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37

2、函數上下文

在函數內部,this的值取決於函數是如何調用的。瀏覽器

一、直接調用

在非嚴格模式下,this的值不會在函數執行時被設置,此時的this的值會默認設置爲全局對象。app

function foo(){
  return this;
}

foo() === window; // true

在嚴格模式下,this將保持他進入執行環境時的值,因此下面的this將會默認爲undefined函數

function foo(){
  "use strict"; // 嚴格模式
  return this;
}

foo() === undefined; // true

在嚴格模式下,若是this未被執行的上下文環境定義,那麼它將會默認爲undefinedthis

二、對象方法中的this

當以對象裏的方法的方式調用函數時,它們的this是調用該函數的對象。prototype

下面的例子中,當obj.f()被調用時,函數內的this將綁定到obj對象。code

var obj = {
  prop: 37,
  foo: function() {
    return this.prop;
  }
};

console.log(obj.foo()); // 37

注意,在何處或者如何定義調用函數徹底不會影響到this的行爲

在上一個例子中,咱們在定義obj的時候爲其成員foo定義了一個匿名函數。可是,咱們也能夠首先定義函數而後再將其附屬到obj.foo。這樣作this的取值也和上面一致:

var obj = { prop: 37 };

function independent() {
  return this.prop;
}

obj.foo = independent;

console.log(obj.foo()); // 37

這說明this的值只與函數foo做爲obj的成員被調用有關係。

相似的,this的綁定只受最靠近的成員引用的影響。

在下面的這個例子中,咱們把一個方法g看成對象obj.b的函數調用。在此次執行期間,函數中的this將指向obj.b。事實上,這與對象自己的成員沒有多大關係,最靠近的引用纔是最重要的。

var obj = { prop: 37 };

function independent() {
  return this.prop;
}

obj.b = {
  g: independent,
  prop: 42
};

console.log(obj.b.g()); // 42

三、原型鏈中的this

相同的概念在定義在原型鏈中的方法也是一致的。若是該方法存在於一個對象的原型鏈上,那麼this指向的是調用這個方法的對象,表現得好像是這個方法就存在於這個對象上同樣。

var obj = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(obj);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

在這個例子中,對象p沒有屬於它本身的f屬性,它的f屬性繼承自它的原型。可是這對於最終在obj中找到f屬性的查找過程來講沒有關係;查找過程首先從p.f的引用開始,因此函數中的this指向p。也就是說,由於f是做爲p的方法調用的,因此它的this指向了p。這是JavaScript的原型繼承中的一個有趣的特性。

四、gettersetter中的this

相同的概念也適用時的函數做爲一個getter或者setter調用。做爲gettersetter函數都會綁定this到從設置屬性或獲得屬性的那個對象。

function modulus(){
  return Math.sqrt(this.re * this.re + this.im * this.im);
}

var obj = {
  re: 1,
  im: -1,
  get phase(){
    return Math.atan2(this.im, this.re);
  }
};

Object.defineProperty(obj, 'modulus', {
  get: modulus,
  enumerable: true,
  configurable: true
});

console.log(obj.phase, obj.modulus); // -0.785 1.414

五、構造函數中的this

當一個函數被做爲一個構造函數來使用(使用new關鍵字),它的this與即將被建立的新對象綁定。

注意:當構造器返回的默認值是一個this引用的對象時,能夠手動設置返回其餘的對象,若是返回值不是一個對象,返回this

function Fn(){
  this.a = 37;
}

var obj = new Fn();
console.log(obj.a); // 37

function Foo(){
  this.a = 37;
  return { a: 38 };
}

obj = new Foo();
console.log(obj.a); // 38

六、callapply

當一個函數的函數體中使用了this關鍵字時,經過全部函數都從Function對象的原型中繼承的call()方法和apply()方法調用時,它的值能夠綁定到一個指定的對象上。

function add(c, d){
  return console.log(this.a + this.b + c + d);
}

var obj = {
  a: 1,
  b: 3
};

add.call(obj, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(obj, [10, 20]); // 1 + 3 + 10 + 20 = 34

使用callapply函數的時候要注意,若是傳遞的this值不是一個對象,JavaScript將會嘗試使用內部 ToObject操做將其轉換爲對象。所以,若是傳遞的值是一個原始值好比7foo,那麼就會使用相關構造函數將它轉換爲對象,因此原始值7經過new Number(7)被轉換爲對象,而字符串foo使用new String('foo')轉化爲對象,例如:

function bar() {
  console.log(Object.prototype.toString.call(this));
}

bar.call(7); // [object Number]

七、bind()方法

ECMAScript 5引入了Function.prototype.bind。調用fn.bind(someObject)會建立一個與fn具備相同函數體和做用域的函數,可是在這個新函數中,this將永久地被綁定到了bind的第一個參數,不管這個函數是如何被調用的。

function fn(){
  return this.a;
}

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

var obj = {
  a: 37,
  foo: fn,
  go: g
};
console.log(obj.foo(), obj.go()); // 37, "azerty"

八、DOM事件處理函數中的this

當函數被用做事件處理函數時,它的this指向觸發事件的元素(一些瀏覽器在動態添加監聽器時不遵照這個約定,除非使用addEventListener)。

// 獲取文檔中的全部元素的列表
var elements = document.getElementsByTagName('*');

for(var i = 0; i < elements.length; i++){
  // 當元素被點擊時,就會變成藍色
  elements[i].addEventListener('click', function (e) {
    e.stopPropagation();
    console.log(this);
    console.log(e.currentTarget);
    console.log(e.target);
    // 上面3個值是同樣的
    this.style.backgroundColor = '#A5D9F3';
  });
}

九、內聯事件處理函數中的this

當代碼被內聯處理函數調用時,它的this指向監聽器所在的DOM元素:

<button onclick="alert(this.tagName.toLowerCase());">
  Show this
</button>

上面的alert會顯示button。注意只有外層代碼中的this是這樣設置的:

<button onclick="alert((function(){return this})());">
  Show inner this
</button>

在這種狀況下,沒有設置內部函數的this,因此它指向global/window對象(即非嚴格模式下調用的函數未設置this時指向的默認對象)。

(完)

相關文章
相關標籤/搜索