说到立即执行函数大家可能一点都不陌生,但是当真正的深入我们会发现处处采坑,那我们接下来就从简单的开始捋一捋。
一般来说,立即执行函数表现形式有两种常见的形式,分别为:
1 | // 常见形式一(括号在外面): |
接下来我们再来看看一些其他不太常见的写法:
1 | void function() {} (); |
有时候遇到的时候大家需要注意一下,不要被这种唬人的形式给吓到了,下面我们需要注意一下立即函数错误的一种形式:function (){}();
在解释为什么这样写是错误时,我再给大家说一下什么是函数表达式和函数声明:
1 | // 函数表达式 |
当我们使用function(){}()的形式时,解析器会认为function(){}是函数声明,由于函数声明式语句后面不能有括号,因此解析器会抛出一个语法错误。
但是当我们在函数声明语句之间添加一个括号,即”(function (){})”,此时解析器会认为这是函数表达式,因此在表达式后面添加一个括号,解析器不会抛出错误。
总结一下:在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。
关于常见的两种形式写法,我来解释一下为什么这么写是正确的:
1 | // 常见形式一(括号在外面): |
因为Javascript引擎看到function关键字之后,认为后面跟的是函数定义语句,不应该以圆括号结尾。其解决办法就是:让引擎知道圆括号前面的部分不是函数定义语句,而是一个表达式,这两种写法都是以圆括号开头,引擎就会认为后面跟的是一个表示式,而不是函数定义。
对于不太常见的形式,我们也是遵循这样的一个思路:通过符号的使用,使得解析器认为是函数表达式语句,从而实现立即函数。
下面我们先来看看两个例子:
例子1:
1 | var arr = []; |
例子2:
1 | var arr = []; |
看过上面两个例子之后,可能大家还有点疑惑,没关系,大家只需要记住一点就是:两个单独的数字永远不相等。
下面我们来看一个例子:
1 | var obj = { |
造成修改newObj中的name属性值时,导致obj中的name属性值也进行了一个修改的原因是:newObj对象获得的只是一个内存地址,即newObj和obj同时指向的是一个地址,导致其不是真正的一个拷贝过程。
当使用es6中的assign()方法时:
1 | var obj2 = { |
但是我们需要注意的一点是assign()方法是浅拷贝,并不是深拷贝。换句话来说就是:如果源对象某个属性的值是对象,那么目标对象拷贝的是这个对象的引用,我们来举一个例子:
1 | var obj1 = {a: {b: 1} }; |
如果对象的属性值不是一个对象的话,那么就不会影响该值,举一个例子:
1 | var obj1 = {a: 1}; |
assign()方法还有许多的用处,但是在这里我只列举它的一些用处,对此很感兴趣的同学可以去百度搜索更多关于它的一些内容和方法。
下面我们来看一个例子1:
1 | function fn() { |
Array.prototype.slice.call()能把类数组对象转化成数组,在这里如果不懂Array.prototype.slice.call()的机制的同学可以去网上百度一下,在这里我就不进行一个细说了。上面的代码利用函数中的arguments类数组对象获取传入函数的参数数组,所以输出数组[1,2,3,4]。
例子2:
1 | function fn () { |
例子3:
1 | var args = [1,2,3,4]; |
上面的代码利用了Array.prototype.push.call()方法向args数组中插入了5、6、7、8,并利用ES6中的延展操作符(…)将数组展开并传入到fn函数中,所以控制台打印出[1,2,3,4,5,6,7,8]。