目录
🆙【前文回顾】👉 js之正则相关函数以及正则对象_02
一. 回顾函数
1. 什么是函数
程序中专门保存一段可反复使用的代码段的程序结构,再起一个名字。
2. 为何使用函数
重用
3. 何时使用函数
今后只要一段代码段可能被反复使用,都要定义在函数中,反复调用函数!
4. 如何使用函数
如何使用函数: 2步
(1). 定义函数:
function 函数名(形参变量列表){
函数体;
return 返回值;
}
(2). 调用函数:
var 变量=函数名(实参值列表)
5. 什么是返回值
(1). 什么是返回值: 一个函数的执行结果。
(2). 何时使用返回值: 如果函数外的调用者需要获得函数的执行结果时,都要定义返回值。
(3). 调用有返回值的函数时,都要用变量接住函数的返回值。
6. 什么是参数
(1). 什么是参数: 专门保存函数执行时必须的数据的变量
(2). 为什么: 为了让函数变的更灵活
(3). 何时: 如果一个函数执行时必须某些数据才能正常执行。但是这些数据又有可能发生变化,不是固定的!都要用参数变量来为将来可能使用的数据,临时占位。
(4). 如何: 2步:
a. 定义函数时:
function 函数名(形参变量列表){
... ...
}
b. 调用函数时:
函数名(实参值列表)
7. 示例: 定义一个函数
示例: 定义一个函数可以根据传入的食材不同做出各种西红柿鸡蛋面 ⏬
4_function.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>
<script>
//想定义函数做西红柿鸡蛋面
// 形参变量列表
//要做西红柿鸡蛋面至少需要三种食材:蛋,菜,面
function cook(dan,cai,mian){
//因为食材由外部通过形参传入来决定
//所以函数中不能再写死食材
console.log(`炒${dan}`)
console.log(`炒${cai}`)
console.log(`${dan}和${cai}一起炒`)
console.log(`煮${mian}`)
return `香喷喷的${cai}${dan}${mian}`
}
//周一想吃西红柿鸡蛋面
//因为cook()函数要求必须提供食材,才能做饭
//所以调用cook函数时,必须使用实参值方式传入食材
// 实参值列表
//变量wan接住的就是cook函数调用后返回的返回值
var wan=cook("鸡蛋","西红柿","面条");
console.log(`得到一碗:${wan}`);
//周二没西红柿了,有黄瓜,没鸡蛋了,有辣条,没面条了有粉丝
var wan=cook("辣条","黄瓜","粉丝");
console.log(`得到一碗:${wan}`);
//周三没西红柿了,有榨菜,没鸡蛋了,有豆腐卤,没面条了有螺蛳粉
var wan=cook("豆腐卤","榨菜","螺蛳粉");
console.log(`得到一碗:${wan}`);
</script>
</body>
</html>
运行结果:
炒鸡蛋
炒西红柿
鸡蛋和西红柿一起炒
煮面条
得到一碗:香喷喷的西红柿鸡蛋面条
炒辣条
炒黄瓜
辣条和黄瓜一起炒
煮粉丝
得到一碗:香喷喷的黄瓜辣条粉丝
炒豆腐卤
炒榨菜
豆腐卤和榨菜一起炒
煮螺蛳粉
得到一碗:香喷喷的榨菜豆腐卤螺蛳粉
8. 函数的原理
(1). 定义函数时,其实只是在内存中创建一个函数对象,保存一段代码段而已,并不会立刻执行函数中的代码段。
(2). 只有使用"函数名()"专门的语法才能找到函数对象,并执行其中的代码段
a. js引擎会在内存中找到函数对象的存储位置
b. 将调用函数时的实参值列表中的每个实参值临时依次传给函数对象中每个形参变量
c. js引擎会依次读取函数体中的每行代码执行规定的操作
d. 所有代码执行完,函数返回执行结果交给js引擎,带回原程序位置,继续使用函数的返回值。
二. 创建函数: 3种方式
1. 用声明方式
(1). 如何:
function 函数名(形参变量列表){
函数体
return 返回值
}
(2). 问题: 会被声明提前,打乱程序正常的执行顺序
2. 用赋值方式
(1). 如何:
var 函数名=function(形参变量列表){
函数体
return 返回值
}
(2). 优点: 不会被声明提前!
(3). 其实,出了名的框架或大师级的人物定义函数时都首选用赋值方式创建!
3. 用new: ——几乎不用!
(1). 如何:
var 函数名=new Function("形参1","形参2",...,"函数体;return 返回值");
(2). 揭示:
a. js中,函数其实也是一个对象。只不过对象的内容中保存的不是数据,而是代码段而已。
b. 函数名其实只是一个普通的变量
c. 所有的function,其实在运行时都会被自动翻译为new Function()。每个函数对象都有唯一的地址值。函数对象的地址都保存在函数名变量中
(3). 总结: 我们看到的函数的演变过程:
a. function fun(){ console.log(1) } —— 先提前,再翻译
↓
先被声明提前
↓
被自动翻译为: var fun=new Function("console.log(1)")
↓
进一步被翻译为2句话:
var fun;
fun=new Function("console.log(1)")
b. var fun=function(){ console.log(1) } ——不提前直接翻译
↓
被直接就地翻译为:
var fun;
fun=new Function("console.log(1)");
4. 示例: 使用三种方式创建函数
5_hoist.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>
<script>
console.log(a);//undefined
var a=10;
console.log(a);//10
// function fun(){console.log(1)}
// fun();//2
// function fun(){console.log(2)}
// fun();//2
// var fun=function(){console.log(1)}
// fun();//1
// var fun=function(){console.log(2)}
// fun();//2
var fun=new Function("console.log(1)");
fun();//1
var fun=new Function("console.log(2)");
fun();//2
</script>
</body>
</html>
运行结果:
undefined
10
1
2
三. 重载(overload)
1. 问题
有的时候,一件事,可能根据传入的参数不同,执行不同的操作!但是,如果给每一种操作都专门定义一个函数,还专门起一个不同的函数名,代码会很繁琐!
2. 解决
重载!
3. 什么是重载
(其它语言中)相同函数名不同参数列表的多个函数,可在调用时自动根据传入的实参值不同,自动匹配对应的函数版本执行其中的内容。
🙋♂️ 补:重载的定义是指函数的方法名相同,但参数不同
重载是面向对象语言里很重要的一个特性,JS中没有真正的重载,是模拟出来的(因为js是基于对象的编程语言,不是纯面向对象的,它没有真正的多态:如继承、重载、重写)
所谓重载,就是一组相同的函数名,有不同个数的参数,在使用时调用一个函数名,传入不同参数,根据你的参数个数,来决定使用不同的函数!也就是说,函数名相同,函数的参数列表不同(包括参数个数和参数类型),根据传入参数的不同去执行不同的操作。
但是我们知道js中是没有重载的,因为在JavaScript中,同一个作用域,出现两个名字一样的函数,后定义的函数会覆盖前面的同名函数,所以 JavaScript 没有真正意义的重载。但是我们又想实现函数重载该怎么办呢?借助于arguments对象来实现重载
4. 为什么使用重载
减少函数名的个数,减轻调用者的负担!(程序员只需要记住一个函数名即可)
5. 何时使用重载
一件事,需要根据不同的参数值,有选择的执行不同的逻辑时,都用重载!(就是说只要将来的实参值不确定有几个时,需要用重载)
6. 存在问题
因为js中不允许多个同名函数同时存在,所以,如果在js中定义多个同名函数,最后只有最后一个能留下来!如果将来调用时,也只能调用最后一个!——js不支持传统的重载语法!
7. 如何解决
借助于arguments对象来实现重载
8. 什么是arguments
定义:每个函数中都自带的、自动收集保存本次传入函数的所有实参值的类数组对象
(1). 每个函数中都自带的——不用自己创建,可直接用
(2). 自动收集保存本次传入函数的所有实参值的——内容
(3). 类数组对象: 长的像数组的对象
a. 像数组: 1). 下标, 2).length 3)遍历
b. 和数组的区别: 本质区别: 类型不同
1). 数组是Array家的孩子,可以使用数组家的函数
2). 类数组对象是Object家的孩子,不能使用数组家的函数。
9. 何时使用arguments
今后,只要一个函数,不确定将来会传入多少个实参值时,都可用arguments接住所有的实参值!
10. 如何使用arguments变通实现重载
(1). 只定义一个函数,且不要定义形参变量!
(2). 函数内部arguments会自动接住所有实参值
(3). 在函数内根据arguments的长度或元素值的不同,动态判断选择不同的逻辑执行!
11. 示例: 使用重载实现三种支付方式
6_overload.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>
<script>
//定义三个函数,分别可以执行3种付款方式
//第一个函数: 手机支付
//第二个函数: 现金支付
//第三个函数: 刷卡支付
// pay("6553",'123456');
function pay( ){
// arguments{"6553",'123456'}.length
// 0 1
//如果arguments的长度==0,说明没传实参,就手机支付
if(arguments.length==0){
console.log(`手机支付...`)
}else if(arguments.length==1){//否则如果arguments的长度==1,说明传了一个实参,就现金支付
console.log(`现金支付...收您${arguments[0]}元`)
}else{//否则都执行刷卡支付
console.log(`刷卡支付...从您卡号${arguments[0]}的银行卡,扣款成功!`)
}
}
pay();
pay(100);
pay("6553",'123456');
</script>
</body>
</html>
运行结果:
手机支付...
现金支付...收您100元
刷卡支付...从您卡号6553的银行卡,扣款成功!
四. 匿名函数
1. 存在问题
有些函数只使用一次,如果也起函数名,不但麻烦,而且浪费内存!
2. 如何解决
使用匿名函数
3. 什么是匿名函数
定义函数时,不指定函数名的函数
4. 何时使用匿名函数
只要一个函数,只使用一次时,不会反复使用时,都必须用匿名函数。包含两种情况(使用场景):
(1). 回调函数: 我们定义了函数,但是我们自己不调用,而是交给其他函数去调用!——(为什么回调函数要用匿名函数)节约内存!——(原因是)用完就立刻释放!
(2). 匿名函数自调: 定义一个匿名函数后,立刻调用这个函数执行一次!调用完,立刻释放!
扩展:立即执行函数的两种常见形式
( function(){…} )()和( function (){…} () ),一个是一个匿名函数包裹在一个括号运算符中,后面再跟一个小括号,另一个是一个匿名函数后面跟一个小括号,然后整个包裹在一个括号运算符中,这两种写法是等价的。要想立即执行函数能做到立即执行,要注意两点,一是函数体后面要有小括号(),二是函数体必须是函数表达式而不能是函数声明
a. 问题: 全局变量有明显的缺点,就是全局污染。因为全局变量随处可修改,极易被污染。——所以,在公司中严格禁止使用全局变量的!如何保证程序的功能不变,但是还不产生全局变量?
b. 解决: 将整段代码放入一个函数中,所有变量都变成局部变量了!
c. 问题: 如果使用有名称的函数包裹功能,虽然变量变成局部的(函数调用后,变量自动释放,不会遗留在内存中),不会造成全局污染。但是,有名称的函数却遗留在内存中。
d. 解决: 匿名函数自调!
xxxxx;
(function(){
...
})();
强调: 匿名函数自调前后语法必须加;结尾!
c. 总结: 今后所有的js代码都要包裹在匿名函数自调中!(大师级人物都是这样写)
5. 示例: 使用匿名函数自调
示例: 使用匿名函数自调包裹功能代码避免使用全局变量 ⏬
7_anonyFun.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>
<script>
//获得当前系统时间
//var fun1=new Function(...);
(function(){
//变成了局部变量,不会保存在window中,不会造成全局污染
var start=new Date();
alert(`开始加载页面内容, 在${start.toLocaleString()}`);
// })();
// (function(){
//变成了局部变量,不会保存在window中,不会造成全局污染
var end=new Date();
alert(`页面内容加载完成,在${end.toLocaleString()}`);
})();
//JS中没有堆和栈。浏览器中,所有的全局变量和函数都是保存在一个巨大的window对象中的。
console.log(window);
//前边功能都执行完了,但是两个变量却遗留在内存window中——全局污染,浪费内存!
console.log(window.start);
console.log(window.end);
console.log(window.fun1);
console.log(window.fun2);
</script>
</body>
</html>
运行结果:
Window
undefined
undefined
undefined
undefined
注意:2个函数可以各自包裹在各自的匿名函数自调中,也可以同时包裹在一个匿名函数自调中,不会影响执行结果
扩展:全局变量示例——全局污染,浪费内存
<!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>
<script>
//获得当前系统时间
var start=new Date();
alert(`开始加载页面内容, 在${start.toLocaleString()}`);
var end=new Date();
alert(`页面内容加载完成,在${end.toLocaleString()}`);
//JS中没有堆和栈。浏览器中,所有的全局变量和函数都是保存在一个巨大的window对象中的。
console.log(window);//全局变量stat和end驻留在window中
//前边功能都执行完了,但是两个变量却遗留在内存window中——全局污染,浪费内存!
console.log(window.start);
console.log(window.end);
</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>
<script>
//获得当前系统时间
//下面的函数fun1会被翻译为:var fun1=new Function(...);
function fun1(){
//变成了局部变量,不会保存在window中,不会造成全局污染
var start=new Date();
alert(`开始加载页面内容, 在${start.toLocaleString()}`);
}
fun1();
function fun2(){
//变成了局部变量,不会保存在window中,不会造成全局污染
var end=new Date();
alert(`页面内容加载完成,在${end.toLocaleString()}`);
};
fun2();
//JS中没有堆和栈。浏览器中,所有的全局变量和函数都是保存在一个巨大的window对象中的。
console.log(window);//fun1和fun2驻留在window对象中
console.log(window.start);//undefined
console.log(window.end);//undefined
console.log(window.fun1);
console.log(window.fun2);
</script>
</body>
</html>
五. 作用域和作用域链
A:作用域(scope)
1. 什么是作用域(scope): 2个概念:
(1). 用途: 一个变量的可用范围
(2). 本质: 作用域其实是专门保存各种变量的对象。
2. 为什么会有作用域: 避免不同范围之间的变量互相干扰
3. js中包括: 2级:
(1). 全局作用域:
a. 什么是: 保存所有全局变量和全局函数的范围,称为全局作用域
b. 本质: 其实全局作用域也是一个对象: window对象(也称全局作用域对象)
其实我们所声明的所有全局变量和全局函数都保存在window对象中
注意:Windows对象是打开浏览器就自动创建的全局对象
c. 特点: 随处可用,可反复使用!
(2). 函数作用域:
a. 什么是: 专门保存所有仅函数内可用的局部变量的范围,称为函数作用域
b. 本质: 专门保存函数中局部变量的对象。见函数执行过程中的图示。
c. 特点: 仅函数内可用,不能反复使用!
d. 局部变量包括2种情况:
1). 必须在函数内var出来的!
2). 形参变量虽然不是var出来的,但是也算局部变量
4. 示例: 作用域简单案例: ⏬
8_scope.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>
<script>
var a=10;
function fun(){
var a=100;
a++
console.log(a);//101
}
fun();
console.log(a);//10
var b=10;
function fun(){
b=100;
b++
console.log(b);//101
}
fun();
console.log(b);//101
</script>
</body>
</html>
运行结果:
101
10
101
101
分析:函数在内存中是如何存储的
JS中规定:一个变量只能存一个值。所以变量是不能存函数的,因为函数太大了,有很多语句再加上返回值等,由此变量是不可能存储函数这么复杂的东西的,于是就出现了引用类型,变量存储的是函数对象的地址,进而变量与函数对象之间形成了一个引用的关系,当我们使用这个函数时,会先找到函数名,然后顺着地址找到这个函数对象所在的位置,执行函数对象里保存的代码,这也就是我们使用变量或是函数时,都会经历一个找的过程
5. 函数的执行过程:
(1). 创建一个函数对象,保存函数的内容。每个函数对象身上,其实都有一个"好友列表"。一般函数的好友列表有两个格子:
a. 离自己近的格子是空的,暂时预留
b. 离自己远的格子中保存着全局作用域对象window对象的引用
(2). 刚开始调用函数时,都会临时创建一个函数作用域的对象,保存函数的所有局部变量。并且将函数作用域对象的地址,保存到函数的"好友列表"中离函数近的格子里。
(3). 函数执行过程中,会依次读取函数中每一条js语句执行。执行语句过程中,会查找所需的变量使用:
a. 优先找离自己近的函数作用域对象中的局部变量使用
b. 除非函数作用域中找不到所需的变量,才被迫去window中查找全局变量使用
(4). 函数调用后,先释放本次临时创建的函数作用域对象。导致函数作用域对象中的所有局部变量跟着释放!所以,局部变量只能在函数内使用,出了函数不能用!且,局部变量都不可重用!(因为局部变量用完就会释放,下次再用需要重新创建)
B:作用域链(scopes)
作用域链: 保存一个函数所有可用的作用域对象的链式结构(好友列表)学名就叫作用域链。
1). 作用域链保存着一个函数可用的所有变量
2). 作用域链控制着变量的使用顺序。先局部后全局。
复习: 声明提前hoist
1. 什么是声明提前:
(1).在程序开始执行前
(2). js引擎会先扫描程序,查找2样东西:
a. var声明的变量: var 变量名
b. function 声明的函数: function 函数名( ... ){ ... }
(3). 将变量的声明和函数的声明提升到当前作用域的顶部集中创建
(4). 赋值留在原地
2. 问题: 打乱了程序正常的执行顺序!
💥 扩展:this判断—8种指向
this 8种指向: 判断this,一定不要看定义在哪儿!只看调用时!
1. obj.fun() this->obj
2. fun() 或 (function(){ ... })() 或 多数回调函数 或 定时器函数 this->window
3. new Fun() this->new正在创建的新对象
4. 类型名.prototype.共有方法=function(){ ... } this->将来谁调用指谁,同第一种情况
5. DOM或jq中事件处理函数中的this->当前正在触发事件的DOM元素对象
如果需要使用简化版函数,必须$(this)
6. 箭头函数中的this->箭头函数外部作用域中的this
7. jQuery.fn.自定义函数=function(){ ... } this->将来调用这个自定义函数的.前的jQuery子对象,不用再$(this)
8. new Vue()中methods中的函数中的this->当前new Vue()对象
💠 总结:知识点提炼
1. 只要验证字符串格式或查找、屏蔽敏感词时都要用正则
(1). 最简单的正则: 一个敏感词的原文
(2). 某一位字符上可能有多种备选字时用: [备选字列表]
(3). 如果[]中部分字符是连续的,可用: [x-x]
a. 一位小写字母: [a-z]
b. 一位大写字母: [A-Z]
c. 一位字母(大小写都行): [A-Za-z]
d. 一位字母或数字都行: [0-9A-Za-z]
e. 一位汉字: [\u4e00-\u9fa5]
(4). 预定义字符集:
a. \d 一位数字
b. \w 一位数字、字母或_
c. \s 空格、tab、换行等空字符
d. . 任意字符
(5). 如果规定一个字符集或子规则反复出现的次数时就用量词:
a. 有明确数量边界的量词:
1). {n} =n 必须n个,不能多也不能少
2). {n,m} n个<= <=m个
3). {n,} n个<= 多了不限
b. 没有明确数量边界的量词:
1). * 0个<= 可有可无,多了不限
2). ? 0个或1个 可有可无,最多一个
3). + 1个<= 至少一个,多个不限
(6). 两个规则中选其一匹配即可: 规则1|规则2
(7).希望将多个子规则分为一组先联合匹配,再和分组外的其他规则联合匹配:
(多个子规则)
(8). 匹配特殊位置: 3个
a. 字符串的开头位置: ^
b. 字符串的结尾位置: $
c. 英文句子中的单词的左右边界: \b
2. String家提供的正则相关函数: 3件事
(1). 查找敏感词: 4种情况
a. 查找一个固定的敏感词出现的位置:
var i=str.indexOf("敏感词")
// 如果找不到,返回-1
b. 用正则查找多种敏感词出现的位置:
var i=str.search(/正则/i)
// 如果找不到,返回-1
c. 查找敏感词的内容:
1). 查找第一个敏感词的内容和位置:
var arr=str.match(/正则/i)
// arr: [ 0:"敏感词内容", index:敏感词位置 ]
// 如果找不到返回null
2). 查找所有敏感词的内容,不关心位置:
var arr=str.match(/正则/ig)
// arr: [ 敏感词1, 敏感词2, ... ]
// 如果找不到返回null
d. 查找每个敏感词的内容和位置: reg.exec
补: js中所有数组底层本质都是关联数组(下标都为字符串) 1. 访问数组中元素值的标注写法: arr["下标"] 2. 简写: a. 如果下标为自定义字符串名称,可简写为: arr.自定义名称的下标 b. 如果下标为数字内容的字符串,可简写为: arr[数字下标] |
总结: 查找方法的返回值规律 1. 如果原函数返回的是下标位置i,如果找不到,都返回-1 2. 如果原函数返回的是一个数组arr或一个对象obj,如果找不到,都返回null 3. 如果原函数返回类数组对象,如果找不到返回空类数组对象: { length:0 } |
(2). 替换敏感词: 2种
a. 简单替换:
变量=str.replace(/正则/ig, "新值")
b. 高级替换:
变量=str.replace(/正则/ig, function(形参){
return 根据本次敏感词动态生成一个新值
})
c. 删除敏感词:
变量=str.replace(/正则/ig, "")
(3). 切割字符串:
a. 简单切割:
var arr=str.split("切割符")
b. 复杂切割:
var arr=str.split(/正则/i)
c. 打散字符串为字符数组:
var arr=str.split("")
3. RegExp对象:
(1). 创建正则表达式对象:
a. 如果正则是固定的:
var reg=/正则/ig
b. 如果正则需要动态生成:
var reg=new RegExp("正则",ig)
(2). 验证字符串格式:
var bool=reg.test(str)
reg必须同时前加^后加$
(3). 既查找每个关键词的内容又查找每个关键词的位置: (待续)
do{
var arr=reg.exec(str);
if(arr!=null){
获得本次找到的敏感词的内容(arr[0])和位置(arr.index)
}
}while(arr!=null);
4. 函数: ⏬
(1). 创建函数三种方式:
a. function 函数名(形参列表){ 函数体; return 返回值 } //会被声明提前,不好
b. var 函数名=function(形参列表){ 函数体; return 返回值 }//不会被声明提前,首选
c. var 函数名=new Function("形参1", "形参2", ... , "函数体; return 返回值")
函数本质: 1). 函数也是一个对象,对象中保存着函数的函数体代码 2). 函数名只是一个普通的变量,函数名通过函数对象地址,引用着函数对象 3). function在底层等效于new Function() function 函数名(){ ... }和var 函数名=function(){}在底层都会被翻译为 var 函数名=new Function(...) 只不过function 函数名(){}是先提前,再翻译 而var 函数名=function(){}是不提前,原地翻译 |
(2). 重载: 今后,一件事,根据传入不同的参数值,动态执行不同的逻辑时,都用重载
function 一个函数名(不写形参变量){
//arguments对象自动接住所有实参值
if(arguments.length==0){
执行一种逻辑
}else if(arguments.length==1){
执行另一种逻辑
}else{
执行其它逻辑
}
}
其中arguments是类数组对象: 和数组相比:
a. 相同点: 也有下标,length属性,也可for循环遍历
b. 不同点: 不是数组类型,无法使用数组家的函数
(3). 匿名函数:
a. 所有回调函数优先使用匿名函数——用完释放,节约内存
b. 所有js代码都应该保存在匿名函数自调中,禁止使用全局变量,避免全局污染!
(function(){
要执行的js代码
})();
结果: 匿名函数内的都是局部变量,不会产生全局变量。
局部变量随匿名函数一起释放。不会污染全局。
(4). 作用域和作用域链: (跟着视频亲自画图!!!)
a. 作用域:
1). 全局作用域:window,保存全局变量
优: 可重用,缺: 随处可用, 极易被污染
2). 函数作用域: 保存局部变量
局部变量包括2中: 函数中var出的变量和形参变量
优: 仅函数内可用,不会被污染,缺: 不可重用
3). 函数作用域对象原理:
i. 每个函数定义时都自带好友列表,好友列表里2个格子,一个是空,一个引用window
ii. 调用函数时临时创建函数作用域对象保存函数局部变量。并将函数作用域对象的地址保存到函数好友列表中离自己近的格子里。
iii. 函数执行过程中按就近原则先在自己的函数作用域对象中找局部变量使用。如果找不到,才被迫去全局window中找变量使用.
iv. 函数调用后,好友列表中离自己近的格子清空,导致函数作用域对象以及内部的局部变量被释放!——所以局部变量不可重用!
b. 作用域链: 保存一个函数所有可用的作用域对象的链式结构(好友列表)学名就叫作用域链。
1). 作用域链保存着一个函数可用的所有变量
2). 作用域链控制着变量的使用顺序。先局部后全局。
🆕【后文传送门】👉 js之闭包对象_04
如果这篇【文章】有帮助到你,希望可以给【青春木鱼】点个赞👍,创作不易,相比官方的陈述,我更喜欢用【通俗易懂】的文笔去讲解每一个知识点,如果有对【前端技术】感兴趣的小可爱,也欢迎关注❤️❤️❤️【青春木鱼】❤️❤️❤️,我将会给你带来巨大的【收获与惊喜】💕💕!