嚴格模式下的普通函數this爲undenfied,非嚴格模式是window;箭頭函數的this是定義時所在的this面試
箭頭函數體內的this對象,若是包裹在函數中就是函數調用時所在的對象,若是放在全局中就是指全局對象window。而且固定不會更改。換句話說內部的this就是外層代碼塊的thiswindows
// 普通函數 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 就不會改變了。app
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
普通函數解釋:定義時this指向函數foo做用域,==可是在定時器100毫秒以後執行函數時,此時this指向window對象==es5
箭頭函數解釋:this始終指向定義時所在對象,也就是始終指向foo做用域prototype
進一步分析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,因此也就不能用做構造函數。
下面的面試題1、2、四都設計到引用問題,若是不是很好理解還能夠理解成在 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綁定 和 明確綁定)