1. 程式人生 > >this的用法(二)-箭頭函式+面試題

this的用法(二)-箭頭函式+面試題

箭頭函式與普通函式的this

嚴格模式下的普通函式this為undenfied,非嚴格模式是window;箭頭函式的this是定義時所在的this

箭頭函式this指向注意事項

箭頭函式體內的this物件,如果包裹在函式中就是函式呼叫時所在的物件,如果放在全域性中就是指全域性物件window。並且固定不會更改。換句話說內部的this就是外層程式碼塊的this

下面是對比分析普通函式和箭頭函式中this區別

// 普通函式
function foo() {
  setTimeout(function() {
    console.log('id:', this.id);
  });
}

var id = 21;
foo.call({ id: 42 }); //21
//注意定時器,此時this指向window
// 箭頭函式
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;
foo.call({ id: 42 }); //42
// 上面的匿名函式定義時所在的執行環境就是foo函式,所以匿名
//函式內部的this執向始終會和foo函式的this執向保持一致,不會更改,如同下面的這個案例
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

var id = 21;
foo(); //21(沒有用call)

如果不使用 ES6,那麼這種方式應該是最簡單的不會出錯的方式了,我們是==先將呼叫這個函式的物件儲存在變數 _this== 中,然後在函式中都使用這個 _this,這樣 _this 就不會改變了。

var name = "windowsName";
var a = {
 name : "Cherry",
 func1: function () {
  console.log(this.name)  
 },
 func2: function () {
  var _this = this;
  setTimeout( function() {
   _this.func1()
  },100);
 }
};
a.func2()  // Cherry

這個例子中,在 func2 中,首先設定 var _this = this;,這裡的 this 是呼叫 func2 的物件 a,為了防止在 func2 中的 setTimeout 被 window 呼叫而導致的在 setTimeout 中的 this 為 window。我們將 this(指向變數 a) 賦值給一個變數 _this,這樣,在 func2 中我們使用 _this 就是指向物件 a 了。

call的作用就是將foo函式的執行環境從window改成物件{id: 42}

==定時器==的作用就是延遲執行當前函式的外部執行環境,無論有沒有設定延遲時間

普通函式解釋:定義時this指向函式foo作用域,==但是在定時器100毫秒之後執行函式時,此時this指向window物件==

箭頭函式解釋:this始終指向定義時所在物件,也就是始終指向foo作用域

進一步分析this

var handler = {
  id: '123456',

  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false);
  },

  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id);
  }
};
handler.init()// Handlingclickfor123456

箭頭函式的this始終指向handler,如果是普通函式,this指向document

this指向的固定化,並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為它沒有this,所以也就不能用作建構函式。

面試題

下面的面試題一、二、四都設計到引用問題,如果不是很好理解還可以理解成在 es5 中,永遠是this 永遠指向最後呼叫它的那個物件。

面試題一

this.x = 9;    // this refers to global "window" object here in the browser
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();   
// returns 9 - The function gets invoked at the global scope

// Create a new function with 'this' bound to module
// New programmers might confuse the
// global var x with module's property x
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

retrieveX只是getX函式的引用,也就是隻是getX的一個指標(getX的另一個指標是module.getX),所以retrieveX還是指向getX函式本身的

和上面類似的案例,下面的func只是函式引用,所以即使在函式內部,還是執行的函式本身,不受詞法作用域限制(箭頭函式則受限制)

document.getElementById( 'div1' ).onclick = function(){
    console.log( this.id );// 輸出: div1
    var func = function(){ 
        console.log ( this.id );// 輸出: undefined
    } 
    func();
}; 
//修正後
document.getElementById( 'div1' ).onclick = function(){
    var func = function(){ 
        console.log ( this.id );// 輸出: div1
    } 
    func.call(this);
}; 
function foo() {
    console.log( this.a );
}

var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };

o.foo(); // 3
(p.foo = o.foo)(); // 2

面試題二

var A = function( name ){ 
    this.name = name;
};
var B = function(){ 
    A.apply(this,arguments);
};
B.prototype.getName = function(){ 
    return this.name;
};
var b=new B('sven');
console.log( b.getName() ); // 輸出:  'sven'

面試題三

確實,許多包中的函式,和許多在JavaScript語言以及宿主環境中的內建函式,都提供一個可選引數,通常稱為“環境(context)”,這種設計作為一種替代方案來確保你的回撥函式使用特定的this而不必非得使用bind(..)。

舉例來說:

function foo(el) {
    console.log( el, this.id );
}

var obj = {
    id: "awesome"
};

// 使用`obj`作為`this`來呼叫`foo(..)`
[1, 2, 3].forEach( foo, obj ); // 1 awesome  2 awesome  3 awesome

面試題四

明確繫結 的優先權要高於 隱含繫結

function foo() {
    console.log( this.a );
}

var obj1 = {
    a: 2,
    foo: foo
};

var obj2 = {
    a: 3,
    foo: foo
};

obj1.foo(); // 2
obj2.foo(); // 3

obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2

new繫結的優先順序高於隱含繫結(new和call/apply不能同時使用,所以new foo.call(obj1)是不允許的,也就是不能直接對比測試 new繫結 和 明確繫結)