;(function(win, doc, $, undefined) {
// 主业务代码
})(window, document, jQuery);
甚至连如RequireJS, SeaJS, OzJS 等前端模块化加载解决方案,都是采用类似的形式:
复制代码 代码如下:
// RequireJS
define(['jquery'], function($) {
// 主业务代码
});
// SeaJS
define('module', ['dep', 'underscore'], function($, _) {
// 主业务代码
});
如果你说很多Node.js 开源项目的代码都没有这样处理的话,那你就错了。Node.js 在实际运行代码之前,会把每一个.js 文件进行包装,变成如下的形式:
复制代码 代码如下:
(function(exports, require, module, __dirname, __filename) {
// 主业务代码
});
这样做有什么好处?我们都知道文章开始的时候就说了,JavaScript中能形成作用域的有函数的调用、with语句和全局作用域。而我们也知道,被定义在全局作用域的对象,很有可能是会一直存活到进程退出的,如果是一个很大的对象,那就麻烦了。比如有的人喜欢在JavaScript中做模版渲染:
复制代码 代码如下:
<?php
$db = mysqli_connect(server, user, password, 'myapp');
$topics = mysqli_query($db, "SELECT * FROM topics;");
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>你是猴子请来的逗比么?</title>
</head>
<body>
<ul></ul>
<script type="text/tmpl">
<li>
<h1><%=title%></h1>
<p><%=content%></p>
</li>
</script>
<script type="text/javascript">
var data = <?php echo json_encode($topics); ?>;
var topicTmpl = document.querySelector('#topic-tmpl').innerHTML;
var render = function(tmlp, view) {
var complied = tmlp
.replace(/\n/g, '\\n')
.replace(/<%=([\s\S]+?)%>/g, function(match, code) {
return '" + escape(' + code + ') + "';
});
complied = [
'var res = "";',
'with (view || {}) {',
'res = "' + complied + '";',
'}',
'return res;'
].join('\n');
var fn = new Function('view', complied);
return fn(view);
};
var topics = document.querySelector('#topics');
function init()
data.forEach(function(topic) {
topics.innerHTML += render(topicTmpl, topic);
});
}
init();
</script>
</body>
</html>
这种代码在新手的作品中经常能看得到,这里存在什么问题呢?如果在从数据库中获取到的数据的量是非常大的话,前端完成模板渲染以后,data变量便被闲置在一边。可因为这个变量是被定义在全局作用域中的,所以JavaScript引擎不会将其回收销毁。如此该变量就会一直存在于老生代堆内存中,直到页面被关闭。
可是如果我们作出一些很简单的修改,在逻辑代码外包装一层函数,这样效果就大不同了。当UI渲染完成之后,代码对data的引用也就随之解除,而在最外层函数执行完毕时,JavaScript引擎就开始对其中的对象进行检查,data也就可以随之被回收。
3.2 绝对不要定义全局变量
我们刚才也谈到了,当一个变量被定义在全局作用域中,默认情况下JavaScript 引擎就不会将其回收销毁。如此该变量就会一直存在于老生代堆内存中,直到页面被关闭。
那么我们就一直遵循一个原则:绝对不要使用全局变量。虽然全局变量在开发中确实很省事,但是全局变量所导致的问题远比其所带来的方便更严重。
使变量不易被回收;
1.多人协作时容易产生混淆;
2.在作用域链中容易被干扰。
3.配合上面的包装函数,我们也可以通过包装函数来处理『全局变量』。
3.3 手工解除变量引用
如果在业务代码中,一个变量已经确切是不再需要了,那么就可以手工解除变量引用,以使其被回收。
复制代码 代码如下:
var data = { /* some big data */ };
// blah blah blah
data = null;
3.4 善用回调
除了使用闭包进行内部变量访问,我们还可以使用现在十分流行的回调函数来进行业务处理。
复制代码 代码如下:
function getData(callback) {
var data = 'some big data';
callback(null, data);
}
getData(function(err, data) {
console.log(data);