day6
比较运算
< >= <= == != === !== 结果:布尔值 隐式转换:默认,左右两边都会悄悄的转为数字,再比较 特殊:
1.如果左右【两边】参与比较的都是字符串,则是按位PK每个字符的十六进制的unicode号(十进制ascii码)
0-9<A-Z<a-z<汉字
汉字的第一个字:一(unicode:4E00)-(ascii:19968) 汉字的最后一个字:龥(unicode:9fa5)-(ascii:40869)
2.NaN参与任何比较运算结果都为false,带了一个问题,没有办法使用普通的比较运算判断一个xx是不是NaN
解决:!isNaN(xx); true->有效数字 false->NaN
3.undefined==null,
问题:==区分不开undefined和null,怎么才能区分开?
undefined===null
全等:===,要求值和数据类型都要相同,换句话说,就是不再带有隐式转换
!==,不带隐式转换的不等比较
写一个String()方法的底层原理
function String(x) {
if (x === null) {
return "null";
} else if (x === undefined) {
return "undefined";
} else {
return x.toString();//return 返回
}
}
逻辑运算
短路逻辑:
只要前一个条件已经可以得出最后结论,则后续条件不再执行!
&&短路:
如果前一个条件为true,后一个操作才执行
如果前一个条件为false,后一个操作不执行
简化了【简单的】分支:if(条件){操作}
语法:条件&&(操作);
举例:
曾经:if(total>=500){total*=0.8};
现在:total>=500&&(total*=0.8);
特殊:【简单的】-> 操作只能有一句话!
建议:能用短路分支绝对不用if分支
||短路:
如果前一个条件为true,不需要执行后一个
如果前一个条件为false,需要执行后一个
使用场景:两个值二选一 - 后期做浏览器兼容性(老IE)
e=e||window.event; - 见小案例
赋值运算
一个操作两件事,先计算,在赋值回去。
i++; 递增:每次只会固定+1
i+=1; 累加:每次+几由我们程序员自己决定
i++ === i+=1 === i=i+1;
鄙视题:
++i和i++的区别:
单独使用,放前放后无所谓,效果一样。
但是如果参与了别的表达式,变量中的值都会+1
前++返回的是加了过后的新值
后++返回的是加了之前的旧值
三目运算:
简化if...else 简化if...else if...else
语法:
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:条件3?操作3:默认操作;
特殊:
1、只能简化简单的分支 - 操作只能有一句话
2、默认操作不能省略
day7
函数作用域
全局作用域:
全局变量 和 全局函数,在页面的任何一个位置都可以使用
函数/局部作用域:
局部变量 和 局部函数,在【当前函数调用时内部可用】
*变量的使用规则:优先使用自己的,自己没有找全局,全局没有报错
特殊/错误:
1.千万不要再函数中对着未声明的变量直接赋值 - 全局污染
2.局部可以用全局的,但是全局不能用局部的 - 解决:看上面return
声明提前:
在程序正式执行之前
将var声明的变量(轻)和function声明的函数(重)
都会悄悄集中定义在当前作用域的顶部
但是赋值留在原地
声明方式创建的函数会完整的提前(第一种方式)
直接量方式创建的函数不会完整提前,只有变量部分会提前(第二种方式)
何时使用:永远不会自己使用,垃圾干扰我们判断 - 只会在鄙视中遇到,为什么平时开发根本不可能遇到它
只要你遵守以下原则:
1.变量名和函数名尽量的不要重复
2.先创建后使用
3.如果鄙视时需要先使用后创建,多半都是在考你声明提前
按值传递:
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,因为大家操作其实是同一个【地址值】
分支结构
switch...case...
switch (变量 / 表达式) {
case 值1:
操作1;
case 值2:
操作2;
case 值3:
操作3;
default:
默认操作;
}
特殊:
case的比较不带隐式转换
问题:默认只要一个case满足后,会将后面所有的操作全部做完
解决:break;
建议:
每一个case的操作后都跟上一个break
有的地方也可以不加break:
1.最后的一个操作default可以省略break;
2.如果中间多个条件,做的操作是一样的,也不需要
3.default可以省略,如果条件都不满足的情况,什么事都不会做
day8
循环结构
1、*var 循环变量=几;
while(循环条件){
操作;
变量变化;
}
2、do...while循环语法:
var 循环变量=几;
do{
操作;
变量变化;
}while(循环条件)
hash/关联数组
下标是可以自定义的数组
创建:
1、创建一个空数组:var arr=[];
2、为数组添加自定义下标并且赋值:arr["自定义下标"]=新值
访问:
arr["自定义下标"]
强调:
hash数组length永久失效,永久为0!
问题:hash数组不能使用for遍历,必须使用 for in循环遍历数组,语法:
for(var i in 数组名){
console.log(i);//自动获得当前数组的所有的下标,不需要我们去设置从哪里开始到哪里结束
console.log(arr[i]);//当前次元素
}
牛逼:不光可以遍历hash数组,也可以遍历索引数组:
建议:hash用for in,索引用for
*js中一切东西都是对象,万物皆对象,除了undefined和null,【一切对象的底层都是hash数组】
数组的API
arr 转为 str:
语法:var str=arr.join("自定义连接符");
作用:
1、鄙视时:给你一个数组,将他无缝拼接在一起:
var arr=["h","e","l","l","o"];
var str=arr.join("");
console.log(str);
2、拼接为页面元素:
//以后从数据库中取出数据
var cities=["-请选择-","北京","南京","西京","东京","重庆"];
//拼接成页面元素后,innerHTML是识别标签的
sel.innerHTML=""+cities.join("")+"";
拼接数组:
添加元素的新方式
语法: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,....);
特殊:
删除的个数和插入的个数不必相同
day9
数组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,来判断要不要交换位置
建议:开发中使用API排序,鄙视可能会碰到冒泡排序
强调:切记前端所有技术中:唯独只有数组可以排序,意味着以后如果网页中有一个排序功能,说明他的底层一定是一个数组
栈和队列
栈:
其实就是一个数组,只不过要求只能从一端进出,另一端是封闭的
何时:始终希望使用最新的数据时,现实中很少,代码中也很少
如何:
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
前出:var first=arr.shift(); - 删除元素的新方式,一次只能删除一个,而且是第一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
队列:
其实就是一个数组,只不过要求一端进,另一端出
何时:按照先来后到的顺序
如何:
前进:arr.unshift(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
后进:arr.push(新元素,...) - 添加元素的新方式,建议不要添加数组
后出:var last=arr.pop(); - 删除元素的新方式,一次只能删除一个,而且是最后一个
二维数组:
数组的元素,又引用着另一个数组
何时使用:在一个数组内,希望再次细分每个分类
如何使用
创建:
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]);
}
}
String的概念
什么是字符串:
多个字符组成的只读字符【数组】:
只读:
我们下周一要学习的所有的字符串的API,都不会修改原字符串,只会返回一个新字符串
和数组有相同点:
1、可以使用下标得到某个字符
2、可以使用length获取字符串的长度
3、遍历字符串
4、数组不修改原数组的API,字符串也可以使用(拼接 - 垃圾还不如直接+运算,截取)
当然和数组也有很多的不同点,数组修改原数组的API,字符串一个都用不到,字符串也有十几个API等待我们学习
引用/对象类型:11个
*String Number Boolean - > 具有包装类型
*Array *Function Math(数学) Date(日期) *RegExp(正则表达式:验证)
Error(错误)
*Object(面向对象)
Global - 全局对象:在前端/浏览器端/客户端/js中被window代替了:功能:保存着全局变量和全局函数,只有window可以省略不写
包装类型:
专门用于将原始类型的值封装为一个引用类型的对象的(带了属性和方法)
为什么:原始类型的值原本是不具备任何属性和方法的,但是前辈们发现我们程序员会经常操作到字符串
前辈们为了方便我们程序员,为三个原始类型提供了包装类型
何时使用:只要试图用原始类型的变量去用.调用属性和方法时,就会悄悄的用上包装类型变为对象
何时释放:方法调用完毕后,自动释放包装类型,并且返回数据(又会变回原始类型)
为什么undefined和null不能用. - 没有提供包装类型
day10
String的API
转义字符:\
作用:
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、切割符"",切散每一个字符
如何使用JS生成元素:
1、创建空标签:var elem=document.createElement("标签名");
2、为此元素设置必要的属性 或 事件
elem.属性名="值";
elem.on事件名=function(){}
3、渲染页面,上DOM树:
父.appendChild(新);
day11
正则表达式
什么是:定义字符串中【字符出现规则】的一个表达式
何时使用:切割 替换【验证】
如何使用:语法:/正则表达式/
最简单的正则:
关键字原文 "no" -> /no/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)
指定匹配位置:
^:开头
$:结尾
特殊:如果两者同时出现,要求从头到尾完全匹配 - 只要是做验证,必须加上
密码验证:
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位,数字和字母,必须出现一位大写和一位数字和小写
字符串中支持正则表达式的API
切割:
var arr=str.split("固定切割符"/regexp)
替换:
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+"日";
})
正则对象:API
创建正则对象:
*直接量方式:var reg=/正则表达式/后缀;
构造函数方式:var reg=new RegExp("正则表达式","后缀")
为什么有时候要加 前加^后加$,为什么有时候又要加后缀g
验证:前加^后加$ - 用户要和我们的规则完全匹配
替换:加后缀g
API:var bool=reg.test(用户输入的东西);
返回true,说明用户验证成功,否则验证失败
onfocus获取焦点事件 -> 为了在后面的元素显示提示文字(不同的input有不同的提示文字) onblur失去焦点事件 -> 获取到用户输入的,和我们的正则(不同的input有不同的正则)进行验证:给出对的提示和错的提示 onsubmit提交事件是绑定在form上面的,阻止:return false; 后天交
day12
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
乘方和开方 *乘方: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
创建:
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
day13
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("自定义提示");
Function:
函数对象:提前创建好的,以后可以反复使用的代码段
创建:
1、声明方式:function 函数名(形参列表){函数体;return 返回值} - 完整的提前
2、直接量方式:var 函数名=function(形参列表){函数体;return 返回值}
3、构造函数方式:var 函数名=new Function("形参",.....,"函数体;return 返回值");
何时使用:函数体是动态拼接的
调用:
如果有return,记得接住结果
var 结果=函数名(实参);
考点:
重载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、了解了回调函数的原理
day14
函数的执行原理
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,逐级引用,形成的一条链式结构,就称之为叫做作用域链
作用:变量的使用规则,查找变量
闭包:
希望保护一个可以【反复使用的局部变量】的一种词法结构,其实函数一个函数,写法和以后有点不一样
如何使用:
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有一句话万物皆对象,假设一个人是一个对象的话:
属性:姓名、性别、身高、体重、爱好、智商...
方法:吃饭、睡觉、拉粑粑、跑步、走路、打字、说话...
为什么要面向对象:现实生活中所有的数据都要保存在一个事物中才有意义
何时使用面向对象:以后做任何操作都要封装在一个对象中 - 建议:不太适合初学者
封装对象
1、直接量方式:
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的指向