var obj = { foo: function(){} } var foo = obj.foo; // 寫法一 obj.foo(); // 寫法二 foo();
雖然obj.foo和foo指向同一個函數,可是執行結果可能不同。javascript
var obj = { foo: function() { conosle.log(this.bar) }, bar: 2 }; var foo = obj.foo; var bar = 3; obj.foo(); // 2 foo(); // 3
這種差別的緣由就是由於內部使用了this
關鍵字,this
指向的是函數運行的所在環境,對於obj.foo()
來講,this
執行obj
,對於foo()
來講,this
指向window
全局環境html
JavaScript 語言之因此有this的設計,跟內存裏面的數據結構有關係。java
var obj = {foo: 5}
也就是或變量obj是一個地址,後面讀取obj.foo引擎先從obj拿到地址,而後再從該地址讀取原始對象,返回它的屬性值。
原始的對象以字典結構保存,每個屬性名都對應一個屬性描述對象。舉例來講,上面例子的foo屬性,其實是如下面的形式保存的。面試
這樣的結構是很清晰的,問題在於屬性的值多是一個函數。json
var obj = { foo: function () {} };
這時,引擎會將函數單獨保存在內存中,而後再將函數的地址賦值給foo屬性的value屬性。數組
因爲函數是一個單獨的值,因此它能夠在不一樣的環境(上下文)執行。數據結構
var f = function () {}; var obj = { f: f }; // 單獨執行 f() // obj 環境執行 obj.f()
var f = function () { console.log(x); };
上面代碼中,函數體裏面使用了變量x。該變量由運行環境提供。app
如今問題就來了,因爲函數能夠在不一樣的運行環境執行,因此須要有一種機制,可以在函數體內部得到當前的運行環境(context)。因此,this就出現了,它的設計目的就是在函數體內部,指代函數當前的運行環境。函數
var f = function () { console.log(this.x); } var x = 1; var obj = { f: f, x: 2, }; // 單獨執行 f() // 1 // obj 環境執行 obj.f() // 2
在obj環境執行,this.x指向obj.x。
函數f在全局環境執行,this.x指向全局環境的x。post
回到咱們最初的問題 obj.foo()
是經過obj找到foo,因此就是在obj
環境執行。一旦var foo = obj.foo
,變量foo
就直接指向函數自己,因此foo()
就變成在全局環境執行。
this在js中一直是謎同樣的存在着,在面試中也是常常會被問道
this的指向在函數建立的時候是決定不了的,在調用的時候才能決定
this; //在全局範圍內使用`this`,它將會指向全局對象 var name="zhoulujun"; function say(){ console.log(this.name) } say(); //zhoulujun
當執行 say函數的時候, JavaScript 會建立一個 Execute context (執行上下文),執行上下文中就包含了 say函數運行期所須要的全部信息。 Execute context 也有本身的 Scope chain, 當函數運行時, JavaScript 引擎會首先從用 say函數的做用域鏈來初始化執行上下文的做用域鏈。
foo(); //this指向全局對象
test.foo(); //this指向test對象
new foo(); //函數與new一塊使用即構造函數,this指向新建立的對象
function foo(a, b, c) {} var bar = {}; foo.apply(bar, [1, 2, 3]); //this被設置成bar foo.call(bar, 1, 2, 3); //this被設置成bar
實例1
myObj3={ site:"zhoulujun.cn", andy:{ site:"www.zhoulujun.cn", fn:function(){ console.log(this) console.log(this.site) } } }; var site="111"; var fn=myObj3.andy.fn; fn(); // 這裏的調用環境是window // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …} // 111
實例2
myObj3={ site:"zhoulujun.cn", andy:{ site:"www.zhoulujun.cn", fn:function(){ console.log(this) console.log(this.site) } } }; var site="111"; myObj3.andy.fn(); VM51:6 {site: "www.zhoulujun.cn", fn: ƒ} VM51:7 www.zhoulujun.cn
實例3
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); };
實例4
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'
實例5
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
由於apply、call存在於Function.prototype中,因此每一個方法都有這兩個屬性。
call
函數名.call(對象,arg1....argn) //功能: //1.調用函數 //2.將函數內部的this指向第一個參數的對象 //3.將第二個及之後全部的參數,做爲實參傳遞給函數
apply主要用途是直接用數組傳參
函數名.apply(對象, 數組/僞數組); //功能: //1.調用函數 //2.將函數內部的this指向第一個參數的對象 //3.將第二個參數中的數組(僞數組)中的元素,拆解開依次的傳遞給函數做爲實參 //案例求數組中最大值 var a=Math.max.apply( null, [ 1, 2, 5, 3, 4 ] ); console.log(a);// 輸出:5
call應用(將僞數組轉爲數組)
var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 } Array.prototype.join.call(arrayLike, '&'); // name&age&sex Array.prototype.slice.call(arrayLike, 0); // ["name", "age", "sex"] // slice能夠作到類數組轉數組 Array.prototype.map.call(arrayLike, function(item){ return item.toUpperCase(); }); // ["NAME", "AGE", "SEX"] console.log( Object.prototype.toString.call(num), Object.prototype.toString.call(str), Object.prototype.toString.call(bool), Object.prototype.toString.call(arr), Object.prototype.toString.call(json), Object.prototype.toString.call(func), Object.prototype.toString.call(und), Object.prototype.toString.call(nul), Object.prototype.toString.call(date), Object.prototype.toString.call(reg), Object.prototype.toString.call(error) ); // '[object Number]' '[object String]' '[object Boolean]' '[object Array]' '[object Object]' // '[object Function]' '[object Undefined]' '[object Null]' '[object Date]' '[object RegExp]' '[object Error]'