深入淺出JavaScript之this
JavaScript中的this比較靈活,根據在不同環境下,或者同一個函式在不同方式呼叫下,this都有可能是不同的。但是有一個總的原則,那就是this指的是,呼叫函式的那個物件。
系列目錄
- 深入淺出JavaScript之閉包(Closure)
- 深入淺出JavaScript之this
- 深入淺出JavaScript之原型鏈和繼承
下面是我的學習筆記,把它羅列成8種情況。
全域性的this(瀏覽器)
全域性作用域的this一般指向全域性物件,在瀏覽器中這物件就是window,在node中這物件就是global。
console.log(this.document === document); // true (document === window.document) console.log(this === window); // true this.a = 37; //相當於建立了一個全域性變數a console.log(window.a); // 37
一般函式的this(瀏覽器)
一般的函式宣告或者函式表示式,直接呼叫函式的話,this依然指向全域性物件,在瀏覽器中這物件就是window,在node中這物件就是global。
function f1(){ return this; } f1() === window; // true, global object
再舉一個例子,看完就非常透徹了
function test(){ this.x = 1; alert(this.x); } test(); // 1
為了證明this就是全域性物件,對程式碼做一些改變:
var x = 1; function test(){ alert(this.x); } test(); // 1
執行結果還是1。再變一下:
var x = 1; function test(){ this.x = 0; } test(); alert(x); //0
但是在嚴格模式下,一般函式呼叫的時候this指向undefined,這也是node為什麼要用嚴格模式的一個原因。
function f2(){ "use strict"; // see strict mode return this; } f2() === undefined; // true
作為物件方法的函式的this
this作為物件方法來使用是比較常見的。
下面這個例子,我們建立了一個物件字面量o,o裡面有個屬性f,它的值是一個函式物件,把函式作為物件屬性的值這種方式我們常常叫作物件的方法。作為物件的方法呼叫的時候,這時候this指向物件o
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
我們不一定要定義成函式字面量這樣子的物件,像下面這種情況,我們只定義了一個物件o,如果直接呼叫independent()函式的話,this會指向window,但是我們通過賦值的方式,臨時建立一個屬性f,並指向函式物件的時候,我們仍然拿到了37。
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // logs 37
所以並不是看函式是怎麼建立的,而是隻要將函式作為物件的方法去呼叫,this就會指向這個物件。
物件原型鏈上的this
下面這個例子中:我們先建立了一個物件o,裡面有一個屬性f,函式作為物件屬性的值,我們通過Object.create(o)建立了一個物件p,p是一個空物件,它的原型會指向o,然後使用p.a = 1; p.b = 4建立物件p上的屬性,那麼我們呼叫原型上的方法時,this.a,this.b依然能取到物件p上的a和b。這裡需要注意的是p的原型才是o,我們呼叫p.f(),呼叫的是原型鏈o上的屬性f,原型鏈上的this可以拿到當前的物件p。
var o = {f:function(){ return this.a + this.b; }}; var p = Object.create(o); p.a = 1; p.b = 4; console.log(p.f()); // 5
get/set方法與this
get/set方法中的this一般會指向get/set方法所在物件裡面
function modulus(){ return Math.sqrt(this.re * this.re + this.im * this.im); } var o = { re: 1, im: -1, get phase(){ return Math.atan2(this.im, this.re); } }; Object.defineProperty(o, 'modulus', { //臨時動態給o物件建立modules屬性 get: modulus, enumerable:true, configurable:true}); console.log(o.phase, o.modulus); // logs -0.78 1.4142
建構函式中的this
用new把MyClass作為建構函式呼叫的話,this會指向空的物件,並且這個物件的原型會指向MyClass.prototype(可以看這篇文章對原型鏈的總結),但是呼叫的時候做了this.a = 37的賦值,所以最後this會作為返回值(沒寫return語句,或者return的是基本型別的話,會將this作為返回值),第二個例子return語句返回了物件,那麼就會將a = 38作為返回值
function MyClass(){ this.a = 37; } var o = new MyClass(); console.log(o.a); // 37 function C2(){ this.a = 37; return {a : 38}; } o = new C2(); console.log(o.a); // 38
call/apply方法與this
除了不同的呼叫方式外,函式物件有些方法能修改函式執行的this,比如call/apply。
call和apply基本上沒差別,只不過call傳參的方式是扁平的,而apply是把一個陣列傳進去。如下面這個例子
什麼時候用call和apply呢?比如我們想呼叫Object.prototype.toString,但是我們想指定某個this的時候,那我們就可以就用Object.prototype.toString.call(this)這樣子的方式來呼叫些無法直接呼叫的方法。如下面這個例子:
function add(c, d){ return this.a + this.b + c + d; } var o = {a:1, b:3}; add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 //第一個引數接收的是你想作為this的物件 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7); // "[object Number]"
bind方法與this
bind方法是es5開始提供的,所以ie9+才支援
function f(){ return this.a; } var g = f.bind({a : "test"}); //想把某個物件作為this的時候,就把它傳進去,得到一個新物件g console.log(g()); // test //重複呼叫的時候,this已經指向bind引數。這對於我們繫結一次需要重複呼叫依然實現繫結的話,會比apply和call更加高效(看下面這個例子) var o = {a : 37, f : f, g : g}; console.log(o.f(), o.g()); // 37, test //o.f()通過物件的屬性呼叫,this指向物件o;比較特殊的是即使我們把新繫結的方法作為物件的屬性呼叫,o.g()依然會按之前的繫結去走,所以答案是test不是g
總結
做專案的時候才發現這些基礎概念有多麼的重要,如果不把它們逐個落實了,真的是一不小心就會掉進坑裡。後續我還會對原型鏈,作用域,繼承,鏈式呼叫,正則等知識進行總結,歡迎關注
相關文章
- 深入淺出JavaScript之閉包(Closure)JavaScript
- 深入淺出JavaScript之原型鏈&繼承JavaScript原型繼承
- 深入淺出JavaScript之原型鏈和繼承JavaScript原型繼承
- 深入淺出Javascript閉包JavaScript
- 深入淺出 JavaScript 中的 thisJavaScript
- 深入淺出 FlatBuffers 之 Schema
- 深入淺出JavaScript執行機制JavaScript
- [譯] 深入淺出 JavaScript 關鍵詞 -- thisJavaScript
- 深入淺出 Flutter Framework 之 BuildOwnerFlutterFrameworkUI
- 深入淺出 Flutter Framework 之 ElementFlutterFramework
- 深入淺出之切空間
- PostgreSQL VACUUM 之深入淺出 (一)SQL
- PostgreSQL VACUUM 之深入淺出 (二)SQL
- PostgreSQL VACUUM 之深入淺出 (三)SQL
- 深入淺出之JVM GC篇JVMGC
- 深入淺出HOOKS(之伍) (轉)Hook
- 深入淺出HOOKS(之陸) (轉)Hook
- 深入淺出FE(十四)深入淺出websocketWeb
- 深入JavaScript基礎之深淺拷貝JavaScript
- 深入理解JavaScript之深淺複製JavaScript
- 深入淺出 Flutter Framework 之 WidgetFlutterFramework
- 深入淺出AQS之共享鎖模式AQS模式
- 深入淺出之正規表示式
- 深入淺出 妙用Javascript中apply、call、bindJavaScriptAPP
- 深入淺出妙用 Javascript 中 apply、call、bindJavaScriptAPP
- 深入淺出AQS之條件佇列AQS佇列
- 深入淺出AQS之獨佔鎖模式AQS模式
- 深入淺出理解之 onInterceptTouchEvent與onTouchEvent
- 深入淺出之正規表示式(二)
- 深入淺出之正規表示式(一)
- 深入淺出——MVCMVC
- 深入淺出mongooseGo
- HTTP深入淺出HTTP
- 深入淺出IO
- 深入淺出 RabbitMQMQ
- 深入淺出PromisePromise
- ArrayList 深入淺出
- mysqldump 深入淺出MySql