一、立即执行函数
-
一定是表达式才能被执行符号执行;
-
让函数声明变成表达式的方法:在函数声明前加:+ - || && !
-
函数声明后面不能跟立即执行符号(),例如function test(){}(),这样会报语法错误,function test(){}(1),这样不报错,但是也不执行,这样js引擎会认为这是一个函数声明+表达式,所以不报错;
-
立即执行函数在执行完就会销毁函数,不占用内存空间;
-
立即执行函数会自动忽略表达式里函数的名称;
1. 一定是表达式才能被执行符号执行
/*
* 立即执行函数的写法:
* 在立即执行函数前面写分号“;”的原因:js压缩之后变成一行,不同的立即执行函数直接要用“;”隔开防止
* 报错,在立即执行函数后面写也是可以的,只是在前面更容易记得写
*
**/
//方法1:
;(function(){
})()
//方法2:也是w3c推荐的写法
;(function(){
}())
2. 让函数声明变成表达式的方法:在函数声明前加:+ - || && !
// 1. 函数声明前加 +
+function test1(){
console.log("函数声明前加+");
}();
// 2. 函数声明前加 -
-function test2(){
console.log("函数声明前加-");
}();
// 3. 函数声明前加 ||
false || function test3(){
console.log("函数声明前加||");
}();
// 4. 函数声明前加 &&
true && function test4(){
console.log("函数声明前加&&");
}();
// 2. 函数声明前加 !
!function test5(){
console.log("函数声明前加!");
}();
3. 函数声明后面不能跟立即执行符号(),例如function test(){}(),这样会报语法错误,function test(){}(1),这样不报错,但是也不执行,这样js引擎会认为这是一个函数声明+表达式,所以不报错;
// 1. 函数声明后面加立即执行符号()--报错:Uncaught SyntaxError: Unexpected token ')'
function test(){
console.log("我想立即执行。")
}();
/**
* 2.函数声明后面加立即执行符号(),立即执行符号有参数1,不报错,但是也不执行;
* 原因:js引擎会认为这是一个函数声明+表达式,所以不报错
*/
function test(){
console.log("我想立即执行。")
}(1);
4. 立即执行函数传参,获取立即执行函数里面的返回值;
var sum = (function(a,b){
var c = a + b;
console.log(c); //3
return c;
})(1,2);
console.log(sum); //3
二、闭包深入
1. 闭包案例分析1:
function test() {
var arr = [];
//分析:循环里,只有当i=10时,循环才结束,所以无论传什么进来,它都只会打印最后一个值10
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i + ' ');
}
}
return arr;
}
var arr = test();
for (var j = 0; j < 10; j++) {
arr[j](); //无论j是什么,上面的循环都是走到10才结束,只打印循环终止的最后一个值10
}
//打印结果: 10个10
2. 闭包案例分析2:
function test() {
var arr = [];
for (var i = 0; i < 10; i++) {
// 立即执行函数,循环一次,执行一次
(function (j) {
arr[j] = function () {
console.log(j + ' ');
}
})(i) //i的值为0-9
}
return arr;
}
var arr = test();
for (var j = 0; j < 10; j++) {
arr[j](); //
}
//打印结果: 0-9
3. 拓展:通过上面的案例,可以给列表绑定方法
<body>
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
//js代码
var oLi = document.querySelectorAll('li'); //获取li选择器
var oLength = oLi.length; //获取li的个数
for(var i = 0; i < oLength; i++){
;(function(j){
//给每个li都绑定方法
oLi[j].onclick = function(){
console.log(j);
}
})(i)
}
三、逗号运算符:只返回最后一位
案例1:
var num = (1,2);
console.log(num) // 2
案例2:
var fn = (
function test1() {
return 1;
},
function test2() {
return '2';
}
)();
console.log(typeof(fn));
// 打印结果: 'string'
/**
* 1. 立即函数里面有两个返回值1和'2',但是逗号运算符只返回最后一位,所以fn = '2';
* 2. typeof('2') -> string
*/
四、案例合集
1. 面试题:
var a = 10;
if (function b(){}) { //()表达式,忽略函数名
a += typeof(b); // a = a + typeof(b) = 10 + typeof(undefined);
}
console.log(a);
//打印结果:10undefined
/**
* 1. ()表达式,忽略函数名,所以b的值为undefined;
* 2. typeof(undefined)为undefined
*/
2. 闭包实现累加器
function sum(){
var n = 0;
function add(){
n++;
console.log(n);
}
return add;
}
var add = sum(); //将sum()里n的值保存到全局
add(); //1
add(); //2
3. 一个班级:学生名字保存在一个数组里;两个方法写在函数中的一个对象中;第一个方法加入班级; 第二个方法离开班级; 每次加入或离开,都需要打印新的学生名单。
function student(){
var students = ['may','mike','lily'];
var operations = {
join:function(name){
students.push(name);
console.log(students);
},
leave:function(name){
/**
* 1. indexOf 方法返回一个整数值,返回第一次出现的指定子字符串在此字符串中的索引。
* 2. 如果没有找到子字符串,则返回-1
* 3. splice(index,1):如果index为负数,则是从右边起的第几位
*/
// 方法1:使用indexOf实现
var sIdx = students.indexOf(name); //sIdx不为-1即存在
//判断要删除的学生是否在数组里
if(sIdx != -1){
students.splice(sIdx,1); //从sIdx位开始,删除1位
}
//方法2:使用for循环实现
for(var i = 0; i < students.length; i++){
var item = students[i]; // 把每一项缓存到item中,优化for循环的性能
if(item === name){
students.splice(i,1); //从i位开始,删除1位
}
}
console.log(students);
}
}
return operations;
}
var student = student();
student.join('jon'); //["may", "mike", "lily", "jon"]
student.leave('may'); //["mike", "lily", "jon"]