2013-02-13 2 views
8

Я рассечение следующего фрагмента кода, который используется для асинхронной загрузки Segment.io аналитики обертки скрипта:Почему сценарий загрузчика Segment.io вытесняет имена методов/args в очередь, которая, по-видимому, перезаписывается?

// 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 без упаковки этих вызовов в качестве if или ready() вызова.

Но методы на самом деле лучше, чем «тупые» заглушки, поскольку они сохраняют метод, который был вызван, поэтому мы можем повторить действия позже. Вот эта часть:

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, или я просто пропустил его? –

+1

Ах, да, это специфично для нашего фрагмента на Segment.io. Если вы используете автономный режим, библиотека не загружается асинхронно, поэтому вам не нужно воспроизводить очередь. –

+0

Право, имеет смысл сейчас! –