自定义Function
需要先定义好,可以反复使用的一个代码段
何时使用:1、不希望打开页面立刻执行
2、以后可以反复使用
3、希望绑定在页面元素之上
使用
创建、调用
**创建**:2种
【声明方式】创建函数 !推荐!
function 函数名(形参,...){
函数体;
return 返回值/结果;
}
【直接量方式】创建函数 - 无用
var 函数名=function(形参,...){
函数体;
return 返回值/结果;
}
**调用**
var 接住返回的结果=函数名(实参,...);
return的本意退出函数,但是如果return后跟着一个数据,
顺便将数据返回到函数作用域的外部,但return只负责返回,不负责保存
就算省略return,默认也会return undefined;
**作用域**:2种
全局作用域:
全局变量 和 全局函数,在页面的任何一个位置都可以使用
函数/局部作用域:
局部变量 和 局部函数,在【当前函数调用时内部可用】
变量的使用规则:优先使用自己的,自己没有找全局,全局没有报错
特殊:缺点:1、千万不要再函数中对着未声明的变量直接赋值 - 全局污染
2、局部可以用全局的,但是全局不能用局部的 - 解决:return后接住
**声明提前**:在程序正式执行之前
将var声明的变量(轻)和function声明的函数(重)
都会悄悄集中定义在当前作用域的顶部
但是赋值留在原地
声明方式创建的函数会完整的提前
直接量方式创建的函数不会完整提前,只有变量部分会提前
何时使用:永远不会自己使用,干扰判断 - 只会在笔试中遇到
为什么平时开发根本不可能遇到它?
只要遵守原则:
1、变量名和函数名尽量的不要重复
2、先创建后使用
3、如果笔试时需要先试用后创建,多半是在考声明提前
**按值传递**:
两个变量之间赋值,分两种情况
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,引用类型其实根本没有保存到变量中,仅仅只是保存了一个地址值
两者用的是同一个地址值,所以会相互影响
预定义全局函数
提前定义好的,可以直接使用的,在任何位置都可以使用
例如:parseInt/Float/isNaN/eval...
1、编码和解码
问题:url中不允许出现多字节字符(汉字,utf-8编码格式下,一个汉字占3字节),如果出现会乱码
解决:发送前,前端将多字节字符编码为单字节字符(数字、字母、符号)
发送后,后端将单字节字符在解码为多字节原文
如何:
编码:var 不认识=encodeURIComponent("原文");
解码:var 原文=decodeURIComponent(不认识);
无用,被淘汰了,浏览器自带此功能
2、isFinite(num):判断num是不是有效范围
垃圾并不能用于判断是不是NaN,因为有三个人会是false
哪些会为false: NaN , Infinity , 分母为0( num/0 )
分支结构
根据条件的不同,选择部分代码执行
1、if分支
2、三目&短路
3、switch...case...
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、while
2、for
3、do...while...
do...while...
var 循环变量=几;
do{
操作;
变量变化;
}while(循环条件)
面试题:while和do...while的区别?
只看第一次,如果大家都满足,则没区别
如果不满足,while一次都不执行,而dowhile至少会执行一次
退出循环语句
break - 退出整个循环
continue - 退出本次循环
死循环
while(true){}
for(;;){}
遍历
forEach
for in
for of
专为数组准备用
数组Array
什么是数组:一个集合可以保存多个数据
何时使用:多个相关的数据,都要集中的定义在一个集合中
为什么:因为一个好的数据结构,能够极大的提升我们程序员的开发效率
使用
**创建**:2种
1、*直接量方式:var arr=[值1,...];
2、构造函数方式:var arr=new Array(值1,...);
**坑**:new Array(num); - 认为是创建了一个长度为num的空数组
**访问**:
数组名[下标];
添加/修改:数组名[下标]=新值;
特殊:访问时,下标越界 - 返回undefined
添加时,下标越界 - 变为稀疏数组,导致下标不连续,导致以后遍历一定会得到undefined
数组三大不限制
1、不限制类型
2、不限制长度
3、不限制下标越界 - 不推荐
数组唯一的一个属性:数组名.length - 获取数组的长度
三个固定套路:
1、末尾添加:arr[arr.length]=新值
2、获取倒数第n个:arr[arr.length-n];
3、缩容:arr.length-=n;
遍历数组:对数组中的每个元素执行 相同 或 相似的操作
for(var i=0;i<arr.length;i++){
arr[i]; // 当前次元素
}
如何释放一个引用类型:看清楚此引用类型有几个变量关联着,每个变量都要释放后,才能真正的释放干净
建议:我们的代码都要封装在一个函数中,函数中的一切变量都会自动释放
关联(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数组】
数组的API
数组的函数,提前定义好的,只有**数组可以使用**
arr 转为 str
1、语法:var str=arr.join("自定义连接符");
作用:
1、笔试时:给你一个数组,将他无缝拼接在一起:
var arr=["h","e","l","l","o"];
var str=arr.join("");
console.log(str);
2、拼接为页面元素:
//以后从数据库中取出数据
var cities=["-请选择-","北京","南京","西京","东京","重庆"];
//拼接成页面元素后,innerHTML是识别标签的
select.innerHTML="<option>"+cities.join("</option><option>")+"</option>";
拼接数组
语法:var newArr=arr.concat(值1,arr1,...);
特殊:1、此方法不修改原数组,只会返回一个新数组
2、支持传入数组参数,悄悄的将我们传入的数组打散,不会变成二维数组
截取子数组
语法:var subArr=arr.slice(starti,endi);
特殊:1、此方法不修改原数组,只会返回一个新数组
2、含头不含尾
3、第二参数可以省略不写,截取到末尾
4、第一个参数也可以省略不写,如果两个参数都没写,从头截取到尾 - 深拷贝(复制了一份副本)
5、支持负数参数,-1代表倒数第一个
以上的API都不会修改原数组
删除、插入、替换
删除:var dels=arr.splice(starti,n);//n代表删除的个数
特殊:此方法其实也有返回值,返回的就是你删除的元素组成的一个新数组
插入:arr.splice(starti,0,新值1,....);
特殊:1、没有删除,也有返回值,返回的是一个空数组
2、原starti位置的元素以及后续元素都会向后顺移
3、不建议插入数组,会变得不伦不类
替换:var dels=arr.splice(starti,n,新值1,....);
特殊:删除的个数和插入的个数不必相同
反转数组
arr.reverse();
排序
冒泡排序:拿着数组中的每一个元素 笔试时:不允许使用数组的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);
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排序
强调:切记前端所有技术中:唯独只有数组可以排序,意味着以后如果网页中有一个排序功能,说明他的底层一定是一个数组
栈和队列
栈:其实就是一个数组,只不过要求只能从一端进出,另一端是封闭的
何时:始终希望使用最新的数据时,现实中很少,代码中也很少
如何:
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
前出:var first=arr.shift(); - 删除元素的新方式,一次只能删除一个,而且是第一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
队列:其实就是一个数组,只不过要求一端进,另一端出
何时:按照先来后到的顺序
如何:
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
前出:var first=arr.shift(); - 删除元素的新方式,一次只能删除一个,而且是第一个
二维数组
数组的元素,又引用着另一个数组
使用
何时:在一个数组内,希望再次细分每个分类
如何:
创建:
var arr=[
["XX",18,1200],
["YY",19,1500],
["ZZ",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]);
}
}
引用类型
String Number Boolean - > 具有包装类型
Array Function Math Date RegExp
Error
Object
Global - 全局对象:在前端/浏览器端/客户端/js中被window代替了:功能:保存着全局变量和全局函数,只有window可以省略不写
String
概念
什么是字符串:多个字符组成的【只读】字符【数组】
1、只读:所有的字符串的API,都不会修改原字符串,只会返回一个新字符串
2、数组:和数组有相同点:
1、可以使用下标得到某个字符
2、可以使用length获取字符串的长度
3、遍历字符串
4、数组不修改原数组的API,字符串也可以使用(拼接 - 垃圾还不如直接+运算,截取)当然和数组也有很多的不同点,数组修改原数组的API,字符串一个都用不到
包装类型
专门用于将原始类型的值封装为一个引用类型的对象(带了属性和方法)
为什么:原始类型的值原本是不具备任何属性和方法的,但是前辈们发现会经常操作到字符串
为了方便,为三个原始类型提供了包装类型
何时使用:只要试图用原始类型的变量去用.调用属性和方法时,就会悄悄的用上包装类型变为对象
何时释放:方法调用完毕后,自动释放包装类型,并且返回数据(又会变回原始类型)
为什么undefined和null不能用. - 没有提供包装类型
StringAPI
只有字符串可以使用的函数,特点:只读!
转义字符
\
作用:
1、将字符串中和程序冲突的符号编译为原文
"\"" '\''
2、包含特殊功能的符号:
换行:\n
制表符:\t
3、输出unicode编码的字符:
\uXXXX:第一个汉字: 4e00 - ascii:19968
最后一个汉字:9fa5 - ascii:40869
大小写转换
将字符串中的每个英文统一的转为大写 或 小写
何时:只要程序不区分大小写,就要【先统一】的转为大写 或 小写,再比较(验证码)
如何:
大写:var upper=str.toUpperCase();
小写:var lower=str.toLowerCase();
获取字符串中指定位置的字符
str.charAt(i) 还不如直接 str[i]
获取字符串中指定位置的字符的ascii码
var ascii=str.charCodeAt(i);
根据ascii码在转回原文:
var 原文=String.fromCharCode(ascii);
检索字符串
检查索引:获取关键字的下标
var i=str/arr.indexOf("关键字",starti);
从starti位置开始,查找右侧【第一个关键字】的位置
特殊:
1、starti可以省略不写,从0开始查找
2、返回值:找到了,返回的第一个关键字的第一个字符的下标
*没找到,返回-1,我们不关心下标为多少,只关心下标为不为-1
作用:判断有没有,如果不想有重复的,就一定要用上他
3、此方法不光字符串可以使用,数组也可以使用,后期才为数组添加上的,老IE上的数组就没有此方法
4、笔试题:默认只能获取到第一个关键字的下标,如何才能获取到所有的关键字的下标
var str="no zuo no die no can no bibi";
var i=-1;
while((i=str.indexOf("no",i+1))!=-1){
console.log(i);
}
截取字符串
var subStr=str/arr.slice(starti,endi+1);//用法和数组的slice一摸一样
str.substring(starti,endi+1);//几乎和slice一致,不支持附属参数 !不推荐!
str.substr(starti,n);//n代表截取的个数,不必考虑含头不含尾
拼接字符串
var newStr=str.concat(新字符串,...);//不如+运算
替换字符串
本身强大,但是必须搭配正则表达式
var newStr=str.replace("关键字","新内容");
切割/分割字符串
切割/分割字符串:
var arr=str.split("任意切割符");
作用:将字符串 => 数组
特殊:1、切割后,切割符就不存在了
2、切割符"",切散每一个字符
正则表达式RegExp
定义字符串中【字符出现规则】的一个表达式
何时使用:切割 替换 【验证】
如何使用:
语法:/正则表达式/
最简单的正则
关键字原文 "XX" -> /XX/gi 只要用上正则就可以添加后缀
g:全部 i:忽略大小写
备选字符集
/[备选字符集]/
强调:1、一个中括号,只管一位数字
2、正则表达式【默认只要满足条件,不管其他了】,解决:前加^,后加$:/^[备选字符集]$/ -
代表要求:用户从头到尾必须完整匹配我们的要求 - 只要是做验证就必然前加^,后加$
特殊:如果备选字符集中的ascii码是连续的,中间的部分可用 - 代替掉
一位数字:[0-9]
一位字母:[A-Za-z]
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
除了数字之外的:[^0-9] - 很少使用,范围太广了
预定义字符集
前辈提前定义好的,直接使用
目的:简化备选字符集
一位数字:\d === [0-9]
一位数字、字母、下划线:\w === [0-9A-Za-z_]
一位空白字符:\s === 包含:空格、换行、制表符
一位除了换行外的任意字符: . - 很少使用,范围太广了
建议:优先使用预定义字符集,满足不了的时候再用备选字符集自定义
问题:不管是备选字符集,还是预定义字符集,一个都只管一位
量词
有明确数量
字符集{n,m}:前边相邻的字符集,最少出现n次,最多出现m次
字符集{n,}:前边相邻的字符集,最少出现n次,多了不限
字符集{n}:前边相邻的字符集,必须出现n次
无明确数量:
?: 前边相邻的字符集,可有可无,最多1次
*: 前边相邻的字符集,可有可无,多了不限
+:前边相邻的字符集,至少一次,多了不限
选择和分组
选择:多个规则中选择其中一个
规则1|规则2
分组:将多个字符集临时组成一组子规则
(规则1|规则2)
指定匹配位置
^:开头
$:结尾
特殊:如果两者同时出现,要求从头到尾完全匹配 - 只要是做验证,必须加上
密码验证
n位,数字和字母,必须出现一位大写和一位数字
/^[0-9A-Za-z]{n}$/
预判公式:(?![0-9]+$) -> 不能全由数字组成
(?![a-z]+$) -> 不能全由小写组成
(?![0-9a-z]+$) -> 不能全由数字组成,也不能全由小写组成,也不能只由数字和小写的组合组成
/^(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{n}$/; - n位,数字和字母,必须出现一位大写和一位数字
/^(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{n}$/ - n位,数字和字母,必须出现一位大写和一位数字和小写
字符串中支持正则表达式的API
切割
var arr=str.split("固定切割符"/regexp);
替换
基础替换法
var newStr=str.replace("固定关键字"/regexp,"新内容");
高级替换法
var newStr=str.replace("固定关键字"/regexp,function(a,b,c){
console.log(a);//第一个形参保存的是正则匹配到的每一个关键字
console.log(b);//第二个形参保存的是正则匹配到的每一个关键字的第一个字符的下标
console.log(c);//原文本身
return a.length==2?"**":"***";
});
格式化
在使用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+"日";
})
正则API
1、创建正则对象:
直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀")
为什么有时候要加 前加^后加$,为什么有时候又要加后缀g
1、验证:前加^后加$ - 用户要和我们的规则完全匹配
2、替换:加后缀g
2、API:var bool=reg.test(用户输入的东西);
返回true,说明用户验证成功,否则验证失败
Math
提供了一些数学计算的API。强调:不需要创建,直接使用:全局对象window.Math
属性:Math.PI 得到3.1415926
API
取整
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的操作
function toFixed(num,d){
num*=Math.pow(10,d);
num=Math.round(num);
num/=Math.pow(10,d);
return num.toString();
}
console.log(toFixed(Math.PI,3));
乘方和开方
乘方:var result=Math.pow(底数,幂)
开方:var result=Math.sqrt(num);//只能开平方
最大值和最小值
var max/min=Math.max/min(a,b,c,d,....);
问题:本身不支持数组参数的
解决:固定用法:var max/min=Math.max/min.apply(Math,arr);
apply是ES5的,apply具有打散数组的功能
绝对值
把负数变为正数
Math.abs(负数)
随机数
Math.random(): 在0-1之间取随机的小数,但是有可能取到0,不可能取到1
有可能取到最小数,但是绝对不可能取到最大数
公式:parseInt(Math.random()*(max-min+1)+min);
强调:只要以后网页中某一块有一个随机的功能,那么一定需要用到随机数
注意:Math还提供了三角函数
Date
提供了操作日期的API
使用
创建: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);
操作
两个日期对象之间可以相减,得到一个毫秒差,换算出你想要的任何一部分 - 日期的本质底层保存的就是一个毫秒
其实创建还有第五种方式:var date=new Date(毫秒)
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()+n);//对日期+n年
4、格式化日期对象->字符串:
date.toLocaleString();//locale本地 - 具有兼容性问题,在不同的浏览器显示出来的效果是不一样,一般来说都要自定义格式化方法
日期可以用日期自己的API - 日期会自动进制
字符串也只可以用字符串自己的API
一旦格式化为字符串则不可用日期的API
Error错误
浏览器自带四种错误类型
可以快速找到自己的错误
语法错误:SyntaxError - 符号写错了
引用错误:ReferenceError - 没有创建就去使用了
类型错误:TypeError - 不是自己的方法,却使用了,最典型的,就是undefined.xxx或null.xxx
范围错误:RangeError - 只有一个API会碰到:num.toFixed(d); d的取值范围只能是0~100之间
只要发生错误,就会报错,会导致后续代码不执行(如果APP报错,那会直接闪退),我们程序不希望报错
错误处理:就算发生错误,也不会报错,不希望抛出错误,而是希望给一个错误提示即可,后续代码依然可以继续运行
语法:
try{
只放入可能出错的代码
}catch(err){
发生错误的时候才会执行
console.log(err);
}
try...catch...的性能非常差,几乎里面的代码效率会被降到最低,不推荐使用
*可以用一个技术代替:if...else... 提前预判
*开发经验:一切的用户都是坏人,都要防一手(!isNaN(x)、正则:把用户控死)
抛出自定义错误
throw new Error("自定义提示");
Function函数
提前创建好的,以后可以反复使用的代码段
创建:3种
1、声明方式:function 函数名(形参列表){函数体;return 返回值} - 完整的提前
2、直接量方式:var 函数名=function(形参列表){函数体;return 返回值}
3、构造函数方式:var 函数名=new Function("形参",.....,"函数体;return 返回值");
何时使用:函数体是动态拼接的
调用
如果有return,要接住结果
var 结果=函数名(实参);
考点
1、创建的三种方式
2、作用域
3、声明提前
4、按值传递
5、重载overload
6、匿名函数
7、闭包。面试题:两链一包(作用域链、原型链、闭包)
重载overload
相同的函数名,根据传入的实参的不同,自动选择对应的函数执行
为什么:减轻程序员负担!
问题:JS不支持重载!
JS不允许多个同名函数同时存在,如果存在,最后的会覆盖之前的所有
解决:在【函数中】有一个对象:arguments对象,不需要我们创建,自带
什么是arguments:是一个类数组对象:但是不是数组,和数组有3个相同点
1、都可以使用下标
2、都可以使用length
3、都可以遍历
作用:***接受住传到函数内部的所有实参,哪怕不写一个形参
可以做的事:
1、实现重载:可以通过在函数内部判断arguments的不同,执行不同的分支操作
2、有没有形参无所谓
3、正式开发中,有可能会将多个函数整合为一个函数 - 代码优化
匿名函数
没有名字的函数
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、了解回调函数的原理
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();
Object面向对象
三大特点:封装、继承、多态
开发方式:
面向过程:经过 - 开始->结束,其实我们一直以来的开发方式都是面向过程:先干什么再干什么最后干什么
面向对象:对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话:
属性:姓名、性别、身高、体重、爱好、智商...
方法:吃饭、睡觉、拉粑粑、跑步、走路、打字、说话...
为什么要面向对象:现实生活中所有的数据都要保存在一个事物中才有意义
何时使用面向对象:以后做任何操作都要封装在一个对象中 - 不太适合初学者
封装对象:创建自定义对象:3种
直接量方式
var obj={
"属性名":"属性值",
...
"方法名":function(){},
...
}
强调:
1、其实属性名和方法名的""可以省略不写的 - 暂时建议写上,以后会学习JSON数据格式,要求必须加""
2、访问对象的属性和方法
*对象名.属性名 === 对象名["属性名"]
*对象名.方法名(); === 对象名["方法名"]();
JS种一切对象的底层都是hash数组
3、访问到不存在的属性或者方法,返回undefined
4、可以后期随时随地的添加新属性和新事件
5、如果希望遍历出对象中的所有东西
for(var i in 对象名){
对象名[i]
}
6、***如果你希望在对象的【方法中】使用对象自己的属性:写为:this.属性名
*****难点:this的指向:
1、*单个元素绑定事件:this->这个元素
2、*多个元素绑定事件:this->当前触发事件的元素
3、***函数中使用this:this->谁在调用此方法,this指向就是谁
4、定时器this->window
5、箭头函数this->外部对象
预定义构造函数方式
var obj=new Object();//空对象
//需要自己后续添加属性和方法
obj.属性名="属性值";
obj.方法名=function(){}
以上两种方法创建都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候使用(第一种方式)
自定义构造函数方式
1、创建自定义构造函数
function 类名(name,age,salary){
this.name=name;
this.age=age;
this.salary=salary;
}
2、反复调用自定义构造函数创建出对象
var xxx=new 类名("实参",...)
面向对象:
优点:1、逼格高,所有的属性和方法都是保存在一个对象之中 -更符合现实生活:代码风格、代码速度
2、每个功能特地的分开写 - 便于维护
3、铁锁连舟 - 一个方法触发多个方法联动
缺点:1、对新手不友好 - 注意this的指向