Google Analytics代码解释

52

有人可以逐步解释这段代码吗?

我想了解更多关于异步代码的知识以及Google如何加载它们的脚本,如何 "隐藏" JavaScript 代码(我知道我无法隐藏它,但至少可以像Google一样,不要在一个文件中显示所有代码)。

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-xxxxxxxx-x', 'xxxxxx.com');
  ga('send', 'pageview');
</script>

“逐字逐句”使用短变量的匿名函数异步加载另一个脚本(在文档树中的任何其他脚本之前)并执行2个函数。由于混淆,您无法逐步阅读。从“i s o g r”开始 - 这是“(window,document,'script','//www.google-analytics.com/analytics.js','ga')”。 - Mihai Iorga
(i[r].q=i[r].q||[]).push(arguments) 这是什么意思? - Milos Miskone Sretin
window['ga'],推送定义的参数 - 'create','UA-xxxxxxxx-x','xxxxxx.com' - Mihai Iorga
i 是来自 isogr 的第一个变量,它是匿名函数中的变量之一 - window,而 r 则是第二个变量 - ga - Mihai Iorga
7个回答

121

首先,我会将这个通过一个美化器,例如 http://jsbeautifier.org/

 (function (i, s, o, g, r, a, m) {
     i['GoogleAnalyticsObject'] = r;
     i[r] = i[r] || function () {
         (i[r].q = i[r].q || []).push(arguments)
     }, i[r].l = 1 * new Date();
     a = s.createElement(o),
     m = s.getElementsByTagName(o)[0];
     a.async = 1;
     a.src = g;
     m.parentNode.insertBefore(a, m)
 })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

 ga('create', 'UA-xxxxxxxx-x', 'xxxxxx.com');
 ga('send', 'pageview');

之后让我们评估关闭

(function (i, s, o, g, r, a, m) {
...
 })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
通过将命名参数i,s,o,g,r分别替换为它们对应的值window,document,'script','//www.google-analytics.com/analytics.js','ga'来完成。请注意,参数am没有输入值,更像是结果变量。这大致相当于(不考虑变量作用域等):
(function (i, s, o, g, r, a, m) {
     window['GoogleAnalyticsObject'] = 'ga';
     window['ga'] = window['ga'] || function () {
         (window['ga'].q = window['ga'].q || []).push(arguments)
     }, window['ga'].l = 1 * new Date();
     a = document.createElement('script'),
     m = document.getElementsByTagName('script')[0];
     a.async = 1;
     a.src = '//www.google-analytics.com/analytics.js';
     m.parentNode.insertBefore(a, m)
 })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

 ga('create', 'UA-xxxxxxxx-x', 'xxxxxx.com');
 ga('send', 'pageview');

简而言之,这段代码的本质是创建了一个新的脚本标签,其中包含以下行:

a = document.createElement('script'),

然后找到第一个


7
很好的回答@Zlatin。想知道您是否知道为什么此脚本会动态地创建一个脚本标记?为什么不直接在一开始使用<script ... async="true">呢? - cavalcade
3
请参考以下链接:http://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/ 和 http://www.html5rocks.com/en/tutorials/speed/script-loading/,它们可以让你了解为什么有些人更喜欢使用基于脚本的方式来加载其他脚本,而不是纯粹使用脚本标签进行加载。 此外,请查看https://dev59.com/12ox5IYBdhLWcg3wvW3X。 - Zlatin Zlatev
1
@Brandito 是的,没错。但它并没有从闭包的调用中接收到初始值(在自执行匿名函数中没有传递值给 m 的参数)。 - Zlatin Zlatev
1
@ZlatinZlatev 对,我现在明白了,谢谢你用通俗易懂的方式解释给我:p - brandito
3
我希望Stack Overflow上有更多这样的解释性问题。 - PirateApp
显示剩余5条评论

37

谷歌已经发布了此代码的未压缩版本:

<!-- Google Analytics -->
<script>
/**
 * Creates a temporary global ga object and loads analytics.js.
 * Parameters o, a, and m are all used internally. They could have been
 * declared using 'var', instead they are declared as parameters to save
 * 4 bytes ('var ').
 *
 * @param {Window}        i The global context object.
 * @param {HTMLDocument}  s The DOM document object.
 * @param {string}        o Must be 'script'.
 * @param {string}        g Protocol relative URL of the analytics.js script.
 * @param {string}        r Global name of analytics object. Defaults to 'ga'.
 * @param {HTMLElement}   a Async script tag.
 * @param {HTMLElement}   m First script tag in document.
 */
(function(i, s, o, g, r, a, m){
  i['GoogleAnalyticsObject'] = r; // Acts as a pointer to support renaming.

  // Creates an initial ga() function.
  // The queued commands will be executed once analytics.js loads.
  i[r] = i[r] || function() {
    (i[r].q = i[r].q || []).push(arguments)
  },

  // Sets the time (as an integer) this tag was executed.
  // Used for timing hits.
  i[r].l = 1 * new Date();

  // Insert the script tag asynchronously.
  // Inserts above current tag to prevent blocking in addition to using the
  // async attribute.
  a = s.createElement(o),
  m = s.getElementsByTagName(o)[0];
  a.async = 1;
  a.src = g;
  m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

// Creates a default tracker with automatic cookie domain configuration.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sends a pageview hit from the tracker just created.
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->

https://developers.google.com/analytics/devguides/collection/analyticsjs/tracking-snippet-reference

Zlatin的逐行解释仍然有效。


4

i['GoogleAnalyticsObject']=r;这行代码是将'ga'赋值给'window'的'GoogleAnalyticsObject'属性。

window['ga'] = window['ga'] || function(){
        (window['ga'].q = window['ga'].q || []).push(arguments)
    }, window['ga'].l = 1 * new Date();

这部分代码将 window 的 'ga' 属性赋值为一个函数(如果它已存在,则为本身)。该函数然后将 q 属性赋值为空数组,并将所有函数参数添加到其中。接下来,它将 l 属性赋值为当前时间戳(乘以1将其强制转换为整数)。
接下来的几行代码只是创建一个脚本标签,并为其分配一些属性,如源和 async = true,然后将此脚本标签添加到文档中。

2
代码被压缩了。使用http://jsbeautifier.org/,你可以将其还原(有点儿),并使其更易读。基本上,这是一个小函数,它使用相同的协议(http或https)向dom添加另一个javascript(www.google-analytics.com/analytics.js)。
(function (i, s, o, g, r, a, m) {
    i['GoogleAnalyticsObject'] = r;
    i[r] = i[r] || function () {
        (i[r].q = i[r].q || []).push(arguments)
    }, i[r].l = 1 * new Date();
    a = s.createElement(o),
    m = s.getElementsByTagName(o)[0];
    a.async = 1;
    a.src = g;
    m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

1
代码已通过缩小器运行,当以漂亮的方式打印时,代码如下:
(function (i, s, o, g, r, a, m) {
    i['GoogleAnalyticsObject'] = r;
    i[r] = i[r] || function () {
        (i[r].q = i[r].q || []).push(arguments)
    }, i[r].l = 1 * new Date();
    a = s.createElement(o),
    m = s.getElementsByTagName(o)[0];
    a.async = 1;
    a.src = g;
    m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

ga('create', 'UA-xxxxxxxx-x', 'xxxxxx.com');
ga('send', 'pageview');

要了解它确切的作用,您可能需要查看他们的analytics.js文件,但由于这很有可能被压缩,因此您不会得到太多信息。
如果您想做同样的事情,可以使用JSMin等代码压缩工具。它将替换任何不必要的空格和换行符等内容,以帮助减少带宽。

如果你想这么做的话,反混淆代码并不是很难——请查看我下面的评论。 - Zlatin Zlatev
1
我没有说这很难,我是说楼主需要去查阅谷歌的代码并学习。逆向工程谷歌正在做什么可能是获得理解的好方法,但需要耗费比我在回答中花费更多的时间。你对分解事物做得非常出色,非常好,向你致敬 :) - fnostro

0
更优化的代码:
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga');ga('create','UA-xxxxxxxx-x','xxxxxx.com');ga('send','pageview');

Google Analytics 的跟踪入口已经进行了美化:

(function(i, s, o, g, r, a, m) {
    i['GoogleAnalyticsObject'] = r;
    i[r] = i[r] || function() {
        (i[r].q = i[r].q || []).push(arguments)
    }, i[r].l = 1 * new Date();
    a = s.createElement(o),
    m = s.getElementsByTagName(o)[0];
    a.async = 1;
    a.src = g;
    m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-xxxxxxxx-x', 'xxxxxx.com');
ga('send', 'pageview');

Cixon在这段格式化的代码中添加了//注释

(function(i, s, o, g, r, a, m) {
    // Pointer - analytics.js uses window['GoogleAnalyticsObject'] to access the string 'ga', sort of a longness of their minified code, that is a problem. But it is to support renaming.
    i['GoogleAnalyticsObject'] = r;
    // Create a GA function if one does not exist, and assign it to window with the string 'ga'. What it does is pushes arguments to the end of an array that is either already defined or is defined in the function
    i[r] = i[r] || function() {
        (/*set to support a case where it is not defined*/ i[r].q = /*if it is already defined, get it*/ i[r].q /*define it here*/ || []).push(arguments)
    };
    // Sets the time (as an integer) this tag was executed.
    // Used for timing hits.
    i[r].l = 1 * new Date();
    // Create the script tag for Google Analytics
    a = s.createElement(o);
    // Get the first script tag
    m = s.getElementsByTagName(o)[0];
    // Set the async property to true (1) and set the src property to the path for Analytics
    a.async = 1;
    a.src = g;
    // Makes this GA tracking code script element (a) go before this 'first script element' (m)
    m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
// Initialize the actual GA runtime
ga('create', 'UA-xxxxxxxx-x', 'xxxxxx.com');
// Send a PageView request to Google Analytics
ga('send', 'pageview');

你能看懂那个被注释的代码吗?

要真正理解这个,你需要查看他们的 Analytics.js 文件,但是它也被压缩了,所以在我完成this之前,你不会从中获得太多信息。


0
Google在这里解释了:https://developers.google.com/analytics/devguides/collection/analyticsjs/tracking-snippet-reference#the-google-analytics-tag,位于“未压缩版本”下。
<!-- Google Analytics -->
<script>
/**
 * Creates a temporary global ga object and loads analytics.js.
 * Parameters o, a, and m are all used internally. They could have been
 * declared using 'var', instead they are declared as parameters to save
 * 4 bytes ('var ').
 *
 * @param {Window}        i The global context object.
 * @param {HTMLDocument}  s The DOM document object.
 * @param {string}        o Must be 'script'.
 * @param {string}        g Protocol relative URL of the analytics.js script.
 * @param {string}        r Global name of analytics object. Defaults to 'ga'.
 * @param {HTMLElement}   a Async script tag.
 * @param {HTMLElement}   m First script tag in document.
 */
(function(i, s, o, g, r, a, m){
  i['GoogleAnalyticsObject'] = r; // Acts as a pointer to support renaming.

  // Creates an initial ga() function.
  // The queued commands will be executed once analytics.js loads.
  i[r] = i[r] || function() {
    (i[r].q = i[r].q || []).push(arguments)
  },

  // Sets the time (as an integer) this tag was executed.
  // Used for timing hits.
  i[r].l = 1 * new Date();

  // Insert the script tag asynchronously.
  // Inserts above current tag to prevent blocking in addition to using the
  // async attribute.
  a = s.createElement(o),
  m = s.getElementsByTagName(o)[0];
  a.async = 1;
  a.src = g;
  m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

// Creates a default tracker with automatic cookie domain configuration.
ga('create', 'UA-XXXXX-Y', 'auto');

// Sends a pageview hit from the tracker just created.
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接