摘要:普通函數調用函數在全局作用域下運行在非嚴格模式下,指向全局對象,在嚴格模式下,會變成。使用來調用函數,或者說發生構造函數調用時,會自動執行下面的操作。即構造函數的指向它實例化出來的對象。
JavaScript中的this實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數在哪里被調用。
先來列舉一下都有哪些函數調用方式:
普通函數調用
對象方法調用
call()、apply() 調用
bind()
構造函數調用
ES6箭頭函數
通俗來說,“函數被誰調用,this就指向誰”。
普通函數調用函數在全局作用域下運行;在非嚴格模式下,this指向全局對象window,在嚴格模式下,this會變成undefined。
function foo1(){ return this; } foo1() === window; // true function foo2(){ "use strict"; return this; } foo2() === undefined; // true對象方法調用
當以對象里的方法的方式調用函數時,它們的this是調用函數的對象。
function foo() { console.log(this); // {x: 1, foo: ?} console.log(this.x); // 1 } var obj = { x: 1, foo: foo }; obj.foo();
上面代碼中,調用foo的是對象obj ,所以這里的this指向對象obj。
如果將對象里的foo方法賦給一個變量:
function foo() { console.log(this); // window console.log(this.x); // undefined } var obj = { x: 1, foo: foo }; var fn = obj.foo; fn();
將obj.foo賦給了fn變量,而fn是在全局作用域中運行,因此this指向window,由于window下沒有x這個變量,所以this.x為undefined。
如果把obj.foo傳遞給一個自己聲明的函數。
function foo() { console.log(this); // window console.log(this.x); // undefined } var obj = { x: 1, foo: foo }; // 自定義函數 function todoFn(cb) { cb(); }; todoFn(obj.foo);
將obj.foo傳遞給自定義函數作為回調函數執行。結果一樣。因為它仍然是在全局作用域中運行。
最后來看看在setTimeout()中又會輸出怎樣的結果。
function foo() { console.log(this); // {x: 1, foo: ?} setTimeout(function(){ console.log(this); // window }, 0); } var obj = { x: 1, foo: foo } obj.foo();
第一個this還是對象方法調用方式;但在setTimeout()里的this指向的是全局作用域window。為什么會這樣?
原因是,由setTimeout()調用的代碼運行在與所在函數完全分離的執行環境上,該函數是在全局作用域中執行的,所以this就指向 window。
思考一個問題。上面代碼的后三段代碼中,都出現了同樣的問題,就是它們的this都丟失了。怎么做才能讓它們的this仍然指向原來的對象?
后面即將提到的call()、apply()及bind()方法都能解決這一問題。
call()、apply() 調用使用call()或者apply()可以改變上下文的this值。
// 定義一個對象 var obj = { x: "local" }; // 定義一個全局變量 var x = "global"; // 定義函數,返回值取決于調用方式 function speak(){ return this.x; } speak(); // "global" speak.call(obj); // "local" speak.apply(obj); // "local"
直接調用sayName()時,相當于普通函數調用,this指向window,值為global;使用call()或apply()作用一致,都讓this指向了對象obj。只不過在給call()和apply()傳遞參數時有區別,如:
function add(c, d) { return this.a + this.b + c + d; } var obj = { a: 1, b: 2 }; // 第一個參數作為 this 使用的對象,而后續參數作為參數傳遞給函數 add.call(obj, 3, 4); // 10 // 第一個參數同樣是作為 this 使用的對象 // 第二個參數是傳遞一個數組 add.apply(obj, [3, 4]); // 10bind() 方法
Function.prototype.bind()會創建一個新的包裝函數,這個函數會忽略它當前的this綁定(無論綁定的對象是什么), 并把我們提供的對象綁定到this上。
var x = 9; var obj = { x: 81, getX: function() { return this.x; } }; // 相當于對象方法調用,this指向對象 o obj.getX(); // 81 // 將對象 o 的方法getX賦值給 f,f 執行時是在全局環境中,this指向全局作用域 var f = obj.getX; f(); // 9 // 創建一個新函數,將this綁定到對象 o 中 var f2 = obj.getX.bind(o); f2(); // 81
接著上面【對象方法調用】里的幾個丟失this的例子。如何用call()
、apply()或bind()來綁定this到指定的對象。
先看看這個例子:
function foo() { console.log(this); // {x: 1, foo: ?} console.log(this.x); // 1 } var obj = { x: 1, foo: foo }; // 使用call()或apply() var fn1 = obj.foo; fn1.call(obj); //fn.apply(obj); // 使用bind() var fn2 = obj.foo.bind(obj); fn2();
再看這個回調函數的例子:
function foo() { console.log(this); // {x: 1, foo: ?} console.log(this.x); // 1 } var obj = { x: 1, foo: foo }; // 自定義函數 function todoFn(cb) { cb.call(obj); // cb.apply(obj); }; todoFn(obj.foo);
只需要在執行回調函數的時候使用call()或apply()將this綁定到obj對象即可。
最后看定時器的例子:
function foo() { console.log(this); // {x: 1, foo: ?} setTimeout(function(){ console.log(this); // {x: 1, foo: ?} }.bind(this), 0); } var obj = { x: 1, foo: foo } obj.foo();
這里是使用了bind()方法,將上下文的this綁定到定時器里的匿名函數,它最終還是指向對象obj。
構造函數調用如果函數或者方法調用之前帶有關鍵字new,它就構成構造函數調用。使用new來調用函數, 或者說發生構造函數調用時,會自動執行下面的操作。
創建(或者說構造) 一個全新的對象。
這個新對象會被執行 [[ 原型 ]] 連接。
這個新對象會綁定到函數調用的this。
如果函數沒有返回其他對象, 那么new表達式中的函數調用會自動返回這個新對象。
來看一個例子:
function foo(x){ this.x = x; console.log(this); // foo {x: 2} } var f = new foo(2); console.log(f.x); // 2
使用new來調用foo()時, 會構造一個新對象并把它綁定到foo()調用中的this上。即構造函數的this指向它實例化出來的對象。
箭頭函數箭頭函數本身沒有this,是根據外層(函數或者全局)作用域來決定this。
先來看一個例子:
var x = 1; function testFn() { var fn1 = function() { console.log(this); // window console.log(this.x); // 1 } var fn2 = () => { console.log(this); // window console.log(this.x); // 1 } fn1(); fn2(); } testFn();
兩段代碼輸出的結果一致。但運行的機制不一樣。
fn1是普通函數調用,前面說過普通函數調用this指向window;
fn2是箭頭函數,它會繼承外層函數testFn的this,而testFn是在全局作用域中運行的,所以它的this是window,因此fn2箭頭函數的this也是window。
對上面對例子做一點改動:
var x = 1; function testFn() { var fn1 = function() { console.log(this); // window console.log(this.x); // 1 } var fn2 = () => { console.log(this); // {x: 2} console.log(this.x); // 2 } fn1(); fn2(); } testFn.call({ x: 2 });
改變了testFn的調用方式。
fn1依然是普通函數調用,this指向window;
fn2箭頭函數依然是繼承它外層函數testFn的this,但這里testFn通過call使this指向了對象{id: 2},所以fn2的this也指向該對象。
接著上面【對象方法調用】里的setTimeout例子。如果使用箭頭函數會怎樣?
function foo() { console.log(this); // {x: 1, foo: ?} console.log(this.x); // 1 setTimeout(() => { console.log(this); // {x: 1, foo: ?} console.log(this.x); // 1 }, 0); } var obj = { x: 1, foo: foo } obj.foo();
這里定時器回調的this由它外層函數foo的作用域決定,而foo是由obj調用,所以它的this指向對象obj,所以定時器的this也指向對象obj。
另外,由于箭頭函數沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。
小結JavaScript的this取決于被調用的方式。
如果是普通函數調用方式。非嚴格模式下,this指向window,嚴格模式下,this是undefined;
如果是對象調用方式,this指向該對象;
如果是call()、apply()或bind()方式調用,this指向被綁定的對象;
如果是構造函數調用方式,this指向實例化出來的新對象;
如果是箭頭函數,是根據當前的詞法作用域來決定this, 具體來說,箭頭函數會繼承外層函數調用的this綁定。
原文地址:https://github.com/daijingfeng/blog/issues/2
參考資料:
1、https://developer.mozilla.org/zh-CN/docs/Web/API/Window/setTimeout
2、https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
3、http://es6.ruanyifeng.com/#docs/function#箭頭函數
4、《JavaScript權威指南(第6版)(中文版)》
5、《你不知道的JavaScript(上卷)》
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92049.html
摘要:全局環境在全局環境中使用,它會指向全局對象。作為構造函數調用中的構造函數很特殊,如果不使用調用,則和普通函數一樣。作為又一項約定俗成的準則,構造函數以大寫字母開頭,提醒調用者使用正確的方式調用。 在JavaScript中,this關鍵字是動態綁定的,或稱為運行期綁定,這極大地增強的我們程序的靈活性,同時也給初學者帶來了很多困惑。本文總結了this的幾個使用場景和常見誤區。 全局環境 在...
摘要:總結本博文通過介紹執行上下文和作用域的異同的使用以及變量對象,讓我們加深對語言特性的理解。首先,我們介紹了執行上下文和的的關系,并且執行上下文是具有對象的然后,介紹了作用域使變量在作用域范圍內可見,并且作用域是基于函數的。 接上一篇Javascript Context和Scope的學習總結01【轉自cnblogs的JKhuang】(可能是segmentfault對單篇文章發布字數有限制...
摘要:例如通過,調用時強制把它的綁定到上。箭頭函數問題箭頭函數體內的對象就是定義時所在的對象,而不是使用時所在的對象,固定不變。 剛入門javascript,關于this的學習,花了自己挺多的時間,做了比較多的功課,看了一篇又一篇的文章,也看了一些書籍,今天就結合看的那些東西總結下自己所學到的東西,方便留著以后回看,進一步的學習,這篇文章會不斷的更新,不斷的更新自己的想法,現在還是一個入門不...
摘要:文章目的是總結下這個事情,如果沒有考慮周全,遺漏某一條,請過路的留言寫,我在補上。類的實例在函數中的一般指向和中括號里面的第一個參數是誰就指向誰。輸出說明指向的是輸出,說明指向的是箭頭函數中的,就是定義是所在的對象,而不是使用時所在的對象 文章目的是總結下這個事情,如果沒有考慮周全,遺漏某一條,請過路的留言寫,我在補上。 在自制行函數中,this --> window let ...
摘要:發生這種情況的條件是當引用類型值的對象恰好為活躍對象。總結本文介紹中的使用,更重要的是幫助我們能更好地理解值在全局函數構造函數以及一些特例的情況中值的變化。然而,由于對于來說沒有任何意義,因此會隱式轉換為全局對象。 接上一篇Javascript this 的一些學習總結02【轉自cnblogs的JKhuang】 引用類型以及this的null值 對于前面提及的情形,還有例外的情況,當調...
摘要:其中和在傳入要綁定的指向時,立即執行。需要注意的是,當使用改變函數內部的指向時如果傳入的不是一個對象,會調用相對的構造函數,進行隱式類型裝換。 關于javascript中this指向的總結 瀏覽器環境 在全局作用域中,this的指向是window對象 console.log(this); //Window對象 console.log(this === window); //true E...
閱讀 1438·2023-04-25 16:31
閱讀 2040·2021-11-24 10:33
閱讀 2745·2021-09-23 11:33
閱讀 2528·2021-09-23 11:31
閱讀 2900·2021-09-08 09:45
閱讀 2336·2021-09-06 15:02
閱讀 2647·2019-08-30 14:21
閱讀 2313·2019-08-30 12:56