牢騷與javascript中的this


最近在看關於拖延症的一本書《拖拉一點也無妨》,后面得出結論是自己寫博客大部分處於兩種狀態,心情很好和心情很不好的時候。因為正常狀態下感覺寫博客吧,是件很麻煩的事情,不如去看看電影看看漫畫啥的。最近在看漫畫《進擊的巨人》和《一拳超人》,感覺是兩種極端,哈哈。    

 

最近在進行某個項目的重寫工作,前后端都要重新構架重寫,時間給了一個月。項目的現狀大概是后端一個類有一萬行左右,包含幾十個方法。每個方法從一兩百行到上千行不等,大部分方法是沒有參數和返回值的,全局的操作幾百個成員變量。業務需求不明確,沒人能說得清,反正任務就是在不影響現有的功能的情況下重構+重寫。現有的功能有啥?也沒人能說得清。你說測試怎么驗收通過?反正測試也說不清。

前端也面臨着同樣的情況,基本上都是全局的function湊合成的,幾個文件加起來也有1萬+行。同樣沒人能說得清到底有啥東西。咱作為光榮的“接盤俠”現在就要負責處理這些留下的寶貴遺產了。前端重寫+后端重寫+數據庫SQL性能調校。

前端打算引入EventProxy和Seajs來重新整理了。

好吧,閑話扯到這里,現在開始繼續順帶的內容了,javascript中的this

 

一、Javascript中的this

話說javascript中的this是個變態吧,總結一下:                

隱式的改變this的指向的方法

1.直接用括號()調用function的方式,這時this指向的是全局對象。

2.作為對象的方法調用,那么就是指向調用方法的對象。下面就是通過改變this來借用方法。

    function addToArray() {
        arguments.slice = Array.prototype.slice;
        var add = arguments.slice(0);
        return add.concat();
    }

可能有些人沒看明白,咱的文章習慣打破砂鍋問到底嘛,再舉幾個例子 :

我們知道Function類型是javascript中的頂級類型,可以定義自己的屬性和方法。假如有這么一個方法

Function.prototype.test = function () {
    console.log(this === Function.prototype)
}

這種寫法我相信有一點js經驗的人都應該見過,這樣寫就可以給所有的函數實例加上了test方法,可具體是怎么實現的呢?我想很多人就說不清楚了。

上面這種寫法,如果這樣調用,會顯示什么呢?

Function.prototype.test()

答案是 true !

這沒什么好奇怪的,因為此時調用test方法的的確是Function類的prototype屬性的對象。但如果你想想,如果this指向的是prototype的話,那么test方法為什么會在每個函數實例中都能調用呢?

因為我們的確不會像上面這樣直接調用test方法,而是通過Function類的實例來調用test方法,這時候有東西悄悄發生了變化。沒錯,這就是this的指向。

如果有人還記得我上篇文章談談javascript中的prototype與繼承的話,就知道javascript中的對象就是一個指向prototype的指針和一個自身的屬性列表

所以作為函數類的實例,其實是通過指針的方式隱式借用了Function類的prototype屬性中的所有方法。這時this指向的就不再是prototype對象,而是這個實例。

用代碼來表示,類似於

this.test = Function.prototype.test

這里的this指向的是函數類的實例,因此調用的時候,結果就為false。

function myFunction(){}
myFunction.test()

 

 

顯式的改變this的一些方法和關鍵字

1.call

    function addToArray() {
        var add = Array.prototype.slice.call(arguments,0);
        return add.concat();
    }

2.apply

    function addToArray() {
        var add = Array.prototype.slice.apply(arguments,[0]);
        return add.concat();
    }

 

3.bind (ECMA Script5)

它可以看做是call 和apply的延遲版本,如果不存在的話,可以這么實現

簡單版本

if (!Function.prototype.bind) {
    Function.prototype.bind = function (target) {
        var func = this;

        return function () {
            func.apply(target, arguments);
        }
    }
}

 

其實在ECMA Script5的規定中bind是可以預填參數的,考慮到性能的話,相對復雜一些。大部分情況下我們調用bind只是希望改變this的作用域,如果全部調用了slice和concat方法,那么性能就會相對不好。根據arguments的length不同,可以分2種綁定一共4種調用的case,在大部分情況下去獲取更好的性能。

if (!Function.prototype.bind) {
  (function () {
    var slice = Array.prototype.slice;

    Function.prototype.bind = function (target) {
      var func = this;

      if (arguments.length > 1) {
        var args = slice.call(arguments, 1);

        return function () {
          var allArgs = args;

          if (arguments.length > 0) {
            allArgs = args.concat(slice.call(arguments));
          }

          return func.apply(target, allArgs);
        };
      }

      return function () {
        if (arguments.length > 0) {
          return func.apply(target, arguments);
        }

        return func.call(target);
      };
    };
  }());
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM