1、数据类型转换
1、强制转换:3类
1、转字符串:
1、x.toString();//x不能是undefined和null - undefined和null不能.
2、String(x);//万能的,任何人都可以转为字符串,不重要
不重要的原因:
1、页面上一切的数据都是字符串
2、String(); 完全相当于隐式转换 - 还不如+""
2、*转数字:
1、*parseInt/Float(str);//专门为字符串转数字准备的
原理:从左向右依次读取每个字符,碰到非数字字符就停止,如果一来就不认则为NaN,
Int不认识小数点,Float认识第一个小数点
2、Number(x);//万能的,任何人都可以转为数字,不重要 - 完全相当于隐式转换 - 还不如-0 *1 /1
3、转布尔:
Boolean(x);//万能的,任何人都可以转为布尔,不会手动使用的,还不如:!!x
***只有6个会为false:0,"",undefined,null,NaN,false,其余都为true
在分支、循环的条件之中,以后不管代老师写的是什么,他都会悄悄的隐式转换为布尔值,你只需要考虑
为true还是为false
2、隐式转换:都是出现在运算符之中
2、运算符和表达式:
1、算术运算符:+ - * / %
特殊:1、%:取余,判断奇偶性
2、隐式转换:默认转为数字,在运算
true->1
false->0
undefined->NaN
null->0
特殊:1、+运算,碰到字符串,拼接
2、-*/%:字符串可以转为数字,但是纯数字组成才行,但凡包含一个非数字字符,
则为NaN
2、*比较运算符:> < >= <= == != === !==
结果:布尔值
隐式转换:默认,转为数字,再比较大小
特殊:1、如果参与比较的左右两边都是字符串,则是按位PK每个字符的十六进制unicode号
(十进制ascii码)
0-9<A-Z<a-z<汉字:常识:汉字的第一个字:一:unicode:4E00 - ascii:19968
最后一个字:龥:unicode:9FA5 - ascii:40869
2、NaN参与任何比较运算结果都是false,所以没有办法用普通的比较运算判断是不是NaN
!isNaN(x);
3、undefined==null;//true
区分:undefined===null;
=== !==:不带隐式转换的等值比较和不等比较,要求数值相同,并且数据类型也要相同
function String(x){
if(x===undefined){
return "undefined";
}else if(x===null){
return "null";
}else{
return x.toString();
}
}
3、逻辑运算:
&&:全部满足,才true
一个不满足,则false
||:全部不满足,才false
一个不满足,则true
!:颠倒布尔值
特殊:短路逻辑:如果前一个条件,已经能够得出最后结论了,则不看后续
&&短路:如果前一个条件满足,才执行后续操作,如果前一个条件不满足,则不管后续操作
目的:简化【简单的】分支:
1、一个条件一件事满足就做,不满足就不做 if(){}
2、操作只能有一句话
条件&&(操作);
举例:原来:if(money>=500){money*=0.8};
现在:money>=500&&(money*=0.8);
1、自定义Function:
什么是函数:需要先定义好,可以反复使用的一个代码段
何时使用:
1、不希望打开页面立刻执行
2、以后可以反复使用
3、希望绑定在页面元素之上
如何使用:
1、创建并且调用:2种
1、创建
*1、【声明方式】创建函数
function 函数名(形参,...){
函数体;
return 返回值/结果;
}
2、【直接量方式】创建函数 - 无用
var 函数名=function(形参,...){
函数体;
return 返回值/结果;
}
2、调用
var 接住返回的结果=函数名(实参,...);
//其实return的本意退出函数,但是如果return后跟着一个数据,
//顺便将数据返回到函数作用域的外部,但return只负责返回,不负责保存
//就算省略return,默认也会return undefined;
//具体需不要得到函数的结果,看你自己
2、作用域:2种
1、全局作用域:全局变量 和 全局函数,在页面的任何一个位置都可以使用
2、函数/局部作用域:局部变量 和 局部函数,在【当前函数调用时内部可用】
*变量的使用规则:优先使用自己的,自己没有找全局,全局没有报错
特殊:缺点:1、千万不要再函数中对着未声明的变量直接赋值 - 全局污染
2、局部可以用全局的,但是全局不能用局部的 - 解决:看上面return
3、声明提前:
在程序正式执行之前
将var声明的变量(轻)和function声明的函数(重)
都会悄悄集中定义在当前作用域的顶部
但是赋值留在原地
声明方式创建的函数会完整的提前(第一种方式)
直接量方式创建的函数不会完整提前,只有变量部分会提前(第二种方式)
何时使用:永远不会自己使用,垃圾干扰我们判断 - 只会在鄙视中遇到,为什么平时开发根本不可能遇到它?
只要你遵守以下原则:
1、变量名和函数名尽量的不要重复
2、先创建后使用
3、如果鄙视时需要先试用后创建,多半都是在考你声明提前
4、按值传递:两个变量之间赋值,分两种情况
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,引用类型其实根本没有保存到变量中,
仅仅只是保存了一个地址值
两者用的是同一个地址值,所以会相互影响
2、预定义全局函数:
前辈们提前定义好的,我们程序员可以直接使用的,在任何位置都可以使用 *parseInt/Float/isNaN/eval... 其实都是预定义全局函数,但是alert/prompt:确实也是全局预定义函数,只不过属于BOM
1、编码和解码
问题:url中不允许出现多字节字符(汉字,utf-8编码格式下,一个汉字占3字节),如果出现会乱码
解决:发送前,前端将多字节字符编码为单字节字符(数字、字母、符号)
发送后,后端将单字节字符在解码为多字节原文
如何:
编码:var 不认识=encodeURIComponent("大梵");
解码:var 原文=decodeURIComponent(不认识);
这个东西没有用,在某一次浏览器更新后,当前就被淘汰了,浏览器自带此功能
2、isFinite(num):
判断num是不是有效范围 - 垃圾并不能用于判断是不是NaN,因为有三个人会是false
哪些会为false:NaN,Infinity,分母为0
3、分支结构:
根据条件的不同,选择部分代码执行
1、if分支
2、三目&短路
3、switch...case...语法
switch(变量/表达式){
case 值1:
操作1;
case 值2:
操作2;
case 值3:
操作3;
default:
默认操作;
}
特殊:1、不具备隐式转换
2、问题:默认只要一个case满足后,就会将后面所有操作全部做一次
解决:break;
建议:每一个case的操作后都要跟上一个break
有的地方可以不加break:
1、最后一个操作default可以省略break
2、如果中间连续的多个操作,是相同的,也可以省略掉中间部分
面试题:if vs switch
1、switch:缺点:必须要知道最后的结果才能使用,不能做范围判断
优点:执行效率相对较高
2、if : 优点:可以做范围判断
缺点:执行效率相对较慢
建议:代码优化:尽量的将if替换为switch或者三目或短路
1、循环结构:宏观几乎是一瞬间执行的,相同或相似的代码
1、*var 循环变量=几;
while(循环条件){
操作;
变量变化;
}
2、do...while循环语法:
var 循环变量=几;
do{
操作;
变量变化;
}while(循环条件)
面试题:while和do...while的区别?
只看第一次,如果大家都满足,则没区别
如果不满足,while一次都不执行,而dowhile至少会执行一次
3、*for(var 循环变量=几;循环条件;变量变化){
操作;
}
4、*退出循环语句:
break - 退出整个循环
continue - 退出本次循环
5、死循环:while(true){} for(;;){}
forEach for in for of - 专门为遍历数组准备的
2、数组的基础:
1、什么是数组:一个集合可以保存多个数据
何时使用:多个相关的数据,都要集中的定义在一个集合中
为什么:因为一个好的数据结构,能够极大的提升我们程序员的开发效率
2、创建:2种
1、*直接量方式:var arr=[值1,...];
2、构造函数方式:var arr=new Array(值1,...);
坑:new Array(num); - 懂不起:以为你是创建了一个长度为num的空数组
3、访问:数组名[下标];
添加/修改:数组名[下标]=新值;
特殊:访问时,下标越界 - 返回undefined
添加时,下标越界 - 变为稀疏数组,导致下标不连续,导致以后遍历一定会得到undefined
4、数组三大不限制
1、不限制类型
2、不限制长度
3、不限制下标越界 - 不推荐
5、数组唯一的一个属性:数组名.length - 获取数组的长度
三个固定套路:
1、末尾添加:arr[arr.length]=新值
2、获取倒数第n个:arr[arr.length-n];
3、缩容:arr.length-=n;
6、遍历数组:对数组中的每个元素执行 相同 或 相似的操作
for(var i=0;i<arr.length;i++){
arr[i];//当前次元素
}
7、如何释放一个引用类型:
看清楚此引用类型有几个变量关联着,每个变量都要释放后,才能真正的释放干净
建议:我们的代码都要封装在一个函数中,函数中的一切变量都会自动释放
索引数组:下标都是由数字组成的数组
8、关联(hash)数组:下标是可以自定义的数组
为什么:索引数组的下标无具体的意义,不便于查找
如何使用:
1、创建:2步
1、创建一个空数组:var arr=[];
2、为数组添加自定义下标并且赋值:arr["自定义下标"]=新值
2、访问:
arr["自定义下标"]
3、强调:hash数组length永久失效,永久为0!
问题:hash数组不能使用for遍历,必须使用 for in循环遍历数组,语法:
for(var i in 数组名){
console.log(i);//自动获得当前数组的所有的下标,
不需要我们去设置从哪里开始到哪里结束
console.log(arr[i]);//当前次元素
}
厉害的地方:不光可以遍历hash数组,也可以遍历索引数组:
建议:hash用for in,索引用for
4、js中一切东西都是对象,万物皆对象,除了undefined和null
【一切对象的底层都是hash数组】
1、Array API
1、排序:
1、笔试时:
不允许使用数组的API - 冒泡排序:拿着数组中的每一个元素,
让前一个和后一个做比较,如果前一个>后一个,两者应该交换位置:固定公式
var arr=[32,14,43,453,6,58,56,531,5,57];
for(var j=1;j<arr.length;j++){
for(var i=0;i<arr.length-j;i++){
if(arr[i]>arr[i+1]){
var m=arr[i];
arr[i]=arr[i+1];
arr[i+1]=m;
}
}
}
console.log(arr);
2、数组的API:语法:arr.sort();
默认:悄悄的转为字符串,按位PK每个字符的unicode号,默认是按照字符串排序
问题1:希望按照数字升序排序
解决:
arr.sort(function(a,b){
return a-b;
});
原理:1、匿名函数回调,一般都是前辈们规定好的,我们只能学习怎么使用,
自动调用,而且有多少对儿就会调用多少次
2、a:后一个数字 b:前一个数字
3、返回结果,如果是正数,说明后一个大
如果是负数,说明前一个大
如果是0,说明一样大
4、而我们的sort方法会根据你返回的正数负数0,来判断要不要交换位置
问题2:希望按照数字降序排列
arr.sort(function(a,b){
return b-a;
});
建议:开发中使用API排序,鄙视可能会碰到冒泡排序
强调:切记前端所有技术中:唯独只有数组可以排序,意味着以后如果网页中有一个排序功能
,说明他的底层一定是一个数组
2、栈和队列:
栈:其实就是一个数组,只不过要求只能从一端进出,另一端是封闭的
何时:始终希望使用最新的数据时,现实中很少,代码中也很少
如何:
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
前出:var first=arr.shift(); - 删除元素的新方式,一次只能删除一个,而且是第一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
队列:其实就是一个数组,只不过要求一端进,另一端出
何时:按照先来后到的顺序
如何:
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,
而且是最后一个
2、二维数组:
数组的元素,又引用着另一个数组
何时使用:在一个数组内,希望再次细分每个分类
如何使用
创建:
var arr=[
["杨杰",18,1200],
["周洁",19,1500],
["盛蕾",20,3500]
];
访问:arr[行下标][列下标];
特殊:列下标越界 - 返回undefined
行下标越界 - 报错:行下标越界确实会得到undefined,但是undefined没有资格再加[]
遍历二维数组:必然两层循环,外层循环控制行,内层循环控制列
for(var r=0;r<arr.length;r++){
for(var c=0;c<arr[r].length;c++){
console.log(arr[r][c]);
}
}
总结:
1、数组的基础(创建、访问、添加、length、遍历)
2、数组的API:10个(转字符串、拼接、截取、翻转、删插替、排序、栈和队列)
3、二维数组
3、String的概念:
1、什么是字符串:多个字符组成的【只读】字符【数组】:
1、只读:我们下周一要学习的所有的字符串的API,都不会修改原字符串,只会返回一个新字符串
2、数组:和数组有相同点:
1、可以使用下标得到某个字符
2、可以使用length获取字符串的长度
3、遍历字符串
4、数组不修改原数组的API,字符串也可以使用(拼接 - 垃圾还不如直接+运算,截取)
当然和数组也有很多的不同点,数组修改原数组的API,字符串一个都用不到,字符串也有十几个API等待我们学习
2、引用/对象类型:11个
*String Number Boolean - > 具有包装类型
*Array *Function Math(数学) Date(日期) *RegExp(正则表达式:验证)
Error(错误)
*Object(面向对象)
Global - 全局对象:在前端/浏览器端/客户端/js中被window代替了:
功能:保存着全局变量和全局函数,只有window可以省略不写
3、包装类型:
专门用于将原始类型的值封装为一个引用类型的对象的(带了属性和方法)
为什么:原始类型的值原本是不具备任何属性和方法的,但是前辈们发现我们程序员会经常操作到
字符串
前辈们为了方便我们程序员,为三个原始类型提供了包装类型
何时使用:只要试图用原始类型的变量去用.调用属性和方法时,就会悄悄的用上包装类型变为对象
何时释放:方法调用完毕后,自动释放包装类型,并且返回数据(又会变回原始类型)
为什么undefined和null不能用. - 因为没有提供包装类型
1、正则表达式:
什么是:定义字符串中【字符出现规则】的一个表达式
何时使用:切割 替换【验证】
如何使用:语法:/正则表达式/
1、最简单的正则:
关键字原文 "no" -> /no/gi 只要用上正则就可以添加后缀
g:全部 i:忽略大小写
2、备选字符集:
/[备选字符集]/
强调:1、一个中括号,只管一位数字
2、正则表达式【默认只要满足条件,不管其他了】,
解决:前加^,后加$:/^[备选字符集]$/ -
代表要求:用户从头到尾必须完整匹配我们的要求 - 只要是做验证就必然前加^,后加$
特殊:如果备选字符集中的ascii码是连续的,中间的部分可用-代替掉
一位数字:[0-9]
一位字母:[A-Za-z]
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
除了数字之外的:[^0-9] - 很少使用,范围太广了
3、预定义字符集:
前辈们提前定义好的,我们直接使用的
目的:简化备选字符集
一位数字:\d === [0-9]
一位数字、字母、下划线:\w === [0-9A-Za-z_]
一位空白字符:\s === 包含:空格、换行、制表符
一位除了换行外的任意字符:. - 很少使用,范围太广了
建议:优先使用预定义字符集,满足不了的时候再用备选字符集自定义
问题:不管是备选字符集,还是预定义字符集,一个都只管一位
4、量词:规定一个字符集出现的次数
有明确数量
字符集{n,m}:前边相邻的字符集,最少出现n次,最多出现m次
字符集{n,}:前边相邻的字符集,最少出现n次,多了不限
字符集{n}:前边相邻的字符集,必须出现n次
无明确数量:
?: 前边相邻的字符集,可有可无,最多1次
*: 前边相邻的字符集,可有可无,多了不限
+:前边相邻的字符集,至少一次,多了不限
5、选择和分组
选择:多个规则中选择其中一个
规则1|规则2
分组:将多个字符集临时组成一组子规则
(规则1|规则2)
6、指定匹配位置:
^:开头
$:结尾
特殊:如果两者同时出现,要求从头到尾完全匹配 - 只要是做验证,必须加上
7、密码验证:4位,数字和字母,必须出现一位大写和一位数字
/^[0-9A-Za-z]{4}$/
预判公式:(?![0-9]+$) -> 不能全由数字组成
(?![a-z]+$) -> 不能全由小写组成
(?![0-9a-z]+$) -> 不能全由数字组成,也不能全由小写组成,也不能只由数字和小写的组合组成
/^(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{4}$/; - 4位,数字和字母,必须出现一位大写和一位数字
/^(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{4}$/ - 4位,数字和字母,
必须出现一位大写和一位数字和小写
2、字符串中支持正则表达式的API
1、切割:
var arr=str.split("固定切割符"/regexp)
2、替换
1、基础替换法:
var newStr=str.replace("固定关键字"/regexp,"新内容");
2、高级替换法:
var newStr=str.replace("固定关键字"/regexp,function(a,b,c){
console.log(a);//第一个形参保存的是正则匹配到的每一个关键字
console.log(b);//第二个形参保存的是正则匹配到的每一个关键字的第一个字符的下标
console.log(c);//原文本身
return a.length==2?"**":"***";
});
3、格式化:在使用replace替换时,如果搭配上了正则,并且正则中加入分组,
那么我们的高级替换法会得到更多的形参
有几个分组,就会多出几个形参。
var newStr=id.replace(reg,function(a,b,c,d,e,f,g,h){
console.log(a);//第一个形参保存的是正则匹配到的关键字
console.log(b);//第二个形参会第一个分组匹配到的关键字
console.log(c);//第三个形参会第2个分组匹配到的关键字
console.log(d);//第四个形参会第3个分组匹配到的关键字
//....说不清楚有多少个,具体看有多少个分组
console.log(e);//关键字下标
console.log(f);//原文本身
return c+"年"+d+"月"+e+"日";
})
3、正则对象:API
1、创建正则对象:
*直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀")
为什么有时候要加 前加^后加$,为什么有时候又要加后缀g
1、验证:前加^后加$ - 用户要和我们的规则完全匹配
2、替换:加后缀g
2、API:var bool=reg.test(用户输入的东西);
返回true,说明用户验证成功,否则验证失败
1、Math对象:提供了一些数学计算的API
强调:不需要创建,直接使用:全局对象window、Math
属性:Math.PI 得到3.1415926
API:
1、取整:3种
1、上取整:超过一点点,就取下一个整数
var result=Math.ceil(num);
2、下取整:哪怕超过的再多,也会省略掉小数部分
var result=Math.floor(num);
3、四舍五入取整
var result=Math.round(num);
//以上三个操作都只能取整:
//取整的方式:以上三个 + *parseInt + *num.toFixed(0)
//个人更推荐:num.toFixed(d):优点:具有四舍五入,并且小数位数可以自己设置
缺点:返回是一个字符串,搭配上一个parseFloat
//*笔试题:要求不允许使用toFixed,自己封装出toFixed的操作
2、乘方和开方
*乘方:var result=Math.pow(底数,幂)
开方:var result=Math.sqrt(num);//只能开平方
3、最大值和最小值:
var max/min=Math.max/min(a,b,c,d,....);
问题:本身不支持数组参数的
解决:固定用法:var max/min=Math.max/min.apply(Math,arr);
apply是ES5才会学习的东西,apply具有打散数组的功能
4、绝对值:把负数变为正数
Math.abs(负数)
5、随机数:Math.random(): 在0-1之间取随机的小数,但是有可能取到0,不可能取到1
有可能取到最小数,但是绝对不可能取到最大数
公式:parseInt(Math.random()*(max-min+1)+min);
强调:只要以后网页中某一块有一个随机的功能,那么一定需要用到随机数
注意:其实Math还提供了三角函数
2、Date对象:提供了操作日期的API
1、创建:4种
1、*创建一个当前时间:
var now=new Date();
2、*创建一个自定义时间:
var birth=new Date("yyyy/MM/dd hh:mm:ss");
3、创建一个自定义时间:
var birth=new Date(yyyy,MM-1,dd,hh,mm,ss);
缺点:月份需要进行修正,0 代表 1月
4、复制一份日期:
为什么:日期的所有的API都是直接修改原日期对象的,无法获得修改之前的日期
所以,在执行API之前都要先进行复制,然后在操作复制后的日期
var end=new Date(start);
2、操作:
1、两个日期对象之间可以相减,得到一个毫秒差,换算出你想要的任何一部分
- 日期的本质底层保存的就是一个毫秒
其实创建还有第五种方式:var date=new Date(毫秒)
2、API:
分量:时间的单位
年月日星期:FullYear Month Date Day
时分秒毫秒:Hours Minutes Seconds Milliseconds
每一个分量都有一对儿getXXX/setXXX的API
get用于获取
set用于设置
特殊:
1、取值范围:
年 - 当前年份的数字
月 - 0~11
日 - 1~31
星期 - 0~6:外国人觉得星期天是一周的第一天
小时 - 0~23
分秒 - 0~59
2、唯独星期不允许设置set
3、建议如果你希望对某个分量做加减
date.setXXX(date.getXXX()+/-n);
date.setFullYear(date.getFullYear()+3);//对日期+3年
4、格式化日期对象->字符串:
date.toLocaleString();//locale本地 - 具有兼容性问题,在不同的浏览器显示出来的效果是不一样,
一般来说我们都要自定义格式化方法
日期可以用日期自己的API - 日期屌在日期会自动进制
字符串也只可以用字符串自己的API
一旦格式化为字符串则不可用日期的API
1、Error:错误对象
1、***浏览器自带四种错误类型:可以快速找到自己的错误
语法错误:SyntaxError - 符号写错了
引用错误:ReferenceError - 没有创建就去使用了
类型错误:TypeError - 不是自己的方法,你却去使用了,最典型的,就是你们经常会undefined.xxx或null.xxx
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d);//d的取值范围只能是0~100之间
2、只要发生错误,就会报错,会导致后续代码不执行(如果APP报错,那会直接闪退),我们程序不希望报错
错误处理:就算发生错误,也不会报错,不希望抛出错误,而是希望给一个错误提示即可,后续代码依然可以继续允许
语法:
try{
只放入你可能出错的代码
}catch(err){
发生错误的时候才会执行
console.log(err);
}
try...catch...的性能非常差,几乎里面的代码效率会被降到最低,所以不推荐使用
*可以用一个技术代替:if...else... 提前预判
*开发经验:一切的用户都是坏人,都要防一手(!isNaN(x)、正则:把用户控死)
3、抛出自定义错误:
throw new Error("自定义提示");
2、Function:函数对象:提前创建好的,以后可以反复使用的代码段
1、创建:3种
1、声明方式:function 函数名(形参列表){函数体;return 返回值} - 完整的提前
2、直接量方式:var 函数名=function(形参列表){函数体;return 返回值}
3、构造函数方式:var 函数名=new Function("形参",.....,"函数体;return 返回值");
何时使用:函数体是动态拼接的
2、调用:如果有return,记得接住结果
var 结果=函数名(实参);
3、考点:
1、创建的三种方式
2、作用域:变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
3、声明提前:
4、按值传递:
5、重载overload:相同的函数名,根据传入的实参的不同,自动选择对应的函数执行
为什么:减轻程序员负担!
问题:JS不支持重载!
JS不允许多个同名函数同时存在,如果存在,最后的会覆盖之前的所有
解决:在【函数中】有一个对象:arguments对象,不需要我们创建,自带
什么是arguments:是一个类数组对象:但是不是数组,和数组有3个相同点
1、都可以使用下标
2、都可以使用length
3、都可以遍历
作用:***接受住传到函数内部的所有实参,哪怕你不写一个形参
可以做的事儿:
1、实现重载:可以通过在函数内部判断arguments的不同,执行不同的分支操作
2、以后有没有形参无所谓
3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化
6、匿名函数:没有名字的函数
1、匿名函数自调:
//此函数只会执行一次,执行后会自动释放,优点,自动释放 - 代替全局写法,提升网页的性能
(function(){
var a=2;
console.log(a);
btn.onclick=function(){
console.log("释放了码");
}
//引用类型:在还有人使用的情况下,是不会自动释放的
})();
2、匿名函数回调:将函数作为实参,传递给了其他函数调用 - 没有名字就是匿名函数,
匿名函数只有自调和回调,没有自调,则是回调
1、学习回调函数的目的:让你们知道哪些属于回调函数
arr.sort(function(a,b){return a-b;})
str.replace(reg,function(){})
btn.onclick=function(){}
这些东西都是前辈们规定好的,我们只能学习如何使用的
2、ES6 - 一切的回调函数,都可以简化为箭头函数
3、了解了回调函数的原理
1、Function
作用域:
1、全局:成员,随处可用,可以反复使用,缺点:容易被污染
2、函数:成员,只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的
函数的执行原理:
1、程序加载时:
创建执行环境栈(ECS): 保存函数调用顺序的数组
首先压入全局执行环境(全局EC)
全局EC引用着全局对象window
window中保存着全局变量
2、定义函数时
创建函数对象:封装代码段
在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
全局函数的scope都是window
3、调用前
在执行环境栈(ECS)压入新的EC(函数的EC)
创建活动对象(AO):保存着本次函数调用时用到的局部变量
在函数的EC中有一个scope chain(作用域链)属性引用AO
AO有一个parent属性是函数的scope引用的对象
4、调用时
正是因为有前面三步,我们才有了变量的使用规则:优先使用局部的,局部没有找全局,全局没有就报错
5、调用完:
函数的EC会出栈,没人引用AO,AO自动释放,所以局部变量也就释放了
两链一包:
作用域链:以函数的EC中的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,就称之为叫做作用域链
作用:变量的使用规则,查找变量
*****闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实函数一个函数,写法和以后有点不一样
何时使用:希望保护一个可以【反复使用的局部变量】
如何使用:4步
1、两个函数相互嵌套
2、外层函数创建出受保护的变量
3、外层函数要return返回内层函数
4、内层函数要操作受保护的变量
强调:
1、判断是不是闭包,有没有两个函数嵌套,返回内层函数,内层函数在操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远不会被释放,使用过多,造成内存泄漏
使用场景:防抖节流 - 有三个事件需要做防抖节流:执行的速度非常的快,减少DOM树的渲染
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 输入内容有变化就会触发
3、window.onresize - 当前窗口的尺寸如果发生了变化就会触发:JS版本的媒体查询
公式:
elem.on事件名=function(){
inner();
}
function fdjl(){
var timer=null;//1 2 3 - 定时器序号
return function(){
if(timer){clearTimeout(timer);timer=null}
timer=setTimeout(function(){
//操作
},1000)
}
}
var inner=fdjl();