这里有新鲜出炉的 Javascript 教程,程序狗速度看过来!
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
本文主要介绍了 javascript 立即执行函数表达式 IIFE 的相关知识。具有很好的参考价值,下面跟着小编一起来看下吧
一、IIFE 解释
全拼 Imdiately Invoked Function Expression,立即执行的函数表达式。
像如下的代码所示,就是一个匿名立即执行函数:
- (function(window, undefined){
- // 代码...
- })(window);
二、括号的意义
2.1 包住 function(){} 的括号的意义
这个括号的目的,是为了把 function(){} 转化为表达式。像一些库的源码,喜欢用如下方式代替:
- ~
- function() {
- // 代码...
- } ();
或者这种方式:
- +
- function() {
- // 代码...
- } ();
其实,作用都一样,都是把 function(){} 转化成一个可执行的表达式,方便执行。
如果去掉该括号,则会报错。因为单纯的 function(){} 不是可执行的表达式,会直接报错。如下图:
2.1 第二个括号的意义
理解了第一个括号的意义,第二个括号就很简单了,就是执行表达式了。
三、参数的意义
以这段代码为例子,讲解参数
- var wall = {};
- (function(window, WALL, undefined){
- })(window, wall);
参数分为形参和实参。
function(window, WALL, undefined)三个参数为形参,第二个括号 (window, wall) 的两个参数为实参。
也即可以理解为 window == window,wall == WALL。
2.1 普通形参
普通形参是指由 window 和 wall 这样的实际变量传入指定,可以为任何类型的变量。一个形参就对应一个实参
2.2 特殊形参 undefined
为什么形参要多写一个 undefined,这是一个很有趣的话题。
可以知道这个示例,实参只有两个,而形参有三个。所以在函数执行的时候,形参 undefined 会默认赋值为 undefined。
形参 undefined 的作用如下:
2.2.1 防止特殊值 undefined 被恶意代码篡改。
IE6 等低版本浏览器,undefined 是支持被修改的。而这个特殊值被修改后,像以下这种判断就失效了。
- if(wall == undefined){
- // 代码...
- }
所以,这里多加一个形参的目的就是为了防止这种情况发生。只要在这个 IIFE 作用域内,undefined 就能够正常获取到。
2.2.2 压缩代码可以压缩 undefined
因为 undefined 作为形参,像 YUI compressor 这种类型的代码压缩工具,可以将其相关的值进行压缩,减小文件的体积。
四、写法解析
4.1 普通写法
- var wall = {}; // 声明定义一个命名空间wall
- // 定义方法
- (function(window, WALL, undefined){
- // 给wall命名空间绑定方法say
- WALL.say = function(){
- console.log('hello');
- };
- })(window, wall);
- (function(window, WALL, undefined){
- // 给wall命名空间绑定方法 whoIam
- WALL.whoIam = function(){
- console.log('wall');
- };
- })(window, wall);
- // 调用
- wall.say();
- wall.whoIam();
先定义一个命名空间,然后再给这个命名空间加东西。这是最普遍的写法,也是最好理解的。
不足的地方就是必须先声明一个命名空间,然后才能执行相关的绑定代码。存在顺序加载的问题。
4.2 放大模式
- var wall = (function(window, WALL, undefined){
- if(typeof WALL == 'undefined'){
- WALL = {};
- }
- // 给wall命名空间绑定方法say
- WALL.say = function(){
- console.log('hello');
- }
- return WALL; // 返回引用
- })(window, wall);
- var wall = (function(window, WALL, undefined){
- if(typeof WALL == 'undefined'){
- WALL = {};
- }
- // 给wall命名空间绑定方法 whoIam
- WALL.whoIam = function(){
- console.log('wall');
- }
- return WALL; // 返回引用
- })(window, wall);
- // 调用
- wall.say();
- wall.whoIam();
放大模式的好处就是,可以不用考虑代码加载的先后顺序。
因为 js 允许 wall 变量进行重复 var 声明,所以这段代码是可以执行的。
我可以把 IIFE 函数拆分成多个文件进行加载,而不会出现普通写法需要注意的问题。
需要注意的点:
1.IIFE 的头部,都要先进行检查命名空间是否已经实例化,如果还没实例化,则进行实例化。
2.IIFE 的尾部,都要 return 命名空间的引用,使后续代码能够得到最新的 wall 命名空间内容。
4.3 宽放大模式
- (function(window, WALL, undefined){
- // 给wall命名空间绑定方法say
- WALL.say = function(){
- console.log('hello');
- }
- })(window, window.wall || (window.wall = {}));
- (function(window, WALL, undefined){
- // 给wall命名空间绑定方法 whoIam
- WALL.whoIam = function(){
- console.log('wall');
- }
- })(window, window.wall || (window.wall = {}));
- // 调用
- wall.say();
- wall.whoIam();
宽放大模式的重点注意的地方:就是在实参部分的 window.wall || (window.wall = {})。
用 || 运算符进行取巧。
如果 window.wall 是已经实例化的,非 not defined。则直接返回 window.wall 的引用,赋值给形参 WALL。不会执行 || 运算符后面的内容。
如果 window.wall 还未实例化,则进行实例化。这里要注意的点是实例化是一个赋值操作,需要用括号包起来,变成表达式去执行,才不会报错。
表达式 (window.wall = {}) 执行完毕后,会返回新对象 window.wall 的引用。
宽放大模式的好处:是可以切割成多个文件进行加载,而不必考虑文件加载的先后顺序,不存在强耦合关系。
当然,如果 IIFE 里面的方法互相引用,还是存在加载依赖的问题。这个问题可以用加载器 Require.js 等工具解决,这里就不讨论了。
五、分文件加载 IIFE 要注意的点
- ;(function(window, WALL, undefined){
- // 给wall命名空间绑定方法say
- WALL.say = function(){
- console.log('hello');
- }
- })(window, window.wall || (window.wall = {}));
眼尖的已经看出区别了,就是文件开始的地方,先写上分号;。
这样,多个文件合并的时候,才不会出现收尾相接,代码出现错乱的问题。比如下面这种情况:
- // a.js 文件
- wall.log()
- // b.js 文件
- (function(window, WALL, undefined){
- // 给wall命名空间绑定方法say
- WALL.say = function(){
- console.log('hello');
- }
- })(window, window.wall || (window.wall = {}));
由于 a.js 文件的 wall.log() 少写了分号,跟 b.js 文件合并后,js 就会认为'wall.log()(...)'是需要这么执行的,结果代码就报错了。
觉得不错的,可以关注模块化这个系列的文章,容我后续码字,敬请期待!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持 phperz!
来源: http://www.phperz.com/article/17/0621/327712.html