this是執行環境對象的屬性,指向執行環境運行時所在的對象;
this與可執行代碼的類型直接相關,其值在進入執行環境階段肯定,並且在執行過程當中保持不變;javascript
在全局執行環境中,this老是執行全局對象自己;前端
//顯式的定義全局對象的屬性 this.a=10; console.log(a); //隱式的定義全局對象的屬性 b=20; console.log(b); //經過變量聲明間接的定義全局對象的屬性 var c=30; console.log(this.c);//30
在函數執行環境中,this值不是靜態綁定到函數的;java
this值是在進入執行環境時肯定的,指向調用函數時的執行環境,對於同一函數經過不一樣方式調用其this指向不一樣的對象;數組
this值肯定以後,在代碼執行階段其值是不變的,this值不是變量因此不能爲其分配新值;app
var foo = { x:10 }; var bar={ x:20, test: function(){ console.log( this ); // bar, { x: 20, test: [Function] } console.log( this.x ); // 20 } }; bar.test(); foo.test=bar.test; foo.test(); //this指向foo, { x: 10, test: [Function] }
this是由激活函數執行環境的caller提供, 好比調用函數的父級執行環境,但決定this值的唯一因素是調用表達式的形式,即調用函數的語法形式 ;函數
通常會說this值取決於函數的定義方式,全局環境中的函數this值指向全局對象,做爲對象的方法的函數this指向該對象,這種說法是錯誤的;this
var variable = 22; function foo(){ var variable = 11; console.log(this.variable); } console.log( "here" ); foo(); //22 console.log( foo === foo.prototype.constructor );// true foo.prototype.constructor(); // undefined
var foo={ bar:function(){ console.log(this===foo); } }; foo.bar(); // true var test=foo.bar; console.log(test === foo.bar); // true test(); // false
引用類型的值能夠表示爲包含兩個屬性的對象:prototype
var valueOfReferenceType={ base:<base object>, propertyName: <property name> };
引用類型的值只適用於標識符解析和屬性訪問兩種狀況:code
標識符解析對象
標識符解析返回的值是一個引用類型的值;
標識符包括變量名、函數名、函數的形式參數名和全局對象中的unqualified的屬性名;
var foo=10; function bar(){};
以上代碼對應的引用類型以下所示:
// 變量 foo var fooReference={ base:global, propertyName:"foo" } // 函數 bar var barReference={ base:global, propertyName:"bar" }
爲了從引用類型的值獲得對真正的值,在內部使用GetValue()
方法,用僞代碼表示以下:
內部方法[[Get]]
用於從對象獲取指定屬性,包括從原型鏈繼承而來的屬性;
function GetValue(value){ //非引用類型的值直接返回 if( Type(value) != Reference){ return value; } //獲取值所屬的對象 var base=GetBase( value ); if( base===null ){ throw new ReferenceError; } //返回從值所屬的對象獲取的屬性值,經過 [[Get]] 方法 var propertyName = GetPropertyName( value ); return base.[[Get]]( propertyName ); }
獲取fooReference
和barReference
的值:
GetValue(fooReference); // 10 GetValue(barReference); // 函數對象 bar
屬性訪問
屬性訪問分爲點訪問法和方括號訪問法;
點訪問法中的屬性必須是標識符;
foo.bar(); foo["bar"]();
函數執行環境中的this由激活函數執行環境的caller提供,但決定this值的唯一因素是調用表達式的形式,即調用函數的語法形式;
若是表示函數調用的圓括號的左側是一個引用類型的值,則函數中的this指向該引用類型值的base對象;
標識符:
function foo(){ return this; } foo(); // window // 標識符foo的引用類型表示 var fooReference={ base: global, propertyName: "foo" } foo.prototype.constructor(); // foo.prototype // foo.prototype.constructor是屬性 var fooPrototypeConstructorReference = { base:foo.prototype, propertyName:"constructor" }
屬性訪問:
var foo={ bar:function(){ return this; } }; foo.bar(); // foo //屬性foo.bar的引用類型表示 var fooBarReference={ base: foo, propertyName: "bar" }; //經過不一樣形式的調用表達式調用同一函數: var test = foo.bar; test(); // window // test是標識符 var testReference={ base:global, propertyName:"test" }
當表示函數調用的圓括號的左側不是引用類型的值時,this老是設置爲null;
由於將this設置爲null值沒有意義,因此隱式的將其轉換爲全局對象;
在ES5的嚴格模式下this值再也不強迫轉換爲全局對象,而是設置爲undefined;
(function(){ console.log(this); // window })();
var foo={ bar: function(){ console.log(this); } }; foo.bar(); // foo (foo.bar)(); //foo //第一個圓括號是一個組運算符,從引用類型獲取值的GetValue()方法不適用與該運算符(why),其返回值仍然是引用類型 (foo.bar=foo.bar)(); //賦值運算符, 使用GetValue()方法進行求值,返回值是一個函數對象,因此null (false || foo.bar)(); // 返回foo.bar表示的值,函數對象,因此null (foo.bar,foo.bar)(); // 返回函數對象,因此null
當引用類型值的base對象是活動對象時,this值將指向null,並轉換爲全局對象;
function foo(){ function bar(){ console.log(this); // gloabl } bar(); //等價於AO.bar(), 但AO做爲base對象返回null }
經過with調用函數時,with對象被添加到做用域鏈最前端,屏蔽外層函數的活動對象或者全局對象,函數中的this老是指向with對象;
var x=10; with({ foo:function(){ console.log(this.x) }, x:20 }){ foo(); // 20 }
foo的引用類型:
var fooReference={ base:__withObject, propertyName:"foo" };
ES3中調用catch語句傳入的參數函數時,函數中的this指向catch對象,而不是全局對象或活動對象;
這被認爲是一個bug, 在ES5中獲得修正,this將指向全局對象;
try{ throw function(){ console.log(this); }; }catch(e){ e(); } var eReference={ base:global, propertyName:"e" };
在遞歸的調用命名函數表達式時,第一次調用中base對象是外層函數的活動對象或者全局對象,在以後的遞歸調用中base對象應該是存儲函數表達式名的特殊對象,但實際上this值老是指向全局對象;
(function foo(bar){ console.log(this); !bar && foo(1); })() //window window
在構造函數內this老是指向新建立的對象;
new操做符調用構造函數的內部方法[[construct]]
建立新對象,對象建立以後在構造函數上調用[[call]]
方法,this指定爲新建的對象,初始化新對象;
function Foo(){ console.log(this); this.x=10; } var foo=new Foo(); console.log(foo.x);
在Function.prototype
上定義了apply()
和call()
方法用於手動的指定函數調用中的this;
call()
和apply()
的第一個參數是在函數執行環境中使用的this值, 其餘參數傳入調用的函數;
call接受任意數量的參數,apply數組做爲參數;
var variable = 1; function act(arg) { console.log(this.variable); console.log(arg); } act(2); // 1, 2 act.call({variable : 3}, 4); // 3, 4 act.apply({variable : 5}, [6]); // 5, 6