2013-02-13 35 views
8

我已經解剖下面的代碼段,用於異步加載Segment.io分析包裝腳本:爲什麼Segment.io加載程序腳本將方法名/參數推送到似乎被覆蓋的隊列上?

// Create a queue, but don't obliterate an existing one! 
var analytics = analytics || []; 

// Define a method that will asynchronously load analytics.js from our CDN. 
analytics.load = function(apiKey) { 

    // Create an async script element for analytics.js. 
    var script = document.createElement('script'); 
    script.type = 'text/javascript'; 
    script.async = true; 
    script.src = ('https:' === document.location.protocol ? 'https://' : 'http://') + 
        'd2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/' + apiKey + '/analytics.min.js'; 

    // Find the first script element on the page and insert our script next to it. 
    var firstScript = document.getElementsByTagName('script')[0]; 
    firstScript.parentNode.insertBefore(script, firstScript); 

    // Define a factory that generates wrapper methods to push arrays of 
    // arguments onto our `analytics` queue, where the first element of the arrays 
    // is always the name of the analytics.js method itself (eg. `track`). 
    var methodFactory = function (type) { 
     return function() { 
      analytics.push([type].concat(Array.prototype.slice.call(arguments, 0))); 
     }; 
    }; 

    // Loop through analytics.js' methods and generate a wrapper method for each. 
    var methods = ['identify', 'track', 'trackLink', 'trackForm', 'trackClick', 
        'trackSubmit', 'pageview', 'ab', 'alias', 'ready']; 

    for (var i = 0; i < methods.length; i++) { 
     analytics[methods[i]] = methodFactory(methods[i]); 
    } 
}; 

// Load analytics.js with your API key, which will automatically load all of the 
// analytics integrations you've turned on for your account. Boosh! 
analytics.load('MYAPIKEY'); 

它是很好的註釋,我可以看到它在做什麼,但我很困惑,當談到到methodFactory函數,該函數將主要analytics.js腳本加載到全局analytics陣列之前進行的任何方法調用的詳細信息(方法名稱和參數)推送。

這是一切都很好,但隨後如果/當主腳本確實負載,它似乎只是覆蓋全球analytics變量(見last line here),因此所有數據都將丟失。

我怎麼看這樣可以防止腳本錯誤在網頁中通過測試框架哪些目前還不存在的方法,但我不明白爲什麼存根不能只返回一個空的功能:

var methods = ['identify', 'track', 'trackLink', 'trackForm', 'trackClick', 
       'trackSubmit', 'pageview', 'ab', 'alias', 'ready']; 

for (var i = 0; i < methods.length; i++) { 
    lib[methods[i]] = function() { }; 
} 

我錯過了什麼?請幫我理解!

回答

22

Ian在這裏,Segment.io的共同創始人 - 我沒有真正寫出那些代碼,加爾文也這麼做,但我可以讓你知道它在做什麼。

你是對的,methodFactory使他們腳本加載,這意味着人們可以打電話analytics.track不用包裹在ifready()調用這些調用之前可被刪空的方法。

但是這些方法實際上比「愚蠢的」存根更好,因爲它們保存了被調用的方法,因此我們可以稍後重放這些操作。這是這一部分:

analytics.push([type].concat(Array.prototype.slice.call(arguments, 0))); 

爲了讓更多的可讀性:

var methodFactory = function (method) { 
    return function() { 
     var args = Array.prototype.slice.call(arguments, 0); 
     var newArgs = [method].concat(args); 
     analytics.push(newArgs); 
    }; 
}; 

它大頭針上被調用的方法的名稱,這意味着如果我analytics.identify('userId'),我們的隊列實際上得到一個數組,看起來像:

['identify', 'userId'] 

然後,當我們的庫加載的,它卸載所有排隊的呼叫,並將其重播到現實的方法(現在可用),這樣所有數據在負載仍然保留之前記錄。這是關鍵的一部分,因爲我們不想在我們的圖書館有機會加載之前丟棄任何發生的電話。這看起來是這樣的:

// Loop through the interim analytics queue and reapply the calls to their 
// proper analytics.js method. 
while (window.analytics.length > 0) { 
    var item = window.analytics.shift(); 
    var method = item.shift(); 
    if (analytics[method]) analytics[method].apply(analytics, item); 
} 

analytics是在這一點上一個局部變量,而我們完成重播後,我們更換了全球與本地analytics(這是真正的交易)。

希望是有道理的。我們實際上將在我們的博客上發佈關於第三方Javascript的所有小技巧的系列文章,所以您可能很快就會挖掘它!

+0

太棒了!謝謝你解釋,伊恩,我期待着博客文章。我認爲這將是一些沿着這些方向的東西,但是本地到全球的位置給我帶來了刺激,再加上我不能(也不能)看到一段實際上在任何地方實際應用這些呼叫的代碼。 這是特定於Segment.io而不是標準analytics.js庫代碼的東西,還是我只是想念它? – 2013-02-19 22:06:51

+1

嗯,它是特定於Segment.io上的片段。如果您使用的是獨立版本,則不會異步加載庫,因此您無需重播隊列。 – 2013-02-19 23:36:03

+0

對,現在有道理! – 2013-02-20 05:16:58