日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
JavaScript中this的工作原理以及注意事項(xiàng)

在JavaScript中,this 的概念比較復(fù)雜。除了在面向?qū)ο缶幊讨校?em>this 還是隨處可用的。這篇文章介紹了this 的工作原理,它會造成什么樣的問題以及this 的相關(guān)例子。 要根據(jù)this 所在的位置來理解它,情況大概可以分為3種:

  1. 在函數(shù)中:this 通常是一個(gè)隱含的參數(shù)。

  2. 在函數(shù)外(***作用域中):在瀏覽器中this 指的是全局對象;在Node.js中指的是模塊(module)的導(dǎo)出(exports)。

  3. 傳遞到eval()中的字符串:如果eval()是被直接調(diào)用的,this 指的是當(dāng)前對象;如果eval()是被間接調(diào)用的,this 就是指全局對象。

對這幾個(gè)分類,我們做了相應(yīng)的測試:

  1. 在函數(shù)中的this

    函數(shù)基本可以代表JS中所有可被調(diào)用的結(jié)構(gòu),所以這是也最常見的使用this 的場景,而函數(shù)又能被子分為下列三種角色:

    1.1  在實(shí)函數(shù)中的this

    在實(shí)函數(shù)中,this 的值是取決于它所處的上下文的模式。

    • Sloppy模式:this 指的是全局對象(在瀏覽器中就是window)。

 
 
  1. function sloppyFunc() {
  2.     console.log(this === window); // true
  3. }
  4. sloppyFunc();

Strict模式:this 的值是undefined。

 
 
  1. function strictFunc() {
  2.     'use strict';
  3.     console.log(this === undefined); // true
  4. }
  5. strictFunc();

this 是函數(shù)的隱含參數(shù),所以它的值總是相同的。不過你是可以通過使用call()或者apply()的方法顯示地定義好this的值的。

 
 
  1. function func(arg1, arg2) {
  2.     console.log(this); // 1
  3.     console.log(arg1); // 2
  4.     console.log(arg2); // 3
  5. }
  6. func.call(1, 2, 3); // (this, arg1, arg2)
  7. func.apply(1, [2, 3]); // (this, arrayWithArgs)

1.2 構(gòu)造器中的this

你可以通過new 將一個(gè)函數(shù)當(dāng)做一個(gè)構(gòu)造器來使用。new 操作創(chuàng)建了一個(gè)新的對象,并將這個(gè)對象通過this 傳入構(gòu)造器中。

 
 
  1. var savedThis;
  2. function Constr() {
  3.     savedThis = this;
  4. }
  5. var inst = new Constr();
  6. console.log(savedThis === inst); // true

JS中new 操作的實(shí)現(xiàn)原理大概如下面的代碼所示(更準(zhǔn)確的實(shí)現(xiàn)請看這里,這個(gè)實(shí)現(xiàn)也比較復(fù)雜一些):

 
 
  1. function newOperator(Constr, arrayWithArgs) {
  2.     var thisValue = Object.create(Constr.prototype);
  3.     Constr.apply(thisValue, arrayWithArgs);
  4.     return thisValue;
  5. }

1.3 方法中的this

在方法中this 的用法更傾向于傳統(tǒng)的面向?qū)ο笳Z言:this 指向的接收方,也就是包含有這個(gè)方法的對象。

 
 
  1. var obj = {
  2.     method: function () {
  3.         console.log(this === obj); // true
  4.     }
  5. }
  6. obj.method();

實(shí)函數(shù)

  • 構(gòu)造器

方法

2. 作用域中的this

在瀏覽器中,作用域就是全局作用域,this 指的就是這個(gè)全局對象(就像window):

 
 

在Node.js中,你通常都是在module中執(zhí)行函數(shù)的。因此,***作用域是個(gè)很特別的模塊作用域(module scope):

 
 
  1. // `global` (not `window`) refers to global object:
  2. console.log(Math === global.Math); // true
  3. // `this` doesn’t refer to the global object:
  4. console.log(this !== global); // true
  5. // `this` refers to a module’s exports:
  6. console.log(this === module.exports); // true

3. eval()中的this

eval()可以被直接(通過調(diào)用這個(gè)函數(shù)名’eval’)或者間接(通過別的方式調(diào)用,比如call())地調(diào)用。要了解更多細(xì)節(jié),請看這里。

 
 
  1. // Real functions
  2. function sloppyFunc() {
  3.     console.log(eval('this') === window); // true
  4. }
  5. sloppyFunc();
  6. function strictFunc() {
  7.     'use strict';
  8.     console.log(eval('this') === undefined); // true
  9. }
  10. strictFunc();
  11. // Constructors
  12. var savedThis;
  13. function Constr() {
  14.     savedThis = eval('this');
  15. }
  16. var inst = new Constr();
  17. console.log(savedThis === inst); // true
  18. // Methods
  19. var obj = {
  20.     method: function () {
  21.         console.log(eval('this') === obj); // true
  22.     }
  23. }
  24. obj.method();

4.與this有關(guān)的陷阱

你要小心下面將介紹的3個(gè)和this 有關(guān)的陷阱。要注意,在下面的例子中,使用Strict模式(strict mode)都能提高代碼的安全性。由于在實(shí)函數(shù)中,this 的值是undefined,當(dāng)出現(xiàn)問題的時(shí)候,你會得到警告。

4.1  忘記使用new

如果你不是使用new來調(diào)用構(gòu)造器,那其實(shí)你就是在使用一個(gè)實(shí)函數(shù)。因此this就不會是你預(yù)期的值。在Sloppy模式中,this 指向的就是window 而你將會創(chuàng)建全局變量:

 
 
  1. function Point(x, y) {
  2.     this.x = x;
  3.     this.y = y;
  4. }
  5. var p = Point(7, 5); // we forgot new!
  6. console.log(p === undefined); // true
  7. // Global variables have been created:
  8. console.log(x); // 7
  9. console.log(y); // 5

不過如果使用的是strict模式,那你還是會得到警告(this===undefined):

 
 
  1. function Point(x, y) {
  2.     'use strict';
  3.     this.x = x;
  4.     this.y = y;
  5. }
  6. var p = Point(7, 5);
  7. // TypeError: Cannot set property 'x' of undefined

4.2 不恰當(dāng)?shù)厥褂梅椒?/p>

如果你直接取得一個(gè)方法的值(不是調(diào)用它),你就是把這個(gè)方法當(dāng)做函數(shù)在用。當(dāng)你要將一個(gè)方法當(dāng)做一個(gè)參數(shù)傳入一個(gè)函數(shù)或者一個(gè)調(diào)用方法中,你很可能會這么做。setTimeout()和注冊事件句柄(event handlers)就是這種情況。我將會使用callIt()方法來模擬這個(gè)場景:

 
 
  1. /** Similar to setTimeout() and setImmediate() */
  2. function callIt(func) {
  3.     func();
  4. }

如果你是在Sloppy模式下將一個(gè)方法當(dāng)做函數(shù)來調(diào)用,*this*指向的就是全局對象,所以之后創(chuàng)建的都會是全局的變量。

 
 
  1. var counter = {
  2.     count: 0,
  3.     // Sloppy-mode method
  4.     inc: function () {
  5.         this.count++;
  6.     }
  7. }
  8. callIt(counter.inc);
  9. // Didn’t work:
  10. console.log(counter.count); // 0
  11. // Instead, a global variable has been created
  12. // (NaN is result of applying ++ to undefined):
  13. console.log(count);  // NaN

如果你是在Strict模式下這么做的話,this是undefined的,你還是得不到想要的結(jié)果,不過至少你會得到一句警告:

 
 
  1. var counter = {
  2.     count: 0,
  3.     // Strict-mode method
  4.     inc: function () {
  5.         'use strict';
  6.         this.count++;
  7.     }
  8. }
  9. callIt(counter.inc);
  10. // TypeError: Cannot read property 'count' of undefined
  11. console.log(counter.count);

要想得到預(yù)期的結(jié)果,可以使用bind()

 
 
  1. var counter = {
  2.     count: 0,
  3.     inc: function () {
  4.         this.count++;
  5.     }
  6. }
  7. callIt(counter.inc.bind(counter));
  8. // It worked!
  9. console.log(counter.count); // 1

bind()又創(chuàng)建了一個(gè)總是能將this的值設(shè)置為counter 的函數(shù)。

4.3 隱藏this

當(dāng)你在方法中使用函數(shù)的時(shí)候,常常會忽略了函數(shù)是有自己的this 的。這個(gè)this 又有別于方法,因此你不能把這兩個(gè)this 混在一起使用。具體的請看下面這段代碼:

 
 
  1. var obj = {
  2.     name: 'Jane',
  3.     friends: [ 'Tarzan', 'Cheeta' ],
  4.     loop: function () {
  5.         'use strict';
  6.         this.friends.forEach(
  7.             function (friend) {
  8.                 console.log(this.name+' knows '+friend);
  9.             }
  10.         );
  11.     }
  12. };
  13. obj.loop();
  14. // TypeError: Cannot read property 'name' of undefined

上面的例子里函數(shù)中的this.name 不能使用,因?yàn)楹瘮?shù)的this 的值是undefined,這和方法loop()中的this 不一樣。下面提供了三種思路來解決這個(gè)問題:

  • that=this,將this 賦值到一個(gè)變量上,這樣就把this 顯性地表現(xiàn)出來了(除了that,self 也是個(gè)很常見的用于存放this的變量名),之后就使用那個(gè)變量:
 
 
  1. loop: function () {
  2.     'use strict';
  3.     var that = this;
  4.     this.friends.forEach(function (friend) {
  5.         console.log(that.name+' knows '+friend);
  6.     });
  7. }
  • bind()。使用bind()來創(chuàng)建一個(gè)函數(shù),這個(gè)函數(shù)的this 總是存有你想要傳遞的值(下面這個(gè)例子中,方法的this):
 
 
  1. loop: function () {
  2.     'use strict';
  3.     this.friends.forEach(function (friend) {
  4.         console.log(this.name+' knows '+friend);
  5.     }.bind(this));
  6. }
  • 用forEach的第二個(gè)參數(shù)。forEach的第二個(gè)參數(shù)會被傳入回調(diào)函數(shù)中,作為回調(diào)函數(shù)的this 來使用。

5. ***實(shí)踐

理論上,我認(rèn)為實(shí)函數(shù)并沒有屬于自己的this,而上述的解決方案也是按照這個(gè)思想的。ECMAScript 6是用箭頭函數(shù)(arrow function)來實(shí)現(xiàn)這個(gè)效果的,箭頭函數(shù)就是沒有自己的this 的函數(shù)。在這樣的函數(shù)中你可以隨便使用this,也不用擔(dān)心有沒有隱式的存在。

 
 
  1. loop: function () {
  2.     'use strict';
  3.     // The parameter of forEach() is an arrow function
  4.     this.friends.forEach(friend => {
  5.         // `this` is loop’s `this`
  6.         console.log(this.name+' knows '+friend);
  7.     });
  8. }

我不喜歡有些API把this 當(dāng)做實(shí)函數(shù)的一個(gè)附加參數(shù):

 
 
  1. beforeEach(function () {  
  2.     this.addMatchers({  
  3.         toBeInRange: function (start, end) {  
  4.             ...
  5.         }  
  6.     });  
  7. });

把一個(gè)隱性參數(shù)寫成顯性地樣子傳入,代碼會顯得更好理解,而且這樣和箭頭函數(shù)的要求也很一致:

 
 
  1. beforeEach(api => {
  2.     api.addMatchers({
  3.         toBeInRange(start, end) {
  4.             ...
  5.         }
  6.     });
  7. });

文章標(biāo)題:JavaScript中this的工作原理以及注意事項(xiàng)
當(dāng)前地址:http://www.dlmjj.cn/article/cddsepe.html