能不能解释一下“ www.google-analytics.com/analytics.js”的作用?我是说代码的工作方式以及2469行代码的作用是什么...
我已经看到了加载此脚本的代码的解释。在这里:Google Analytics Code Explanation。这很有用,但仅说明了最终脚本的加载方式,而不是实际analytics.js文件的内部工作方式。
答案 0 :(得分:0)
da = function(a) {
var b = document.createElement("a");
a && (ye.test(a), b.href = a);
a = b.pathname;
"/" !== a[0] && (a = "/" + a);
var c = b.hostname.replace(Ce, "");
return {
href: b.href,
protocol: b.protocol,
host: b.host,
hostname: c,
pathname: a,
search: b.search,
hash: b.hash,
port: b.port
}
},
这使用了一些浏览器技巧。浏览器必须进行一些处理才能创建<a>
标记,并且可以利用它。第179行是实际创建<a>
元素的位置;它不会出现在页面上,因为它从未被添加为document
的后代。第180行只是if (a) b.href = a;
; ye.test
是完全多余的。
现在,这还没有得到很好的记录,但是通过JavaScript可以访问<a>
标签的一些特殊属性,例如anchor.pathname
。 Internet Explorer提供了类似的属性,只是它不是以"/"
开头。第182行检查b.pathname
的第一个字符是否为"/"
,如果不是,则向其添加"/"
。 (这纯粹是Internet Explorer的兼容性问题。)第183行使用Beta-{Ce
}从主机名中删除端口号。
Beta-da
返回一个对象,其中包含href
,未规范化的protocol
,host
,hostname
-无端口号,{{1} },pathname
(查询字符串),search
(锚定)和传递给它的网址的hash
。
我刚刚意识到我已经解码了Beta-port
,因此我们可以返回到我在第1部分末尾留下的悬崖峭壁!
ha
Beta-xe
始终返回ha
。这意味着...是的,我很难相信任何Google员工都会这这是愚蠢的,这意味着
我在分析中犯了另一个错误。 (如果需要,则为问题here。)但让我们继续吧。
-1
什么意思
case "path":
if (a.pathname.substr(0, 1) == "/") {
a = a.pathname;
} else {
a = "/" + a.pathname;
}
a = a.split("/");
if (0 <= -1) {
a[a.length - 1] = "";
}
a = a.join("/");
return a;
这意味着...
case "path":
if (a.pathname.substr(0, 1) == "/") {
a = a.pathname;
} else {
a = "/" + a.pathname;
}
a = a.split("/");
if (false) {
a[a.length - 1] = "";
}
a = a.join("/");
return a;
因此,它拆分了 case "path":
if (a.pathname.substr(0, 1) == "/") {
a = a.pathname;
} else {
a = "/" + a.pathname;
}
a = a.split("/");
a = a.join("/");
return a;
,然后又无缘无故地再次加入它。所以我们也可以删除它...
a
它类似于Beta-{ case "path":
if (a.pathname.substr(0, 1) == "/") {
a = a.pathname;
} else {
a = "/" + a.pathname;
}
return a;
中的181和182行。因此,给定da
的Beta-xe
将输出"path"
。这似乎很明智!
pathname
我敢打赌这是另一个哈希函数。 Beta- te, Ae, ve = function(c) {
te = te || b();
Ae = Ae || a();
for (var d = [], e = 0; e < c.length; e += 3) {
var g = e + 1 < c.length,
ca = e + 2 < c.length,
Ee = c.charCodeAt(e),
l = g ? c.charCodeAt(e + 1) : 0,
w = ca ? c.charCodeAt(e + 2) : 0,
k = Ee >> 2;
Ee = (Ee & 3) << 4 | l >> 4;
l = (l & 15) << 2 | w >> 6;
w &= 63;
ca || (w = 64, g || (l = 64));
d.push(te[k], te[Ee], te[l], te[w])
}
return d.join("")
},
是一个带有单个字符串参数的函数。 Beta-{ve
和Beta-te
是全局变量,它们分别缓存Beta-{Ae
和Beta-b()
的结果,除非在其他地方被覆盖(我不这样做) 认为就是这种情况)…
我们以前没见过a()
吗?
现在我们可以看到Beta-
te
从类似a
的对象Array
构造一个对象,其中键是值,而值是键,但仅用于数字键。
因此,Beta-te
创建了a
的一种缓存版本,存储为te.indexOf
。由于:
Beta-
Object
返回字符串b
。
我们知道Beta-{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_."
将始终包含相同的对象。现在,我开始认为该函数是生成那些随机字符串的函数。
因此,无论如何,Ae
循环会在3秒内遍历字符串。如果字符串的部分小于3,则使用for
;否则,它将使用角色的代码。然后,它通过移位来执行一些hashy类型的加扰操作,使用Beta-{0
将其映射到一个字符,然后将它们全部连接起来。
我将澄清代码:
te
如果我正确阅读的话,这是Base 64的实现。看看Beta-{ te, Ae, ve = function(c) {
te = te || b();
Ae = Ae || a();
var chars = [];
for (var e = 0; e < c.length; e += 3) {
var is_two_long = e + 1 < c.length;
var is_three_long = e + 2 < c.length;
var Ee = c.charCodeAt(e);
var l = is_two_long ? c.charCodeAt(e + 1) : 0;
var w = is_three_long ? c.charCodeAt(e + 2) : 0;
// the following comments assume that Ee, l and w are 8-bit,
// since that's the only way the code makes any sense.
// since charCodeAt can return values up to 65535, it's clear
// that this code was only tested on English text.
// the final values produced are all 6-bit.
var k = Ee >> 2; // top 6 bits of Ee
Ee = (Ee & 3) << 4 | l >> 4; // bottom 2 bits of Ee, then
// top 4 bits of l
l = (l & 15) << 2 | w >> 6; // bottom 4 bits of l, then
// top 2 bits of w
w &= 63; // bottom 6 bits of w
// replace nonsense with padding character
if (!is_three_long) {
w = 64;
if (!is_two_long) {
l = 64;
}
}
chars.push(te[k], te[Ee], te[l], te[w]);
}
return chars.join("");
},
,很明显填充字符(Beta-te
)是te[64]
而不是.
。
因此,Beta-{=
是ve
的一个版本,它从一些无效的字符串中创建垃圾,而不是始终引发异常。
哦,谷歌。
c => btoa(c).replace('+', '-').replace('/', '_').replace('=', '.')
我现在称呼它为:Beta- Ke = function(c) {
function d(a) {
for (; g < c.length;) {
var b = c.charAt(g++),
d = Ae[b];
if (null != d) return d;
if (!/^[\s\xa0]*$/.test(b)) throw Error(
"Unknown base64 encoding at char: " + b);
}
return a
}
te = te || b();
Ae = Ae || a();
for (var e = "", g = 0;;) {
var ca = d(-1),
Ee = d(0),
l = d(64),
w = d(64);
if (64 === w && -1 === ca) return e;
e += String.fromCharCode(ca << 2 | Ee >> 4);
64 != l && (e += String.fromCharCode(Ee << 4 & 240 | l >> 2),
64 != w && (e += String.fromCharCode(l << 6 & 192 | w)))
}
},
从早期开始解码专有的Base 64编码。
Beta-{Ke
-Ke
接受一个参数,可能是d
,如果到达Beta-Number
-Ke
的末尾,它将返回该参数。 。 Beta-c
-Ke
是计数器,用于说明我们当前使用Beta-g
-Ke
的哪个字符。 c
循环实际上是变相的for
循环。正则表达式可确保在任何字符既无效也不为空白时引发while
。
Beta-{Error
-Ke
返回下一个有效的Base 64字符的d
,除非找到无效的非空白字符(在这种情况下,它会引发{{ 1}})或字符串用完,在这种情况下,它将返回其单个参数。
在Beta-{Number
-Error
之后,您将获得“设置Beta-{Ke
和Beta-d
(如果尚未设置)”,但是因为这与Beta-te
中的代码没有什么不同,所以我们不需要对此予以过多关注。
随后的Ae
循环仅将Base64转换回“原始”字符串(或考虑到原始字符串可能进行的整形,使其尽可能接近原始字符串)。
这样,Beta-{ve
与for
完全等效,除了由于常量Ke
之外,您甚至不能说它更高效。 Beta-c=>atob(c.replace(/[\s\xa0]*/, "").replace('-', '+').replace('_', '/').replace('.', '='))
的作者显然不同于Beta-e += blah
。
Ke
我尚不知道Beta-ve
用于什么用途,但至少我们知道它的范围。
Beta- Le, Me = function() {
var a = {},
b = Ie.sa;
Ie.sa = void 0 === b ? a : b;
a = Ie.sa;
(b = a.wa) && b.va || (b = {
va: []
}, a.wa = b);
return b
},
的含义稍微容易一些,因为:
Beta-{
Le
是Me
,Beta-Ie
是window
。
这意味着我可以这样重写它:
He
完美!辉煌!惊人! Beta-document
会将 Me = function() {
if (window.sa === undefined) {
window.sa = {};
}
var sa = window.sa;
if (!sa.wa || !sa.wa.va) {
sa.wa = {va: []};
}
return sa.wa;
},
初始化为Me
,如果未定义window.sa.wa.va
,则将[]
清除(尽管这可能只是实现细节)。现在我们知道wa
的结构了!
wa.va
Beta-window.sa
将尽可能少地匹配,其次是星号,然后尽可能少地匹配,然后是星号,再接着是尽可能多的匹配。因此, Nd = /(.*?)\*(.*?)\*(.*)/,
将根据匹配方法而导致组Nd
或"abc*def*ghi*jkl"
。我不确定这将用于什么。
["abc*def*ghi*jkl", "abc", "def", "ghi*jkl"]
测试版{["*def*ghi*jkl", "", "def", "ghi*jkl"]
将匹配 we = /([^?#]+)(\?[^#]*)?(#.*)?/,
或we
以外的一个或多个字符,可以选择后面跟着?
然后是一些非#
字符, /或?
后跟一些东西。 #
将分为#
,"https://stackoverflow.com/search?q=programming&where=boat#anchor? yaar!"
和"https://stackoverflow.com/search"
。
这是多余的;代码中已经有其他方法可以做到这一点。但是-这是在Beta-"?q=programming&where=boat"
中使用的!
"#anchor? yaar!"
e
我们知道e
绝对是URL,因此 function e(a, b, c) {
if (c === undefined) {
c = false;
}
var match = we.exec(b);
if (!match) return "";
var match1 = match[1];
var g = match[2] || "";
var Ee = match[3] || "";
var Fe = "_gl=" + a;
function e(a) {
a = d(a);
var b = a.charAt(a.length - 1);
if (a && b !== "&") {
a += "&";
}
return a + Fe
}
if (c) {
Ee = "#" + e(Ee.substring(1));
} else {
g = "?" + e(g.substring(1));
}
return "" + match1 + g + Ee
}
成为b
似乎毫无意义–为什么在地球上您将GET参数附加到锚点上?
我只能重申:
看起来越来越像Beta-
c
为GET请求建立URL。
true
哦……这很棘手,但是我想我已经解决了。此正则表达式查找字符串中最早出现e
的字符串。仅向其传递不带 ze = /(.*?)(^|&)_gl=([^&]*)&?(.*)/,
的查询字符串才有意义。共有四个匹配组。
_gl=
;否则为空字符串。?
参数的“值”。&
之后的所有内容(如果存在)。然后,将第1组,第2组和第4组连接起来,得到一个等效字符串,没有_gl
。等等...那很熟悉。
&
_gl
Beta-
d
是一个带有function d(a) { var b = ze.exec(a); if (b) { var c = b[2], d = b[4]; a = b[1]; d && (a = a + c + d) } return a }
参数的函数。如果d
String
与该字符串匹配,则如果第4组为空,则函数返回第1组,如果不是,则返回第1、2和4组的串联。如果不匹配,该函数将返回它最初传递的字符串。
我可以更新它。
Beta-
RegExp
是一个带有ze
参数的函数,代表一个查询字符串。如果存在(最早的)d
GET参数,它将删除。
现在我们对Beta-String
有了更多的了解。
_gl
e
考虑到所有这些因素,很明显Beta-e
取一个 function e(a, b, c) {
if (c === undefined) {
c = false;
}
var match = we.exec(b);
if (!match) return "";
var match1 = match[1];
var g = match[2] || "";
var Ee = match[3] || "";
var Fe = "_gl=" + a;
function e(a) {
a = d(a);
var b = a.charAt(a.length - 1);
if (a && b !== "&") {
a += "&";
}
return a + Fe
}
if (c) {
Ee = "#" + e(Ee.substring(1));
} else {
g = "?" + e(g.substring(1));
}
return "" + match1 + g + Ee
}
值,一个URL和一个e
,并替换了_gl
的值。 URL(如果存在)以第一个参数提供。如果Boolean
标志是_gl
或未提供,则该函数将具有修改查询字符串的明智行为;如果是Boolean
,它将修改定位字符串。
false
美容师在这里做得不好。
true
Beta- Qe = function(a) {
var b = [],
c;
for (c in a)
if (a.hasOwnProperty(c)) {
var d = a[c];
void 0 !== d && d === d && null !== d && "[object Object]" !== d
.toString() && (b.push(c), b.push(ve(String(d))))
} a = b.join("*");
return ["1", Ne(a), a].join("*")
},
以 Qe = function(a) {
var b = [];
for (var k in a)
if (a.hasOwnProperty(k)) {
var v = a[k];
if (v !== undefined &&
v === v && // NaN check
v !== null &&
v.toString() !== "[object Object]") {
b.push(k);
b.push(ve(String(v)));
}
}
a = b.join("*");
return ["1", Ne(a), a].join("*");
},
作为参数,并迭代其属性,构建密钥的Qe
和Beta-Object
的结果(a Base64变体)以Array
的形式调用字符串化值。然后将此数组与ve
作为分隔符(分配给[key0, ve(String(value0)), key1, ve(String(value1)), …]
)进行连接,并返回字符串"*"
。
有趣的是,使用了两种不同的值化方式:a
和"1*" + Ne(a) + "*" + a
。 According to this Stack Overflow question, .toString
对于某些未定义String
方法但作为String
,.toString
的值的略微更具弹性。等等已经被过滤掉了-null
已经被调用而没有任何undefined
子句 –我认为这种区别没有任何好处。
目前尚不清楚它的目的是什么,但希望了解.toString
会对此有所澄清。哦,多么方便。
catch
啊……那是很多任意的常数和移位。它还使用Beta-{Ne
,解决了那个之谜。
Ne = function(a, b) {
a = [window.navigator.userAgent, (new Date)
.getTimezoneOffset(), window.navigator.xa || window.navigator
.language, Math.floor((new Date)
.getTime() / 60 / 1E3) - (void 0 === b ? 0 : b), a
].join("*");
if (!(b = Le)) {
b = Array(256);
for (var c = 0; 256 > c; c++) {
for (var d = c, e = 0; 8 > e; e++) d = d & 1 ? d >>> 1 ^
3988292384 : d >>> 1;
b[c] = d
}
}
Le = b;
b = 4294967295;
for (c = 0; c < a.length; c++) b = b >>> 8 ^ Le[(b ^ a.charCodeAt(
c)) & 255];
return ((b ^ -1) >>> 0)
.toString(36)
},
Beta-Le
是一个接受两个参数的函数,并返回各种数据散列的Base36表示形式。第一个是 Ne = function(a, b) {
a = [
window.navigator.userAgent,
new Date().getTimezoneOffset(),
window.navigator.xa || window.navigator.language,
Math.floor(new Date().getTime() / 60 / 1000) - (b === undefined ? 0 : b),
a
].join("*");
if (!Le) {
Le = Array(256);
for (var c = 0; 256 > c; c++) {
for (var d = c, e = 0; 8 > e; e++) {
d = d & 1 ? d >>> 1 ^ 3988292384 : d >>> 1;
}
Le[c] = d;
}
}
b = 4294967295;
for (c = 0; c < a.length; c++)
b = (b >>> 8) ^ Le[(b ^ a.charCodeAt(c)) & 255];
return ((b ^ -1) >>> 0).toString(36);
},
,第二个是(默认为Ne
)。
String
位实际上是用于初始化Beta-0
的代码。我不确定为什么将其放置在中间,而不是通常将初始化代码放置在 的开始,而且我也不知道为什么Beta-if (!Le)
被声明得太多考虑到Beta-Le
是唯一使用Beta-Le
的东西,因此早于Beta-{Ne
}。这只是这些谜团中的另一个。
Beta-Ne
(如果您想知道的话)总是相同的:
Le
Le
var Le = Array(256);
for (var c = 0; 256 > c; c++) {
for (var d = c, e = 0; 8 > e; e++) {
d = d & 1 ? d >>> 1 ^ 3988292384 : d >>> 1;
}
Le[c] = d;
}
// See, Google? You're not the only ones who can do this.
var ol = document.querySelector("ol");
Le.forEach((i,a)=>ol.appendChild(((a=document.createElement("li")).innerText=("0000000"+(i>>>0).toString(16)).substr(-8),a)));
因此,这是一种哈希,除了提供的字符串外,还考虑了用户的语言和时区,粗略时间和用户代理。 (在不知道每件事情的可能性小于2²²的情况下),我不认为可以将其取反来提取原始数据。几乎肯定不是很简单。
我不熟悉大多数哈希函数,所以我要提出有关堆栈溢出的问题。但是,仔细检查后,表生成中使用的随机十进制常数实际上等效于ol {
font-family: monospace;
}
–我们钟爱的CRC-32常数。因此,这似乎是CRC的简化版本,它仅检查每个值的低字节。
叹气。
激动等待着!确保选择下一期“ Google Analytics.js代码的作用是什么?”出来的时候。