第一天
一、javascript慨述:
简称js,是一个运行在【js解释器】中的一个【解释型】【弱类型】【面向对象】脚本语言
1、js解释器:
1)浏览器自带解释器
2)以后会安装一个js解释器--Node.js
2、编译型:在运行程序之前,需要先检查程序是否有报错,如果有报错直接不运行,比如:java,c#,c++....严格
解释型:在运行程序之前,不需要检查程序是否有报错,直接运行,碰到错误就停止,比如JavaScript、php...自由
3、弱类型:变量想保存什么数据类型就保存什么数据类型,由数据决定了数据类型是什么,比如javascript、php... 自由
强类型:变量能保存什么数据类型,需要提前声明出来,有数据类型来决定了保存的数据是什么,比如:java... 严格
二、JavaScript特点:
1、可以用任何文本编辑器书写代码:HBuilder、VSCode、记事本开发都可以...
2、解释型
3、弱类型
4、面向对象:万物皆对象
对象名.属性名;
对象名.方法名();
三、JavaScript如何使用:
1、js的引入方式有两种:
1)在html中书写一个script标签:仅用于临时测试
2)创一个xx。js文件,再在html进行引入---正式开发中
如何引入:
建议:js放在我们的HTML和CSS的后面,最好是最后面
2、打桩输出 疯狂打桩:非常重要的一种找错方式:3种
1)在控制台输出日志:console.log(想要输出的内容);
2)在页面上输出:document.write(想要输出的内容);可以识别标签,缺点:如果有点击事件会触发document.write会导致页面上原来的html会全部被覆盖 3)用警告框输出:alert(想要输出的内容) 缺点:会卡主页面,只看到白板
3、变量和常量
1、变量:就是一个值可以发生变化的数据/量。
使用:如果以后某一个数据我们要反复使用,最好都把他保存程一个变量,以后只需要使用变量名相当于就是在使用变量值;
语法:
var 变量名=值;
特殊:
1、面试题:内存和变量有区别吗?
硬盘:保存数据/文件,机械硬盘和固态硬盘--外用
CPU:中央处理器--用于计算的
内存:是程序在运行过程中【临时】用到的数据
内存空间:实际保存数据的地方
没存地址:内存空间的一个门牌号
2、变量名其实不是随便的
1)不能以数开头
2)建议:尽量要见明之意 以英文为主:比如var name="xx";
3)推荐使用驼峰命名法
4)name是一个关键字,不管你保存的数据类型是什么,都会悄悄变成一个字符串
5)不能把关键字当做变量名
3、如果想要创建多个变量,没有必要每个写var,中间的变量都可以省略掉var关键字,中间用,间隔,最后一个还是用;结束
2、常量
一旦创建后,值不允许被修改
使用:用途和变量几乎一致,几乎不用
生活中的常量:pi:3.1415926
一天:24小时
语法:
const 常量名=值
4、算数运算符:+ - * / %
%:取余,俗称模,两个数相除,不去商,而取除不尽的余数
num%1 num%2
作用:
1、判断某个数字的奇偶性
如果结果为0说明是偶数,为1说明是奇数
2、取出一个数字想要的后n位
1234%10 -> 4
1234%100 -> 34
1234%1000 -> 234
特殊:
1、算数运算符具有隐式转换,默认:都是转为数字在运算
特殊在+运算,如果碰到了字符串,两边都会转为字符串,变成拼接操作
2、- * / % :其实字符串也能转为数字,但是前提必须是一个纯数字组成的字符串,但凡包含一个非数字字符,则为NAN:
Not a Number:直译,不是一个数字,但是确实是数字类型,但是不在三界之中
缺点:
1.NAN参与任何算数运算结果仍为NAN
2.NAN参与任何比较运算结果都为false
5、数据类型
原始/基本/值类型:5个
1、String:字符串,可以取无数个值,但是必须写上“”或"
2、Number:数字,取值有无数个,直接书写数字即可
3、Boolean:布尔,取值只有两个true(真,对)和false(假,错) 多半用于判断的结果
4、Underfined;变量如果声明了,但是没有赋值的话,默认值就为underfined,只有这一个值,没有任何用
5、Null:空,取值只有一个null,释放内存/变量
引用/对象类型:11个对象(属性和方法)
第二天
一:数据类型的转换:【一切的页面上获取来的数据类型都是字符串】
JavaScript是一个弱类型的语言,由数据 决定了 我们的数据类型是什么 1 number
"1" string
number + number = number
string + number = string
查看数据库类型:typeof(想要检查的变量);
1、隐式转换:悄悄地我们程序员看不见的转换
算术运算符具有隐式转换:
作用 :我们如果精通了隐式转换,我根本不需要console.log我也知道最后的结果是什么
默认:都是转为数字,在运算
特殊:
1、不管是什么运算,只要没碰上字符串,都会悄悄的转为一个数字
true->1
false->0
undefined->NaN
null->0 2、+运算并且碰上字符串,则为拼接,最后的结果也就还是一个字符串
3、-/%,就算是字符串也可以转为数字,前提是纯数字组成的字符串,但是如果包含了一个非数字字符,则为NaN "100a0" -> NaN*
NaN:Not a Number:不是一个数字,但是确实是数字类型,只不过不在三界之内
全是缺点:
1、不在三界之内,不大于,不小于,不等于,任何值,包括自己
2、参与任何算数运算+-*/%,结果仍为NaN
问题:正是因为NaN参与任何比较运算结果都为false,甚至自己都不认识自己
所以我们没有办法用一个普通的比较运算来判断x是不是NaN
解决:!isNaN(x):此方法不管你放的x是什么都会悄悄的隐式转换为数字
true->说明是一个有效数字
false->说明是一个NaN
目的: 防止用户恶意输入,但是目前为止,只能防止用户输入的必须是一个数字
2、显式转换:也叫作强制转换:
何时使用: 隐式转换出来的结果不是我们想要的,先强制转换为需要的数据,再运算
如何使用:
1、转为字符串:
var str=xx.toString();//xx不能是undefined和null,undefined和null不能使用.操作 因为一切的页面上获取来的数据类型都是字符串
2、转为数字:3种
1)parseInt(x); - parse解析 Int整型:解析为一个整数
执行原理:从左向右,依次读取每个字符,碰到非数字字符就停止,不认识小数点,如果一来就不认识则为NaN 去掉单位
2)parseFloat(x); - parse解析 Float浮点数:解析为一个小数
执行原理: 几乎和parseInt一致,但是认识第一小数点去掉单位
以上两个方法很重要: 但是x只能是数字或者字符串,不能是别的,如果是别的统一的认为是不认识的,不认识的结果就为NaN了
感觉,这两个方法是专门用于str to num
3)Number(x); - 万能的,任何人都可以转为数字,
此方法完全等效于隐式转换
其实隐式转换的底层,就是悄悄地使用了此方法,所以我们绝对不会手动使用 还不如 x-0 /1 *1 %1
二、Function:自定义函数:也称之为方法:需要【预定义好】的,可以【反复使用】的一个【代码段】
rotate(45deg) - 完成了一个根据角度值顺时针旋转45度的功能
url(图片路径) - 完成了一个根据图片路径显示图像的功能
js中的函数 - 完成了一个。。。。。。的功能
1、创建函数: function 函数名(){ 代码段;//若干操作 }
2、调用函数:
1、直接在js中程序员写几次调用,就会执行几次操作: 函数名();
2、让用户来自己触发:
<elem onclick="函数名()"></elem>
3、以后何时使用函数:
1)函数的js地位很高,属于第一等公民地位
2)你不希望打开页面立刻执行
3)能够反复执行
4)他是个独立的功能体
5)你不是自己释放内存,函数调用完毕会自动释放内存/变量
4、带参数的函数:
语法:
形参: 其实就是一个变量名,只不过不需要写var这个关键字,每个形参之间用,间隔,形式参数,简称形参
function 函数名(形参,...){
函数体;
}
调用: 实参:实际参数 函数名(实参,...);
function zwjs(name,age,hobby){
console.log("我的名字叫"+name+",今年"+age+"岁,喜欢"+hobby);
}
zwjs("张三丰",128,"打太极");
zwjs("张无忌",18,"撩妹");
zwjs("张三",28,"说法");
特殊: 实参的个数以及顺序 和 形参的个数以及顺序都要一一对应
5、普通函数: 操作永远是固定的
带参数的函数:根据传入的实参,执行略微不同的操作
三、分支结构:
1、代码流程控制语句:
1)顺序结构:默认的,代码从上向下
2)分支结构:根据条件不同,选择部分代码执行
3)循环结构:根据条件满不满足,考虑要不要再执行一次相同 或 相似代码
2、关系/比较运算符:> < >= <= == !=
结果:一定都是一个布尔值
注意:==才叫比较
=叫赋值:右边的东西放到了左边保存起来
3、逻辑运算符:
&&:与(并且):全部条件都满足,结果为true,只要有一个条件不满足,结果为false
||:或:全部条件都不满足,结果为false,只要有一个条件满足,结果为true
!:颠倒布尔值
4、分支的语法:
1、一个条件一件事,满足就做,不满足就不做
if(条件){
操作
}
2、一个条件两件事,满足就做第一件,不满足就做第二件
if(条件){
操作;
}else{
默认操作;
}
3、多个条件多件事:满足谁就做谁
if(条件1){
操作1;
}else if(条件2){
操作2;
}else if(条件3){
操作3;
}else{
默认操作;
}
else可以省略,但是不推荐
第三天
一、循环结构
解决:
循环结构:反复执行相同 或 相似的代码
吃饭,睡觉,做作业,上课,上班,活着
循环三要素:
1)循环条件:从哪里开始,到哪里结束
2)循环体:操作-要做什么事
3)循环变量、变量还要变化
1、while循环:其实循环从宏观上看是一起执行的,但是微观上看是一次一次执行的,只不过这个执行速度很快
语法:
var 循环变量=几;
while(循环条件){
循环体;
循环变量变化;
}
执行原理: 先判断循环条件满足吗,如果为true,则执行循环体一次,再一次判断循环条件满足吗,如果为true,则再执行循环体一次 .......直到循环条件不满足,才会退出循环
特殊:
死循环:永远不会结束的循环,但是还真用:往往不确定循环次数的时候就要使用
while(true){
循环体;
}
往往死循环还要搭配上,退出循环语句:break; - 只能写在循环里面
2、*for循环:
执行原理跟while一模一样,只是语法更加的简洁
for(循环变量的创建;循环条件;变量的变化){
循环体;
}
特殊:
1、循环变量的创建和循环变量的变化,其实可以写多个
2、死循环:for(;;){操作}
总结:
1、while:语法更加繁琐,建议只用于不确定循环次数的时候 - 死循环
2、for:语法更加简洁,建议只用于确定循环次数的时候 - 大部分情况
问题:函数 和 循环 都是可以反复执行的,区别在哪里?
函数 - 要么程序员调用几次,执行几次,或者,用户来触发几次,执行几次
循环 - 程序员写好的,而且几乎是一瞬间就执行完毕了的
二、数组:
** 问题**:保存1000个同学的姓名: var name1="周洁"; var name2="周杰"; var name3="周杰伦"; ...
以上写不合理:变量就是一个内存,我们开辟的内存空间太多的话,会影响网页效率
希望,一个变量名,就可以保存住所有的数据 - 数组:
数组里面的元素,是按照线性顺序排列的,除了第一个元素,每个元素都有一个唯一的前驱元素
除了最后一个元素,每个元素都有一个唯一的后继元素
每个元素都有一个唯一的位置序号,称之为【下标】:从0开始,到最大长度-1
1、创建数组:2种
1、*直接量方式:
var arr=[];//空数组
var arr=[元素,...];
2、构造函数方式:var arr=new Array();//空数组 - 个人不推荐,更麻烦,其实直接量方式是ES2过后才添加上的,原来只有构造函数方式
var arr=new Array(元素,...);
2、访问/获取:获取出数组中的某个元素数组名[下标]
3、添加/覆盖:
数组名[下标]=新元素;
特殊:
1、下标处没有元素,则为添加
2、下标处有元素,则为覆盖/替换
4、数组的三大不限制:
1)不限制元素的类型
2)不限制元素的个数
3)不限制下标越界 - 算是一个缺点: 获取时 - 下标越界,返回undefined
添加时 - 下标越界,会变成稀疏数组,导致下标不连续,不好
5、数组有一个唯一的属性:数组名.length; - 获取到数组的长度
三个固定套路:
1)向末尾添加元素:arr[arr.length]=新元素;
2)获取倒数第n个元素:arr[arr.length-n];
3)缩容:删除末尾n个元素:arr.length-=n;
6、其实经常我们不会去拿出数组中的某一个来做操作,往往是拿出所有的东西来做操作
遍历数组: 将数组中的每一个元素取出来执行 相同 或 相似的操作;
公式:
for(var i=0;i<arr.length;i++){
arr[i];//当前次元素
}
第四天
一、JavaScript其实由三部分组成:
1、ECMAScript(3/5/6)- 核心语法:我们天天在做应用题,语法很关键 - 内功心法
2、DOM - Document Object Model:文档对象模型,外功招式 - 操作文档(html/css)
3、BOM - Browser Object Model:浏览器对象模型,外功招式 - 操作浏览器(相对较少)- 兼容性问题
2、DOM概述:
Document Object Model:文档对象模型,外功招式 - 操作文档(html/css)
DOM树:
DOM将HTML看做了是一个倒挂的树状结构:树根其实是一个叫做:document的对象
document对象:
不需要我们创建,一个页面自带一个document对象,由浏览器的js解释器自动创建
作用:
只要是对象一定有两个东西(属性和方法):提供了我们找到元素&操作元素的方法
三、查找元素:
1、通过 HTML 特性去查找元素
1、通过ID查找元素:
var elem=document.getElementById("id值");//get得到 element元素 by通过
特殊: 1)如果有多个相同的id,只会找到返回第一个 - id是唯一不重复的
2)返回的结果:
3)你不准用这个方法 - 我们把id留给后端使用,而且id也不好用,一次只能找到一个元素
2、通过标签名查找元素:
var elems=document/parent.getElementsByTagName("标签名");
特殊:
1)返回的结果:不是一个DOM元素/节点/对象,是一个DOM集合,是不能直接用于做任何操作的
解决:
1、 要么用下标拿到某一个,要么用遍历拿到每一个
2、parent:代表的是你已经找到的某个父元素
3、通过class名查找元素:
var elems=document/parent.getElementsByClassName("class名");
1、返回的结果:不是一个DOM元素/节点/对象,是一个DOM集合,是不能直接用于做任何操作的
解决: 要么用下标拿到某一个,要么用遍历拿到每一个
2、parent:代表的是你已经找到的某个父元素
2、通过 关系 获取元素:前提:至少要先找到一个元素:
父元素:elem.parentNode; - 单个元素
子元素:elem.children; - 集合:集合不能直接做操作
第一个儿子:elem.firstElementChild;
最后一个儿子:elem.lastElementChild;
前一个兄弟:elem.previousElementSibling;
后一个兄弟:elem.nextElementSibling;
四、操作元素:
<div class id title href style="样式">内容</div>
1、内容:
1)innerHTML:获取 或 设置元素的内容部分,并且能够识别标签
获取: elem.innerHTML;
设置: elem.innerHTML="新内容";
2)innerText:获取 或 设置元素的文本部分,不能识别标签
获取: elem.innerText;
设置: elem.innerText="新文本";
以上两个操作都是针对双标签使用
3)value:获取 或 设置input的内容部分
获取: input.value;
设置: input.value="新文本";
2、属性:
1、获取属性值:
elem.getAttribute("属性名");
简化版:elem.属性名;
2、设置属性值:
elem.setAttribute("属性名","属性值");
简化版:elem.属性名="新属性值";
简化版虽然简单,但有缺陷:
1、class必须写为className
2、只能操作标准属性,不能操作自定义属性
建议:优先使用简化版,简化版满足不了,再用复杂版做补充
3、样式:
样式有三种使用方式:1、内联,2、内部,3、外部 -
符合内容HTML与样式CSS与行为JS的分离原理
二阶段:更喜欢操作内联样式:
1、内联的优先级最高 - 保证我们js写的样式必然生效
2、只会操作当前元素 - 不会牵一发而动全身
如何: ** 获取:** elem.style.css属性名;
设置: elem.style.css属性名="css属性值";
特殊:
1、设置css属性名如果有横线要去掉横线,变为小驼峰命名法
2、获取只能获取到内联样式
获取: 往往用于判断
设置: 其实就是修改
五绑定事件:
符合内容HTML与样式CSS与行为JS的分离原理
单个元素:
elem.onclick=function(){
this->就是绑定事件的这个元素
}
多个元素:
for(var i=0;i<elems.length;i++){
elems[i].onclick=function(){
this->是当前触发事件的元素
}
}
第五天
一、***数据类型转换
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、隐式转换:都是出现在运算符之中
二、运算符和表达式:
算术运算符:+ - * / %
特殊:
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);
||短路: 浏览器兼容:二选一操作
e=e||window.event;
4、位运算:
左移: m<<n,读作m左移了n位,翻译:m*2的n次方
右移: m>>n,读作m右移了n位,翻译:m/2的n次方
缺点: 底数只能2
5、赋值运算:+= -= *= /= %=
一个操作两件事,先计算,在赋值回去。
i++;//递增:每次只会固定+1
i+=1;//累加:每次+几由我们程序员自己决定
i++ === i+=1 === i=i+1;
鄙视题:++i和i++的区别:
单独使用,放前放后无所谓,效果一样。
但是如果参与了别的表达式,变量中的值都会+1
前++返回的是加了过后的新值
后++返回的是加了之前的旧值
6、三目运算:简化if...else 简化if...else if...else
语法:
1、条件?操作1:默认操作;
2、条件1?操作1:条件2?操作2:条件3?操作3:默认操作;
特殊:
1、只能简化简单的分支 - 操作只能有一句话 、默认操作不能省略
总结:
if === &&短路
if else === 三目
if else if else === 三目
扩展:
1、计算机带有摄入误差:
解决:
num.toFixed(d);//d代表保留的小数位数,会四舍五入自动进制
缺陷:
搭配上一个parseFloat使用最好,结果返回的是一个字符串
2、获取字符串中第一个字符的ascii码:str.charCodeAt(0);
第六天
一、*****自定义Function:
1、什么是函数:
需要先定义好,可以反复使用的一个代码段
何时使用:
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、***按值传递:
两个变量之间赋值,分两种情况
如果传递的是原始类型的值:
修改一个变量,另一个变量是不会受到影响的,其实是复制了一个【副本】给对方
如果传递的是引用类型的对象:
修改一个变量,另一个变量是会受到影响的,引用类型其实根本没有保存到变量中,仅仅只是保存了一个地址值
两者用的是同一个地址值,所以会相互影响
二、预定义全局函数:
定义:
前辈们提前定义好的,我们程序员可以直接使用的,在任何位置都可以使用
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
三、***分支结构:根据条件的不同,选择部分代码执行
**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、
*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 - 专门为遍历数组准备的
二、*****数组的基础:
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数组】
三、*****数组的API
定义: 数组的函数,前辈们定义好的,只有*数组可以使用:
1、*****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是识别标签的
sel.innerHTML="<option>"+cities.join("</option><option>")+"</option>";
2、拼接数组:添加元素的新方式:
语法: var newArr=arr.concat(值1,arr1,...);
特殊:
1、此方法不修改原数组,只会返回一个新数组
2、支持传入数组参数,悄悄的将我们传入的数组打散,不会变成二维数组
3、截取子数组:取出数组中想要的某一部分组成的一个新数组
语法: var subArr=arr.slice(starti,endi);
特殊:
1、此方法不修改原数组,只会返回一个新数组
2、含头不含尾
3、第二参数可以省略不写,截取到末尾
4、第一个参数也可以省略不写,如果两个参数都没写,从头截取到尾 - 深拷贝(复制了一份副本)
5、支持负数参数,-1代表倒数第一个
以上的API都不会修改原数组
以下的API都会修改原数组
4、删除、插入、替换:
删除: var dels=arr.splice(starti,n);//n代表删除的个数
特殊: 此方法其实也有返回值,返回的就是你删除的元素组成的一个新数组
插入: arr.splice(starti,0,新值1,....);
特殊:
1、没有删除,也有返回值,返回的是一个空数组
2、原starti位置的元素以及后续元素都会向后顺移
3、不建议插入数组,会变得不伦不类
替换:
var dels=arr.splice(starti,n,新值1,....);
特殊: 删除的个数和插入的个数不必相同
5、反转数组:arr.reverse();
第八天
一、*****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]);
}
}
三、*****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不能用. - 没有提供包装类型
拓展
周期性定时器:每过一段时间就会自动执行一次
开启:
timer=setInterval(function(){ //不要写var
操作;
},间隔毫秒数)
停止: clearInterval(timer);
鼠标移入: onmouseover
鼠标移出: onmouseout
第九天
一、StringAPI:
只有字符串可以使用的函数,特点:只读!
1、转义字符:\
作用:
1、将字符串中和程序冲突的符号编译为原文""" '''
2、包含特殊功能的符号:
换行:\n
制表符:\t
*3、输出unicode编码的字符:
\uXXXX:第一个汉字:4e00 -ascii:19968
最后一个汉字:9fa5 -ascii:40869
2、*大小写转换:
将字符串中的每个英文统一的转为大写 或 小写
何时: 只要程序不区分大小写,就要【先统一】的转为大写 或 小写,再比较(验证码)
如何:
大写:var upper=str.toUpperCase();
小写:var lower=str.toLowerCase();
3、获取字符串中指定位置的字符:
str.charAt(i) 还不如直接 str[i]
4、获取字符串中指定位置的字符的ascii码:
var ascii=str.charCodeAt(i);
根据ascii码在转回原文:
var 原文=String.fromCharCode(ascii);
5、***检索字符串:检查索引:获取关键字的下标
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);
}
6、*截取字符串:3种
*var subStr=str/arr.slice(starti,endi+1);//用法和数组的slice一摸一样
str.substring(starti,endi+1);//几乎和slice一致,不支持附属参数
*str.substr(starti,n);//n代表截取的个数,不必考虑含头不含尾
7、拼接字符串:var newStr=str.concat(新字符串,...);//还不如+运算
8、*替换字符串:本身强大,但是必须搭配正则表达式
var newStr=str.replace("关键字","新内容");
9、*****切割/分割字符串:
var arr=str.split("任意切割符");
作用: 将字符串=>数组
特殊:
1、切割后,切割符就不存在了
2、切割符"",切散每一个字符
扩展:
如何使用JS生成元素:3步
1、创建空标签:
var elem=document.createElement("标签名");
2、为此元素设置必要的属性 或 事件
elem.属性名="值";
elem.on事件名=function(){}
3、渲染页面,上DOM树:
父.appendChild(新);
第十天
一、*****正则表达式:
定义: 定义字符串中【字符出现规则】的一个表达式
何时使用: 切割 替换【验证】
如何使用:
语法:/正则表达式/
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]+)[0-9A-Za-z]{4}$/; - 4位,数字和字母,必须出现一位大写和一位数字
/^(?![0-9a-z]+)(?![A-Z0-9]+/ - 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+"日";
})
三、*****正则对象: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、取整: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
三、Number对象
四、Boolean对象
*String Number Boolean - > 具有包装类型
*Array *Function Math(数学) Date(日期) *RegExp(正则表达式:验证)
Error(错误)
*Object(面向对象)
Global - 全局对象:在前端/浏览器端/客户端/js中被window代替了:功能:保存着全局变量和全局函数,只有window可以省略不写
第十二天
一、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、创建: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、了解了回调函数的原理
7、闭包 ,面试题:两链一包(作用域链、原型链、闭包)
第十三天
一、*****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();
对象: Array/String/Function/Math...对象具有属性和方法,都是预定义好,现在我们学习自定义对象
二、*****面向对象:Object:
三大特点:封装、继承、多态
1、***开发方式:
面向过程: 经过 - 开始->结束,其实我们一直以来的开发方式都是面向过程:先干什么再干什么最后干什么
面向对象: 对象(属性和方法),js有一句话万物皆对象,假设一个人是一个对象的话:
属性:姓名、性别、身高、体重、爱好、智商...
方法:吃饭、睡觉、拉粑粑、跑步、走路、打字、说话...
为什么要面向对象: 现实生活中所有的数据都要保存在一个事物中才有意义
何时使用面向对象: 以后做任何操作都要封装在一个对象中 -
建议: 不太适合初学者
2、封装对象:创建自定义对象:3种
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->外部对象
2、预定义构造函数方式:
var obj=new Object();//空对象
//需要自己后续添加属性和方法
obj.属性名="属性值";
obj.方法名=function(){}
以上两种方法创建都有一个缺陷:一次只能创建一个对象,适合创建单个对象的时候使用(第一种方式)
3、自定义构造函数方式: - 何时创建多个对象:2步
1、创建自定义构造函数
function 类名(name,age,salary){
this.name=name;
this.age=age;
this.salary=salary;
}
2、反复调用自定义构造函数创建出对象
var xxx=new 类名("实参",...)
面向对象:
优点:
1、所有的属性和方法都是保存在一个对象之中 -更符合现实生活:代码风格、代码速度
2、每个功能特地的分开写 - 便于维护
3、铁锁连舟 - 一个方法触发多个方法联动
缺点:
对新手不友好 - this的指向
第十四天
一、*****Object:
1、继承: 父对象的成员(属性和方法),子对象可以直接使用、
为什么:代码重用!节约内存空间!提升网站的性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中
2、如何找到父对象(原型对象): 保存一类子对象共有属性和共有方法的父对象:2种
1、子对象.proto;//前提:必须先有一个对象
2、构造函数名.prototype;//构造函数名,几乎人人都有,除了Math/Undefined/Null
3、面试题:两链一包:
作用域链:查找变量
闭包:保护一个可以反复使用的局部变量的一种词法结构
原型链:每个对象都有一个属性:proto,可以一层一层的找到每个人的父亲,形成的一条链式结构,就称之为叫做原型链
可以找到所有父对象的成员(属性和方法),作用:查找属性和【方法】
最顶层是Object的原型,上面放着我们眼熟的toString,怪不得人人都可以使用toString
JS种万物皆对象
4、有了原型对象,设置共有属性和共有的方法
1、原型对象.属性名=属性值
2、原型对象.方法名=function(){}
笔试题1:判断自有和共有:
1、判断自有: obj.hasOwnProperty("属性名");
如果结果为true,说明一定是自有,如果结果为false,可能是共有可能是没有
2、判断共有:
if(obj.hasOwnProperty("属性名")==false&&"属性名" in obj){//in 会自动在整条圆形脸上进行查找,找到结果为true,找不到结果为false
说明是共有
}else{
说明是没有
}
完整版公式:
if(obj.hasOwnProperty("属性名")){
自有
}else{
if("属性名" in obj){
共有
}else{
没有
}
}
笔试题2:修改/删除自有和共有
自有:修改:obj.属性名=新值;
删除:delete obj.属性名;
共有:修改:obj.proto.属性名=新值;
删除:delete obj.proto.属性名;
共有千万不要操作本地:
修改:会在本地添加同名属性
删除:没有效果
笔试题3:如何为老IE的数组添加indexOf方法:如何为一类人添加某个方法
原理:
if(Array.prototype.indexOf===undefined){//老IE
Array.prototype.indexOf=function(key,starti){
starti===undefined&&(starti=0);
for(var i=starti;i<this.length;i++){
if(key==this[i]){
return i;
}
}
return -1;
}
}
**笔试题4:判断x是不是一个数组:4种方式 **
1、判断x是不是继承自Array.prototype: Array.prototype.isPrototypeOf(x);
2、判断x是不是由Array这个构造函数创建的
x instanceof Array
3、只有数组可用:ES5提供的一个叫Array.isArray(x);
4、最麻烦:输出【对象的字符串】形式
在Object的原型上保存着最原始的toString方法
最原始的toString方法输出:[object 构造函数名]
多态:子对象觉得父对象的成员不好用,在本地定义了同名成员,覆盖了父对象的成员
如果我们这道题能够跳过数组的爸爸,拿到数组的爷爷上面的toString也就能判断了
固定套路:借用:Object.prototype.toString.apply(x);
笔试题5:实现自定义继承:
1、两个对象之间的继承:
子对象.proto=父对象
2、多个对象之间设置继承:
构造函数名.prototype=父对象
注意时机:一定要在创建对象之前
第十五天
一、ES5带来了新特性:带来了一些新的API操作
1、保护对象:保护对象的属性和方法
如何保护:2种方式
1、四大特性:每一个属性都有四大特性
{
value: 1001, //实际保存值的地方
writable: true,//开关,控制着此属性是否可以被修改
enumerable: true,//开关,控制着此属性是否可以被for in循环遍历到
configurable: true,//开关,控制着此属性是否可以被删除
}
如何设置四大特性:
Object.defineProperties(对象名,{
"属性名":{四大特性},
...
})
2、三个级别:
1、防扩展: 禁止给对象添加属性:
Object.preventExtensions(obj);
2、密封: 禁止给对象添加属性和删除元素
Object.seal(obj);
3、冻结: 禁止给对象添加属性和删除元素和修改元素 Object.freeze(obj);
其实保护对象对于我们程序员来说可有可无:
1、如果你用的是面向过程,你保护个屁
2、别人基本不可能知道我们的对象叫什么名字
3、前辈们几乎都没有保护过对象,你保护个屁
2、*****数组的新的API:3组6个
1、判断:2个:
1、every 每一个 - 要求每一个元素都要满足我们的条件,结果才为true,只要有一个不满足结果就为false,类似于&&
var bool=arr.every(function(val,i,arr){
return 判断条件
})
val - 当前元素
i - 当前元素的下标
arr - 数组本身,前辈们提供了3个,不代表3个对我都有用
2、some 有一些 - 要求只要一个元素都要满足我们的条件,结果就为true,只要全部不满足结果就为false,类似于||
var bool=arr.some(function(val,i,arr){
return 判断条件
})
2、遍历:把每一个元素拿出来执行相同 或 相似的操作:2个 1、forEach:遍历数组,直接修改原数组
arr.forEach(function(val,i,arr){
操作
})
2、Map:遍历数组,不修改原数组,返回一个新数组
var newArr=arr.map(function(val,i,arr){
return 操作
})
3、汇总和过滤:
过滤:不会修改原数组,但是会得到你过滤出来的新数组
var subArr=arr.filter(function(val,i,arr){
return 判断条件;
})
汇总:把数组中的每一个元素汇总到一起
var sum=arr.reduce(function(prev,val,i,arr){
return prev+val;
},基础值)
以上6个API底层都是for循环,目的 - 简化for循环,暂时可能还会用到一些for循环
3、Object.create():根据父对象创建子对象,继承已经设置好了
语法:var 子对象=Object.create(父对象,{
"自有属性名":{四大特性},
...
});
4、面试时:严格模式:很严格
开启:在你的任何作用域的顶部加上一句话:"use strict";
功能:1、禁止给未声明的变量赋值 - 解决全局污染
2、静默失败升级为报错
5、call/apply/bind:不是自己的方法也可以使用 - 笔试面试也很容易碰到
call/apply:临时替换了函数中的this - 借用
语法: 函数名.call(借用的对象,实参,...); - 单独传入每一个实参
函数名.apply(借用的对象,arr); - 只能传入一个实参是一个数组,apply其实会悄悄的将数组打散
强调: call/apply,相当于立刻调用函数,立即执行
bind:永久替换了函数中的this - 买
3件事:
1、创建了一个和原函数完全相同功能的新函数
2、将新函数中的this永久绑定为了指定对象,别人都借不走了
3、将新函数中的部分参数永久固定
用法:var 新函数=原函数.bind(指定对象,永久实参,...) - 不是立刻执行的,需要自己调用
强调:bind绑定的新函数没有办法被call/apply借走
推荐:call/apply,借,白嫖
固定套路:
1、Math.max/min.apply(Math,arr);
2、Object.prototype.toString.call/apply(arr);
3、***类数组转为普通数组:类数组名称=Array.prototype.slice.call/apply(类数组);
ES5其实没有什么简化,是提供了一些API
二、ES6:简化ECMAScript - 语法较大的变化
1、模板字符串:
可以在字符串中放入变量 - 不需要再做字符串拼接,在字符串中实现了一个简单的js的环境
语法: 我的名字叫${name}
2、*块级作用域:尽量以后【优先】使用let创建变量
let 变量名=值;
作用:
1、禁止声明提前
2、添加了块级作用域,一个{}就是一个块
3、记录着当前触发事件的元素的下标
3、***箭头函数:简化回调函数:
公式: 去掉function,()和{}之间添加=>,形参如果只有一个,则省略掉(),函数体如果只有一句话,省略{},
函数体只有一句话并且是return,return和{}都省略
特殊: 千万不要将事件也简化为箭头函数 - 暂时
4、for...of循环:垃圾
for(var v of 数组名){
v;//value当前值
}
缺点:
1、不能修改原数组,只能返回新数组
2、不能遍历hash数组,意味着也不能遍历对象,只能遍历索引数组
第十六天
一、什么是DOM:
Document Object Model(文档对象模型) - 操作HTML文档
将每个元素/标签/文本/属性/注释都会看作是一个DOM节点/元素/对象
面试题: HTML/XHTML/DHTML/XML?
1、HTML - 网页
2、XHTML - 更严格的网页
3、DHTML - Dynamic:动态的网页,其实并不是新技术,也不是新概念,而是现有技术的一个整合统称:让我们的网页再离线版也能具有动态效果
DHTML:HTML+CSS+JS
4、XML - 全部自定义,数据格式:淘汰了:JSON数据格式
DOM: 原本是可以操作一切结构化文档的 HTML 和 XML,后来为了方便各类开发者分为了3个方向
1、核心DOM:【无敌】,既可以操作HTML也可以操作XML
缺点:API比较繁琐,getAttribute/setAttribute()
2、HTML DOM:只能操作HTML,API简单:缺点:自定义的东西操作不了
3、XML DOM:只能操作XML - 不会学习他
开发建议:优先使用HTML DOM,HTML DOM实现不了再用核心DOM补充
2、DOM树:
树根:document - 不需创建,一个页面只有一个document,由js解释器自动创建
作用: 通过树根,可以找到想要的DOM元素/节点/对象
3、每个DOM元素都有三大属性:
1、elem.nodeType:描述节点类型
document 节点:9
element 节点:1
attribute 节点:2
text 节点:3
以前有用: 判断xx是不是一个页面元素 - 因为以前我们的方法和你们现在的方法不一样
2、elem.nodeValue:获取【属性节点】的值
3、elem.nodeName:获取节点名称/标签名
注意: 返回的是一个全大写组成的标签名
4、通过 关系 获取元素
父:xx.parentNode
子:xx.children - 只能找到儿子
第一个儿子:xx.firstElementChild;
最后一个儿子:xx.lastElementChild;
前一个兄弟:xx.previousElementSibling;
后一个兄弟:xx.nextElementSibling;
5、递归:
简单来说就是函数中再次调用了函数本身,迟早有一天会停下来
何时使用: 遍历DOM树,专门用于【遍历层级不明确】的树状结构
如何使用:
function 函数名(根){
1、第一层要做什么直接做
2、判断它有没有下一级,如果有下一级,则再次调用此函数,但是传入的实参是自己的下一级
}
函数名(实际的根)
算法: 深度优先!优先遍历当前节点的子节点,子节点遍历完才会跳到兄弟节点
缺点: 同时开启大量的函数调用,浪费的内存,只有一个情况:【遍历层级不明确】
**递归 vs 循环 **
递归:优点: 直观,易用
缺点: 性能较差
循环:优点: 性能较好
缺点: 难得一批
6、遍历层级不明确的API: TreeWalker:一个在DOM树上行走的人
缺点: 只能遍历层级不明确的DOM树,不能遍历层级不明确的数据
如何使用:2步
**1、创建TW **
var tw=document.createTreeWalker(根元素,NodeFilter.SHOW_ELEMENT);
2、反复调用tw的nextNode函数找到节点
while((node=tw.nextNode())!=null){
node要干什么
}
7、直接找元素:
1、通过HTML的一些特点找元素
你会的:id/class/标签
新的: 通过name找元素:
var elems=document.getElementsByName("name值")
2、通过css选择器查找元素:
2个方法 - 更适合用于复杂查找(多句才能找到最终元素)
1、var elem=document.querySelector("任意css选择器");
只会找到一个元素,如何同时匹配多个,也只会返回第一个
没找到null
2、var elem=document.querySelectorAll("任意css选择器");
找到了是一个集合
没找到是一个空集合
面试题:getXXXX和queryXXXX的区别?
返回的结果不一样:
1、getXXXX: 返回的是一个动态集合HTMLCollection
特点: 每一次DOM树修改过后,js都会悄悄的再次查找元素,保证数据和页面对应,效率相对较低
2、queryXXX: 返回的是一个静态集合Nodelist
特点: 只管查找的一次找出来的结果,后续DOM树的修改,我们的数据也不会变化,效率相对较高
优点:
1、查找简单
2、还支持forEach
总结:找元素的方式:
1、直接找元素:getXXX、queryXXX
2、通过节点的关系
3、层级不明确的情况 - 递归
第十七天
操作元素
一、元素的内容
1、 elem.innerHTML:
获取或者设置开始标签到结束标签之间的HTML代码,没有兼容性问题,可以识别标签
获取:elem.innerHTML
设置:elem.innerHTML="新内容"
2、elem.textContent:
获取或者设置开始标签到结束标签之间的纯文本,但是有兼容性问题,不可以识别标签
获取: elem.textContent
设置: elem.textContent="新文本"
特殊: 老IE才叫elem.innerText;//第一见到小三上位,反而所有的主流浏览器来将就了老IE
3、input.value:
获取或设置单标签(input)的内容准备的
获取:input.value
设置:input.value="新值"
二、元素的属性:
1、获取属性值
核心DOM:elem.getAttribute("属性名")
HTML DOM:elem.属性名;
2、设置属性值:
核心DOM:elem.setAttribute("属性名","属性值");
HTML DOM:elem.属性名="新值";
3、删除属性值:
核心DOM:elem.removeAttribute("属性名")
HTML DOM:elem.属性名="" - 虽然简单,但是有的情况会删不干净,有的属性删不干净依然会有功能比如:href
4、判断有没有 - 垃圾
核心DOM:elem.hasAttribute("属性名");
HTML DOM:elem.属性名!="";
返回的结果是一个布尔值,只能判断有没有,不能判断是什么
HTML DOM:有两个小缺陷:
1、class需要写为className
2、一切自定属性不能操作
3、删除删不干净,依然会留下属性节点
三、元素的样式:
1、内联样式:
获取:elem.style.css属性名
设置:elem.style.css属性名="css属性值"
特殊:
1、css属性名如果有横线要省略,变为小驼峰命名法
2、获取的时候只能获取到内联样式,这个小缺陷可以忽略不计
2、样式表: - 不推荐
获取到你想要操作的某个样式表
var sheet=document.styleSheets[i];
获取样式表中所有的样式规则
var rules=sheet.cssRules;
选出自己想要操作的某个规则
var rule=rules[i];
操作
获取: console.log(rule.style.css属性名);
设置: rule.style.css属性名="css属性值"
为什么更推荐内联:
1、不会牵一发动全身
2、优先级一定是最高的
第十七天 下
1、创建元素&渲染页面:3步
1、先创建空标签
var elem=document.createElement("标签名");
2、为其添加必要属性和事件
elem.属性名="属性值";
elem.on事件名=function(){操作
}
3、渲染DOM树/页面:3种
1、父元素.appendChild(elem);//elem追加到父元素里面,当最后一个儿子
2、父元素.insertBefore(elem,已有子元素);//elem追加到父元素里面,插入在已有子元素的前面 - 影响其他元素的下标,不推荐
3、父元素.replaceChild(elem,已有子元素);//elem追加到父元素里面,新元素替换掉已有子元素 - 不推荐
2、删除元素:elem.remove();
HTML DOM:简化核心DOM开发,核心语法太繁琐,提供了一些常用的对象
1、image对象: 垃圾,仅仅只是简化了创建部分
创建:var img=new Image(); - 不是人人都可以简化为构造函数创建方式
2、form对象: 垃圾,仅仅只是简化了查找部分
查找form元素:var form=document.forms[i];
以及找表单控件:var inp=form.elements[i];
3、select对象: 牛逼:提供了2个属性,2个方法,1个事件
属性:
1、select.options === select.children; 获取select里面的所有的option
2、select.selectedIndex - 获取当前选中项的下标,一定是搭配联动一起使用
方法:
1、sel.add(option); - 完全相当于appendChild
2、elem.remove(i); - 当时当remove删除元素出现过后,他就当场淘汰了
专属事件: onchange - 选中项发生了变化才会触发
4、option对象: 仅仅只是简化了创建部分
创建: var opt=new Option("innerHTML","value")
一句话完成4个操作:sel.add(new Option("榴莲","榴莲"));
1、创建了空的option
2、为option设置了innerHTML
3、为option设置了value
4、上树
拓展
用户确认框: var bool=confirm("提示文字");