立即(自)执行函数 定义全局变量 var _hmt = _hmt || [] 百度统计代码引发的思考

相信大家对下面的这段代码肯定不陌生,前端同学可能都写过或者在扒别人网站时都见过:

var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?xxxxxxxxxxxxx";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();

本人也是刚开始深入js领域,所以剖析的东西较浅显,前端资深同学请飘过哈…
我一开始接触这段代码,是在React单页面应用工程的.index.html文件中,然后见到很多.js文件里有使用_hmt.push(['_trackPageview', "/#/xxx"]);
当时能想到的是_hmt肯定是一个全局变量,之所以先写var _hmt = _hmt || []; 应该是为了防止一种case的发生:百度统计的js文件未加载完成前,代码就有使用_hmt变量的了,但这时候_hmt属于未申明会报is not defined错误,也就不能执行push操作 ,所以才给了默认值[](数组具有push方法)。但是不明白的是百度统计代码里面是怎么将_hmt重新赋值并且怎么处理未加载成功前就已经执行push等方法的case。
如果有人跟我有一样的疑问的话,说明前端js基础知识不够扎实了,哈哈来咱们来一起学习下呗。
上面那段代码,其中是包含两个知识点的:
1. 立即执行函数表达式(IIFE)

相信大家都看过下面的这几种写法
(function(){})() 
(function(){}())
!function(){}()
+function(){}()
-function(){}()
~function(){}()
传统的写法
function f() {...};
f();
1.很明显传统的方法啰嗦,定义和执行分开写;
2.直接污染全局命名空间(window的属性)
肯定有人想过那为啥不是function(){}()这种写法呢?试试就会发现
 //SyntaxError: function statement requires a name 发生语法错误了,js代码解析遇到function 关键字认为是在函数声明 后面跟()是不行的。
那我们再去看看最上面那几种写法有么有共通点呢?()|!|+|-|~ 这些是什么呢,是运算符。
运算符 + 函数声明 = 立即执行函数?
大家再拓展下思维,想想以前是不是也写过xx-0,xx*0这些代码呢我们当时是利用的js的运算规则(自动转化参与运算的变量),这样让变量自动变成number类型了。
那么我们再回过头来来看上面,其实道理是一样的,也是利用js的运算促使函数的自动执行,可以理解为通过运算来调用这个函数,可以明确的讲不是函数自己执行了,而是通过部分运算表达式来调用了这个函数。

2.定义全局变量

1.使用var在js的function外定义一个变量
var tt='test';
function ff(){
   alert(tt);
}

2.不使用var,直接给定义变量,隐式的声明了全局变量
tt='test';
function ff(){
   alert(tt);
}
这种方法需要注意的地方:如果是在一个函数体内去定义变量的话,只有当该函数执行后它才变成了全局变量,才能被其他函数知道,所以最好定义在函数体外

3.使用window.变量名定义为全局变量.
window.tt='test';
function ff(){
   alert(window.tt);
}
其中一个小知识点请get:(在我们不使用var定义的变量的时候以及window属性上定义的变量,变量的特性configurable的值为true,可以使用delete对其操作。而使用var定义的全局变量的时候,变量的特性configurable的值为false,不可以使用delete对其操作。)

下面咱们再回到百度统计代码`var _hmt = _hmt || [];`
我先前抛的一个问题是百度统计代码里面是怎么将_hmt重新赋值的?
讲述完全局变量的定义后我想大家应该明白了的,使用var定义的全局变量,是可以使用Window.属性名去访问的,下面给一段证明的代码
 <script type="text/javascript">
  var _hmt = _hmt || [];  
  _hmt='I am  first'
  console.log(window._hmt)
</script>
console.log //  打印 I am  first
由此可见全局变量的一个缺点:代码可读性低。很多函数都可能会使用全局变量,去做一些赋值操作,导致全局变量的值可能随时发生变化,对于程序的查错和调试不利。

还剩一个问题是:百度统计js代码是怎么处理_hmt变量在百度统计js文件未加载成功前就以已经被执行push等方法而产生的历史遗留件呢?
大概原理:先判断_hmt的数据类型,如果其是数组说明js没加载过,然后把当前_hmt保存的数据先存到临时变量里,待_hmt被重新赋值为正确的统计js的function对象后,最后再过来处理下临时变量里的数据,这样就保证了数据不流失。有兴趣的同学,可以去百度统计源码里去找下。

猜你喜欢

转载自blog.csdn.net/u012982629/article/details/80463583