seven
一:立即执行函数(IIFE)
1.1 什么是立即执行函数?
中文:立即执行函数(直译:立即调用函数表达式) 英文简写:IIFE 英文全称:immediately invoked function expression 功能性称呼:初始化函数 当然,初始化函数不一定是立即执行函数,但是最好使用立即执行函数。
MDN: IIFE(立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数。
个人理解: 立即执行函数,按照字面理解,就是一个函数,会立即执行,那是什么时候会立即执行呢?按照MDN的解释,就是定义时。
立即执行函数特点:
- 每次加载时,只执行一次;
- 自动执行,无需调用;
- 执行完成就立即销毁。
1.2 它的作用?
作用:
- 创建独立的作用域,让外部无法访问该作用域内部的变量,从而避免变量污染;
- 初始化全局变量。
需求:当一个页面加载完成后,接受两个参数,同时每次页面加载完成后,自动执行求得两个参数的和,然后在销毁。
尝试使用常规函数来完成上述需求
function add(a, b){ return a + b; } var result = add(1, 2); result();
- 传入两个参数 ✔
- 加载完成后 ✔
- 两个参数的和 ✔
- 自动执行 x
- 销毁 x
可以看出,实际上还是没有完成需求
使用立即执行函数完成上述需求
var result = (function(a,b){ return a + b; }(1, 2));
- 传入两个参数 ✔
- 加载完成后 ✔
- 两个参数的和 ✔
- 自动执行 ✔
- 销毁 ✔
完美按需完成
1.3 三种写法
常规写法
W3C建议写法
特殊写法
//常规写法
(function(){})();
//W3C规范写法
(function(){}());
//特殊写法,前面加 + - ! ~ && ||
+ (function(){}());
- (function(){}());
! (function(){}());
~ (function(){}());
true && (function(){}());
false || (function(){}());
1.4 立即执行函数细节
1.4.1 在立即执行函数种函数名是否可以不写?
- 可以不写;
- 因为立即执行函数本身就是立即执行完后就会立即销毁,所以写与不写,无关紧要。
验证上述内容
var testing = (function verify(){
var a = 1;
var b = 2;
return a + b;
}());
console.log(testing);
//Print Result:3
console.log(testing.name)
//Print Result:undefined
//由此可以得知,函数名已经被忽略,同时联想到函数表达式,是可以打印出函数名的。
1.4.2 是否可以传参?
- 可以;
- 立即执行函数,跟常规函数比较其它,就是多了,自动执行以及执行完后立即销毁。
验证上述内容
var result = (function(a,b){
return a + b;
}(1, 2));
console.log(result);
//Print Result:3
1.4.3 ()---小括号分析
立即执行函数之小括号延深: 1、一定是表达式才能被执行符号执行; 2、() --- 被小括号包裹的,不管是什么内容,一律是表达式;
改造函数表达式,后面跟着 () 。就立即执行该函数,然后在赋值给变量名。这个技巧了解就行,感觉没什么作用。
//正常的函数表达式
var testing = function test(){
console.log(1 + 2);
};
testing();
console.log(testing.name);
//Print result:3
//Print result:test
//函数表达式跟着函数执行符 ()
var testing = function test(){
console.log(1 + 2);
}();
console.log(testing.name)
//Print result:3
//Print result:Uncaught TypeError: Cannot read properties of undefined (reading 'name')
//函数表达式跟着函数执行符 ()
//使用 return 返回
var testing = function test(){
return 1 + 2;
}();
console.log(testing);
console.log(testing.name);
//Print result:3
//Print result:undefined
1.5 立即执行函数之面试题
问:以下这段代码是否会报错?如果报错,为什么?如果不报错又为什么?
答:不报错。 不报错的原因如下:
1、可以将这段代码分为两个部分来看;
2、第一部分为,test 函数声明;
3、第二部分为,表达式;
4、这两个部分组合在一起,无任何语法错误,所以不会报错;
5、如果,把表达式中的 6 给去除,则 JavaScript 引擎就会认为此时的 () 为函数执行符号,就会报语法错误。
function test(a){
}(6);
二:闭包深入
2.1 代码分析
代码实例:
//闭包现象
function test(){ //声明一个 test 函数
var arr = []; //创建一个空数组,也可以说初始化数组,这样感觉有B格。
for(var i = 0; i < 10; i++){ //for循环,这里会产生变量污染
arr[i] = function(){ //在调用这个匿名函数时,在页面写入 i 的值
document.write(i + ' ');
}
}
return arr; //在当前环境下返回数组到全局中
}
var myArr = test(); //函数赋值给一个全局变量
for(var j = 0; j < 10; j++){
myArr[j](); //调用执行函数
}
问:为什么结果是 10 个 10 ?
答:因为闭包的产生,运行过程展开如下:
1、test 函数执行时;
2、在for循环体内,按照其规则
i < 10,依次从 0 - 9 的顺序,赋给 arr 数组下标 一个匿名函数体,且匿名函数并没有在整个函数内调用执行;3、当局部便变量 i 的值为 9 ,进入且完成循环跳出时,其值在后置型自增作用下,已经更新为 10,并存储在 test 函数的 AO 中;
4、通过
return指令,将 arr 数组返回到全局环境中去,也就是 test 函数赋值给 myArr 全局变量 ;5、在 test 函数赋值给 myArr 变量时,就已经结束了 test 函数的调用执行,根据此时的执行期上下文就已产生了闭包;
6、闭包的产生,就意为着 test 函数中的作用域链的 AO 不销毁,并被匿名函数关联起来,只是断开了与 test 函数作用域链映射关系;
7、此时的局部变量 i 就包含在当前的 AO 中;
8、在第二个for循环中,当调用执行匿名函数时,匿名函数在自身的 AO 中没有查到变量 i ,则向上找,在 test 函数 AO 中找到变量 i 并调用;
9、匿名函数执行结束后,销毁自身的 AO,并一直钩住 GO 和 test 函数的 AO 不释放。
2.2 改造上面的代码
改造 2.1 章节代码,在页面上呈现 0-9 的自然数。 三种方式来改造上面的代码。
改造一:立即执行函数;
个人理解 对比最初代码,以下代码,是把匿名函数直接修改为立即执行函数,同时也把跟原匿名函数相关的代码全部踢出。
function test(){
for(var i = 0; i < 10; i++){
(function(){
document.write(i + ' ');
}());
}
}
test();
改造二:传参方式1;
个人理解 改造方式:通过传参的方式,在匿名函数调用执行时,直接用实参传递给形参的方式来展示 0-9 的自然数。 说直白点,就是把变量
j的值传入匿名函数内,在写入到HTML中然后在显示出来。
function test(){
var arr = [];
for(var i = 0; i < 10; i++){
arr[i] = function(num){
document.write(num + ' ');
}
}
return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++){
myArr[j](j);
}
改造三:传参方式2;
听小野老师说,这是企业开发中最最常用的一种立即执行函数传参方式。
个人理解
改造方式:万变不离其宗,还是使用立即执行函数以及对匿名函数的改造。如果要具体展开说,有太多要说的,抓重点就是 在立即执行函数中使用每次for循环中变量
i的值为实参值,进行实参与形参的统一,从而隔绝(相对于 2.1 代码而言)了闭包对匿名函数的影响,其它的就无关重点,这里就不写了。
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
(function(j){
arr[j] = function(){
document.write( j + ' ');
}
}(i));
}
return arr;
}
var myArr = test();
for (var j = 0; j < 10; j++) {
myArr[j]();
}
2.3 改造下面代码
需求:改造以下代码,点击
li后,弹出其数组下标。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var oli = document.querySelectorAll('li');
for(var i = 0; i < oli.length; i++){
oli[i].onclick = function(){
alert(i);
}
}
</script>
</body>
</html>
思路:使用立即执行函数,来去除闭包的影响。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var oli = document.querySelectorAll('li');
for (var i = 0; i < oli.length; i++) {
(function (click) {
oli[click].onclick = function () {
alert(click);
}
}(i));
}
</script>
</body>
</html>
三:逗号运算符
逗号运算符,只返回逗号后面的值。
逗号运算符,不只这一点点东西,先简单了解,后续在说。
console.log(5, 6);
//Print Result:6
//逗号的应用
var fn =(
function test1(){
return 1;
},
function test2(){
return '2';
}
)();
console.log(typeof(fn));
//Print Result:string
记录一下,后期在回来弄
for(let i = 0, j = 10; i <= 10; i++, j--) {
console.log(`i: ${i}, j: ${j}`);
}