一.js基础
1.js的介绍:js是一个运行在服务器端的解释型,弱类型的面向对象的脚本语言,由三部分组成:ECMAScript(核心语法3/5/6/7/8/9/10/11/12),DOM(和网页挂钩),BOM(和浏览器挂钩)
tips: 1.运行环境:浏览器,打开浏览器就自动运行
2.解释型:在代码运行之前不会检查代码,直接运行,遇到错就报错
3.弱类型:保存的变量可以随意
作用:可以做一切css实现不了的效果
2.js的使用方法:
1.在HTML中写一个script标签,在标签文本中写js内容
2.创建一个.js的文件,在HTML中用script标签的src属性来引入.js文件,在.js文件里写Js
3.输出方式:
1.控制台输出:console.log();
2.页面输出:document.write();此方法支持标签写法,但是搭配点击事件时会把网页内容变没
3.在警告框输出:alert();多次刷新页面时可能会卡住整个页面
4.变量:创建后用于存放值,可以再次修改
语法:var 变量名=值;
作用:之后会反复用到的数据就存到变量里方便以后调用
tips:
1.变量名不能随意。应遵守以下规则:不能以数字开头,建议使用驼峰命名法,尽量见名知意
2.如果变量名是name,就会自动把内容变成字符串类型
3.变量名不能是关键字
4.如果变量只创建不赋值,默认会变成undefined,undefined做不了任何操作
5.可以多个变量名同时创建: var 变量1=值1,变量2=值2...;
5.数据类型
1.值类型
①.string:字符串类型,取值有无数个,必须加上""或''
②.number:数字类型,取值有无数个,直接写
③boolean:布尔型,取值只有true和false,用于做判断
④null:空类型,唯一的作用是释放内存,提升网页的性能
⑤undefined:取值就是undefined,是一个没用的东西,拿来做什么操作都不行
特殊:NaN是数字类型,不是一个有效的数字,没有优点有两个缺点,参与任何的运算都是NaN,参加任何的比较运算都为false
*****页面上的一切数据js获取到之后都是字符串类型的
强制类型转化的方式:
1.字符串两种:
①.var str=x.toString(); x不可以是undefined和null,会报错,它们不能做任何操作,因为它们不是对象
②.var str=String(x);万能的,但是绝对不要手动去用,完全等效于隐式转换
2.数字三种:
①.parseInt(str/num);专门用于将字符串转换为数字,从左到右依次转,遇到不是数字的字符就会停止,如果一来就不认识就会识别为NaN,不认识小数点
②.parseFloat(str);专门用于把字符转化为小数,认识小数点
②.Number(x);万能的,但是绝对不要手动去用,完全等效于隐式转换
3.转布尔:
Boolean(x);万能的,记住为false的东西只有6个:0,"",null,undefined,false,NaN,绝对不要手动去用,在分支结构或者循环结构的条件中会隐式转换为布尔值,只要记得这6个为false
2.引用类型
string Number Boolean Array Function Date Math Regexp(正则表达式) Error Object Global(全局对象)
二.运算符 *
1.算术运算符:+ - * / %
特点:
1.带有隐式转换,会悄悄的把数据的类型变为数字类型:true -> 1 ,false -> 0 ,undefined ->NaN,null -> 0
2.%是取余符号,通常用于做奇偶判断或者用于取数字的后n位
3.+运算符比较特殊,当+的左右两边有字符串的时候,会悄悄的隐式转换左右两边的数据为字符串,此时做的不是+运算而是将两个字符串拼接起来
2.比较运算符:> < >= <= == != === !==
特点:比较运算符的结果一定是个布尔值也带有隐式转换,默认左右为数字,一般用于做判断
tips:
①.如果比较运算符的左右两边都是字符串,则会按位PK两边每个字符的十六进制unicode号(十进制ASCLL码),一般:数字0-9<大写字母<小写字母<汉字。
汉字的第一个字是:一 unicode号是4e00 ascll码是19968
汉字的最后一个字是:龥 unicode号是9fa5 ascll码是40869
②.NaN参与任何的比较运算结果都为false,判断一个值是不是NaN:isNaN()一般前面加!用于判断
③.undefined==null的结果为true,它们做比较的时候要用===,全等运算符的意思为不带隐式转换的比较,即值要相等类型也要相等(!==为不全等)
3.赋值运算符:= += -= *= /= %=
特点:
1.= 意为把=右边的东西赋值给左边
2.其余的是=的升级版,意为先与=后面的东西做计算再将得到的值赋值给变量本身
4.逻辑运算符:&& || !,综合比较,用于写条件(结果一定是一个布尔值)
特点:
&&(与):当全部的条件都满足时就为true,否则为false
||(或):当其中一个条件满足时就为true
!(非):用于颠倒布尔值
5.自增自减运算符:++ --,每次只能增加或减少1
注意:单独使用++或--时,放在值的前后都没有影响,但是参与了其他的表达式返回的结果就会不同,前++返回的值是新值,后++返回的值是旧值,只要使用了值都会变化
6.位运符:>> <<
<<:左移:m<< n,m左移了n位意为m*2的n次方
>>:右移:m>>n,m右移了n位意为m/2的n次方
三.分支结构
1.程序的流程结构
①.顺序结构:默认的,从上往下依次执行
②.分支结构:通过条件判断,选择部分代码执行
③.循环结构:通过条件判断,选择要不要重复执行某一个块代码
2.分支结构(三种)
if...else分支结构:
特点:else if可以写多个,取决于自己,可以做范围判断
1.一个条件一件事:if(条件){
操作;
}
2.一个条件两件事:if(条件){
操作1;
}else{
操作2;
}
3.多个条件,多件事:if(条件1){
操作1;
}else if(条件2){
操作2;
}else if(条件3){
操作3;
}
switch...case分支:
特点:1.问题:默认只要有一个case满足后,会把后面的操作都执行一遍
解决:在每个case操作后面加break(跳出判断)
2.case在做比较的时候不带有隐式转换
3.default可以省略,但是不推荐,不写的话条件都不满足时什么都不会发生
switch(变量/表达式){
case 值1:
操作1;
break;
case 值2:
操作2;
break;
default;
默认操作;
}
if和switch的区别:
switch的优点:效率高,速度快(等值比较)
缺点:必须要知道结果值才能做判断
if的优点:可以做范围判断
缺点:效率低,速度慢
3.三目运算:简化if...else分支
注意:1.默认操作不能省略,省略会报错;
2.如果操作复杂,不能使用三目运算,操作只能有一句话。
条件1?操作1:条件2:操作2:默认操作
3.循环结构:反复执行相同或者相似的操作
循环三要素:
1.循环条件:开始-结束,循环次数
2.循环体:做的操作
3.循环变量:记录了当前操作的次数,会不断变化
循环的原理:当循环条件满足时,执行一次操作,同时循环变量变化,不会退出操作,回过头去再次判断循环的条件满足不满足,满足再继续,不满足就跳出循环。
死循环:没有循环条件或者循环条件能一直满足的循环,循环便不会停止的一直做下去,当我们不确定循环的次数的时候需要用到死循环,死循环也会停止,当使用了break就能让循环停止
break:退出整个循环
continue:退出本次循环,下一次循环依然执行
三种循环结构:
1.while循环:
语法:
var 循环变量=xxx;
while(循环条件){
循环体;
循环变量的变化;
}
while的死循环:
while(true){
循环体
}
2.for循环:
语法:
for(变量;条件;变量的变化){
循环体;
}
for的死循环:
for(;;){
循环体;
}
3.do...while循环 (没啥用,基本不写)
语法:
var 循环变量=几;
do {
循环体=xxx;
循环变量的变化;
}while(循环条件){
}
区别:当第一次循环不满足时while循环不会执行,do...while循环会执行一次;
四.函数
1.概念
function:函数也叫方法,事先预定好以后可以反复调用的代码段。
2.语法:
1.定义函数(两种)
1.声明方式 function 函数名(形参){
函数体;
}
2.直接量方式
var 函数名=function(形参){
函数体;
}
2.调用函数:
函数名(实参列表);
3.作用:
1.不希望页面一打开就立刻执行
2.希望用户来触发提升用户的体验感
3.函数是js的第一公民,以后每一个独立的功能都要封装成函数
tips:
1.调用带有参数的函数时,必须要传入实参且顺序要一一对应,数量不能多也不能少。
2.当函数调用完后变量会自动被释放
4.作用域(两种) *****
1.全局作用域:全局变量和全局函数,在页面的任何一个位置都可以使用。
2.函数作用域:局部变量和局部函数,在当前函数调用时,内部可以使用
变量的使用规则:优先使用局部的变量,没有局部变量就找全局的,都没有就报错。
缺点:
1.全局污染:全局本身没有变量,但是被函数作用域添加了
解决:不要对函数内未声明的变量进行赋值操作;
2.局部的东西可以使用全局的东西,但是全局的东西不能使用局部的东西;
解决:return(退出函数)
return的特点:
1.如果return后面跟着一个数据将会返回这个数据,但是不会保存起来,想要看到这个数据需要用一个变量保存起来(return 只返回不保存)
2.省略return,默认也会有,会return一个undefined
3.如果需要在全局使用函数返回的结果去做别的操作要记得加上return
4.前辈们提供了的方法基本上都加上了return
5.声明提前
鄙视题常客:
概念:在程序开始执行之前,会把var声明的变量(轻)和function(重),悄悄的集中定义到当前作用域的顶部,但是赋值会留在原地
tips:声明方式创建的函数会完整的提前,直接量方式创建的函数不会完整的提前,只有变量名部分会提前(即直接量方式的提前类似var声明的变量方式)
只要遵守以下规则就能避免声明提前的问题:
1.变量名和函数尽量不要重复
2.先创建再使用
6.方法重载
概念:相同的函数名,根据传入的实参的不同能自动的选择对应的函数去执行,即记住一个方法能执行多个操作,但是js不支持此功能
解决:在函数的内部自带了一个argument类数组对象,使用它来进行判断实参传入的不同执行不同的操作
用法:
1.通过下标获取传入了几个实参:argument[i];
2.通过数组的唯一属性length来获取传入了几个实参:arguments.length
五.数组
1.概念:创建一个变量可以保存多个数据
2.特点:
1.都是线性排列,除了第一个元素,每个元素都有唯一的前驱元素;除了最后一个元素,每个元素都有一个后继元素。
2.下标:即每个元素自己的位置,下标是从0开始的,到数组的最大长度-1结束
3.length:数组的唯一属性,从1开始,到数组的最大长度的值
3.语法:
1.创建数组(两种)
1.直接创建: var arr/数组名=[];
2.构造函数: var arr=new Array(数据1,数据2,...);
2.获取数组中的元素:
数组名[i]
3.添加元素/替换元素:
数组名[下标]=新元素;
如果下标下有元素就是替换,没有就是添加
4.数组的3大不限制:
1.不限制元素类型(优点)
2.不限制元素的长度(优点)
3.不限制下标的越界(缺点)
如果元素的下标越界,返回的值是undefined,不能进行操作
5.数组的唯一属性length ****
用法: 数组名.length
作用:获取数组的长度,方便我们进行对数组进行操作
1.在数组末尾添加新的元素:数组名[数组名.length]=新的数据;
2.获取数组倒数第n个元素:数组名[数组名.length-n];
3.缩容:删除倒数n个元素:数组名.length=n
6.数组的遍历 *****
目的:取出数组里的所有元素来进行相同或者相似的操作
固定公式:
for(i=0;i<数组名.length;i++){
数组名[];
}
六.DOM
Document Object Model:文档对象类型,提供了一些方法来专门用于操作HTML文档
1、核心DOM:无敌的,即可以操作HTML又可以操作XML,但是语法相对比较繁琐
2、HTML DOM:只可以操作HTML,不能访问一切自定义的东西,但是语法简单
3、XML DOM:只可以操作XML,被淘汰了,被JSON数据格式代替了
DOM树的概念:DOM把整个HTML看成一个倒挂的树状结构,树根不是HTML是Document的对象,它由浏览器自动创建,一个页面只有一个树根
DOM树的作用:DOM会将页面上的每一个元素,属性,文本,注释等视为一个DOM元素/节点/对象,我们可以通过树根找到我们想要的任何一个DOM元素/节点/对象(属性和方法)
*****1.查找元素的方法:
两大方法:
一.直接通过HTML的特点来查找元素
①.通过id查找 var elem=document.getElementById("id名");
tips:
1.返回值:找到了就返回此元素,没找到返回null;
2.出现了多个id的话只会找到第一个;
3.此方法不允许使用,一次只能找一个,留给后端去用;
②.***** 通过标签名查找元素:
var elems=document/已经找到的父元素.getElementsByName("标签名");
tips:
1.返回值:找到了就返回一个类数组DOM集合,没找到就返回空集合
2.js只能直接操作DOM元素,不能直接操作集合,要么通过下标拿到某一个元素来使用,要么遍历拿到每一个元素再来使用
3.可以从已经找到的父元素开始查找
③.**** 通过class来查找元素
var elems=document/已经找到的父元素.getElementsByClassName("class名");
tips:
与通过标签名查找相同
④.****css选择器查找
1.var elem=document.querySelector("任意css选择器的");
缺陷:只能找到单个元素,如果匹配到了多个,也只会返回第一个,没找到null,一次只能操作一个元素,不舒服
2、***var elems=document.querySelectorAll("任意css选择器的");
优点:
1、找到了是一个集合,没找到是一个空集合
2、复杂查找时,非常简单
3、返回的是一个静态集合NodeList
二.通过关系获取元素:(前提:已经先找到了一个元素才可以使用)
父元素:elem.parentNode;
子元素:elem.children;
第一个子元素:elem.fristElementChild;
最后一个子元素:elem.lastElementChild;
前一个兄弟:elem.previousElementSibling;
后一个兄弟:elem.nextElementSibling;
tips:
1.除了子元素,其他获取到的元素都是单个元素,可以直接使用
2.使用关系获取的目的是不影响别的元素,只影响到自己有关系的元素
*****2.操作元素(三个方面:内容,属性,样式)
1.内容:
1.elem.innerHTML 获取和设置标签开始到结束之间的内容,能识别标签
语法:
获取:elem.innerHTML;
设置:elem.innerHTML="新内容";
2.elem.innerText 获取和设置标签之间的纯文本内容,不会识别标签
获取:elem.innerText;
设置:elem.innerText="新内容";
3.input.value 专门获取或设置input里的内容
获取:input.value;
设置:input.value="新内容";
2.属性:
获取属性值:elem.getAttribute("属性名");
设置属性值:elem.setAttribute("属性名","属性值")
简化写法:
获取属性:elem.属性名;
设置属性值:elem.属性名="属性值";
简化的缺点:
class必须写为className,不能操作自定义的属性
3.样式
使用样式的3种方法:
1.内联样式
2.内部样式表
3.外部样式表
js的样式操作用于操作内联样式
原因:1.不会牵一发动全身
2.优先级最高
获取样式:elem.style.css属性名;
设置样式:elem.style.css属性名="属性值";
tips:
1.js里-是减号,css样式里有-号的地方变为小驼峰命名法
2.因为我们是对内联样式进行操作,所以只能获取到内联样式
4.绑定事件
语法:
elem.on事件名=function(){
操作;
}
绑定事件的关键字:this
如果单个元素绑定了事件,this只指代这个元素 如果多个元素绑定了事件,this指代当前触发的元素
*****3.创建元素的方式:
1.创建空标签:var elem=document.creareElement("标签名");
2.为其设置必要的属性和事件:
elem.属性名="属性值";
elem.on事件名=function(){操作}
3.挂载上树/渲染页面:
父元素.appendchild(elem);
七.数组
1.创建数组:
方法1:var arr=[];
方法2:var arr=new Array();
注:方法2里如果只放一个数字,会创建出一个长度为这个数字的空数组,内容全是undefined
2.按值传递 ***
1.传递的值如果是原始类型,就是复制了一个文本给对方,两者互不影响。
2.传递的值如果是引用类型,只是保存了地址值,其实是赋值了自己的地址值给对方,两者用的是同一个地址值,会相互影响。
3.释放引用类型
js的底层有一个垃圾回收器,只有垃圾回收器为0的时候才会删除不要的数据
我们要将代码封装为函数,函数的变量会自动释放
4.hash数组 ***
1.使用:
1.创建一个空数组:var arr=[];
2.为数组添加自定义的下标并且赋值:
arr["自定义下标"]=新值;
3.访问元素=arr["自定义下标"];
tips:
1.hash数组的length会失效,永远为0;
2.遍历hash数组不能使用for循环只能使用for in循环,for in也能遍历索引数组,但是建议分开使用
for(var i in 数组名){
i;
数组名[i];
}
3.原理 ***
hash算法:将字符串计算出一个尽量不重复的数字字符串内容相同,则计算出来的数字也一定相同。
添加元素:把自定义下标交给hash算法,得到一个数字,直接将你要保存的数据放到这里(地址值)保存起来
获取元素:将指定的自定义下标交给hash算法,得到一个和当初保存时一样的数字(地址值),通过这个地址找到你当初保存的数据再取出使用
4.js里面一切的东西都是对象。除了undefined和null,一切对象的底层都是hash数组
5.数组的API ******
1.数组转字符串:
arr.join("自定义连接符");
tips: 二级联动:
1.select的专属事件onchange -只有选中项发生之后才会触发
2.select专属的属性 this.selectedIndex
2.数组的拼接:添加元素的新方式
arr.concat(新值1...)
自动把传入的新参数放到数组末尾
tips:
1.不修改数组,只会返回一个新数组,需要用变量保存
2.可以放数组,会自动把数组打散之后一个一个放进去
3.截取子数组
arr.slice(开始的下标,结束下标)
tips: 1.不修改数组,只会返回一个新数组,需要用变量保存
2.含头不含尾 ***
3.可以不写结束下标,会直接截取完后面所有的元素
4.开始下标也可以不写,就是两个参数都省略那么就是完全的复制一份副本,这个操作也叫做深拷贝
5.可以写负数,意为倒数第几个,-1就是倒数第1个数
4.删,插,替 ***
删除:arr.splice(开始下标,n);
tips:会修改原数组,但是也有返回的值,返回的值就是被删除了的数组,没有删除也会返回一个空的数组
插入:arr.splice(开始下标,0,新值...);
tips:
1.原开始下标位置的元素会和后面的元素会自动往后移
2.尽量不要插入新的数组,因为会导致数组变为二维数组
替换:arr.splice(开始下标,n,新值...);
tips:删除的个数和插入的个数可以不相同
5.翻转
arr.reverse();
今日一见,不会再见
6.数组排序:
arr.sort();
将数组中的元素转换为字符串之后,按位pk,一般不直接使用
arr.sort(function(a,b){})
此方法衍生的匿名回调函数,常用
a指代前一个数
b指代后一个数
数字的升序:
arr.sort(function(a,b){
return a-b;
})
数字的降序:
arr.sort(function(a,b){
return b-a;
})
tips:
1.网页上的所有排序功能底层都是数组,因为js中只有数组可以排序
2.以后只要网页上有随机的功能,那么他的底层一定用到了随机数
7.栈和队列:添加元素和删除元素
栈:就是一端封闭的数组,只能一个端进出
开头进:arr.unshift(新值1...)
开头出:arr.shift(); 一次只能删除一个
头进头出的缺点:会改变后面元素的下标
尾部进:arr.push();
尾部出:arr.pop();
队列:就是从一端进,另一端出的数组
开头进:arr.unshift(新值1...)
尾部出:var last=arr.pop();
头进头出的缺点:会改变后面元素的下标
尾部进:arr.push();
开头出:arr.shift();
8.es5提供的3组6个API:
1.判断:
every:每个,每个都满足返回true,有一个不满足就返回false,类似&&
var bool=arr.every(function(val,i,arr){
return 条件;
})
some:一些,每个都不满足返回true,有一个满足就返回true,类似||
var bool=arr.some(function(val,i,arr){
return 条件;
})
2.遍历:
forEach:直接修改原数组
arr.forEach(function(val,i,arr){
直接写操作;
})
map:不直接修改原数组
arr.map(function(val,i,arr){
return 直接写操作;
})
3.过滤和汇总:
过滤:var subArr=arr.filter(function(val,i,arr){
return 判断条件;
})
汇总:var result=arr.filter(function(prev,val,i,arr){
return prev+val;
},基础值)
tips:
1.6个API都是为了简化for循环的操作
2.箭头函数:为了简化一切回调函数的一个公式:function去掉,()和{}之间用=>连接,如果形参只有一句话就可以把形参的()省略,如果函数体只有一句话可以把{}去掉。
6.周期性定时器:每过一段时间就自动执行一次
开启:timer=setInterval(function(){
操作;
},间隔时间(单位:毫秒))
结束:clearInterval(timer)
鼠标移入和移出事件:
elem.onmouse(over/out)=function(){
操作:
}
7.二维数组:数组元素,又引用着另一个数组
创建:var arr=[ [],
[],
[]
];
访问:arr[行下标,列下标]
特殊:列下标越界,返回undefined
行下标越界得到是报错
遍历二维数组:两层循环,外层循环控制行,内层循环控制列
for (r=0;r<arr.length;r++){
for(var c=0;c<arr[r.length];c++){
console.log(arr[r][c]);
}
}
八.string字符串
1.string的概念
字符串:多个字符组成的只读字符数组
tips:
字符串和数组有相同点:
1.字符串中的个数:str.length;
2.获取字符串中的字符:str[i];
3.可以遍历
4.所有数组不修改原数组的API,字符串也可以使用(concat,slice)
2.**** stringAPI:
1.转义字符:\
作用:
1.把字符串和程序冲突的字符转为原文
2.包含特殊功能的符号:\n换行;\t制表符(和敲tab一个效果)
3.输出unicode码的字符:\u4e00 一;\u9fa5 龥
2.* 大小写转换:
转大写:x.toUpperCase();
转小写:x.toLowerCase();
作用:把字符中的每一个字符统一的转化为大写或小写,做验证码的比较时常用
3.获取字符串中的指定位置的字符:str.charAt(),没啥用,不如str[i];
4.获取字符串中指定位置字符的ASCII码:
var ascii=str.charCodeAt(i);
ascii码转原文:
var a=String.fromCharCode(ascii);
5.*** 检索字符串:
var i=str.indexof("关键字",开始下标)
从左到右的检索关键字的第一个字符的位置,开始下标可以省略,默认从0的位置开始查找
返回值:找到了就返回第一个关键字的第一个字符的下标,没找到就返回-1,通常用返回值做判断。
6.拼接字符串:
var str=str.conact;
但是对于字符串来说不然直接+拼接
7.*截取字符串
var substr=str.slice()
var substr=str.substring()
var substr=str.subStr(开始下标,n)n是个数
8.替换字符串:
str.replace("固定关键字","新内容")
9.**** 分割字符串:
作用:把字符串转化为数组
var arr=str.split("自定义切割符");
如果切割的是“”,会切散每个字符串
10.去掉空白字符
str.trim/trimStart/trimEnd()
九.Math和Date
1.Math
1.Math对象:专门提供了数学计算的API
2.Math的API:
1.取整:
1.上取整:超过就取下整数,小数位不能超过15位
var num=Math.ceil(num)
2.下取整:
var num=Math.floor
3.四舍五入取整:
var num=Math.round(num);
4.num.toFixed(d)
d是保留小数的位数
缺点:结果是一个字符串,要配合parseFloat()
2.乘方和开方:
Math.pow(底数,幂) 简化:底数**幂
开方:Math.sqrt() 只能开方
3.最大值和最小值
Math.max/min()
自动从()找到最大值和最小值但是不支持数组
数组的写法:
Math.max/min(apply(Math,arr))
4.绝对值:负数变正数:Math.abs();
5.*** 随机数
Math.random() 在0-1之间取一个随机的小数搭配上parseInt使用,只能取到0但是不可能取到1
2.Date
1.Date对象提供了操作日期和时间的API
2.创建:
1.创建一个当前日期:
var now=new Date();
2.创建一个自定义时间:
var birth=new Date ("yyyy/MM/dd hh:mm:ss");
3.创建一个自定义时间:
var birth=new Date(yyyy,MM,dd,hh,mm,ss)
4.复制一个日期:日期的所有API都会直接修改原日期,在调用API之前要先复制
var end=new Date(birth)
2.使用:
1.两个日期对象之间,可以相减(大-小),得到一个毫秒差,换算出自己想要的任何一部分
2.API
FullYear(年)
Month(月) 取值:0-11
Date(日) 取值:1-31
Day(星期) 取值:0-6,星期天是0
Hours(小时) 取值:0-23
Minutes(分) 取值:0-59
Seconds(秒) 取值:0-59
Milliseconds(毫秒)
每一个分量有一对get/set方法
tips:
1.Day只有get方法,没有set方法
2.如果set超出了取值会自动进制
3.想对某个分量进行加减操作
date.setXXX(date.getXXX()+(-n))
4.格式化日期为本地字符串:date.toLocaleString(),垃圾有兼容性问题
十.BOM
1.BOM的概念
Browser Object Model 浏览器对象模型,专门用于操作浏览器的,但是使用不多,不如ES和DOM,浏览器很多操作都是自带的,且BOM没有标准,各个浏览器都有自己的定义,但是大部分浏览器都是一致规范的,除了老IE
2.window对象:两个角色
1.全局对象
2.指代当前窗口本事
3.属性
1.获取浏览器的完整大小:outerWidth/outerHeight
2.获取浏览器的文档显示区域大小:innerWidth/innerHeight
3.获取屏幕的大小:跟window没关系:screen.width/height 做桌面应用
4.方法
1.打开链接的新方式,提升用户的体验
1.当前窗口打开,可以后退
open("url","_self");
2.当前窗口打开,不能后退
location.replace("新url");
tips:
1.history:当前窗口的历史记录,因为有了历史记录才会有能后退的窗口
2.此方法叫替换,不是跳转,不会产生历史记录,自然不能后退,但是网址被替换了,使用场景:电商网站结账之后不允许后退
3.新窗口打开,可以打开多个
open("url","_blank")
4.新窗口打开,只能打开一个:
open("url","自定义一个name");
tips:窗口的底层都有一个名字,如果打开了一个已经开着的名字的窗口,他会把这个窗口关闭后再打开
作用:
1.任何的标签都能设置跳转
2.提升用户的体验感
3.a标签的其他用法:
①.跳转,锚点
②.下载按钮:
<a href="xxx.exe/rar/zip/7z"></a>
③.打开图片或txt文件:
<a href="xx.png/jpg/jepg/gif/txt"></a>
④.直接写js:
直接书写js-不需要绑定点击事件:<a href="javascript:js代码;"> 一般不用
5.打开新窗口/新连接:open("url","target","width=?,height=?,left=?,top=?");
tips:
1.如果没加第三个参数,窗口会和浏览器融为一体
2.如果加了第三个参数,窗口会脱离浏览器独立存在
3.关闭窗口:window.close();
4.改变窗口大小:newW.resizeTo(新宽,新高)
5.改变窗口的位置:newW.moveTo(新x,新y)
6.window提供了三个框:
警告框:alert("")
输入框:var user=prompt("提示文字");
确认框:var bool=confirm("提示文字");
7.定时器也是window的
5.事件:
1.window.onload事件 等别的资源加载完成后再加载该事件中的代码
2.window.onresize事件 窗口大小发生变化就触发,搭配判断innerWidth,可以理解为js的css媒体查询方式
3.window.onscroll事件:滚动事件 滚动就触发搭配
①.获取滚动条的当前位置:window.scrollY
②.获取元素距离页面顶部有多远:elem.offsetTop/Left
6.本地存储技术
cookie:淘汰了,存储的大小只有2kb,操作复杂,要到处切割,最多存储30天
webStorage:H5带来的新特性,存储大小有8M,永久存储,操作简单
webStorage的学习:
分类:
1.sessionStorage:会话级,只要浏览器关闭了就自动死亡
2.localStorage:本地级,只要不清空,就永远存在
使用:
1.添加
localStorage.属性名="属性名"
2.读取:
localStorage.属性名
3.删除
xxStorage.removeItem("属性名");
4.清空:
xxxStorage.clear();
7.BOM的常用对象
1、history对象:保存了当前窗口的历史纪录(过去的url) 前进:history.go(1); 后退:history.go(-1); 刷新:history.go(0);
2、***location对象:保存了当前窗口的正在打开的url(现在的url)
***程序员常识:一个url由几部分组成?分别每个部分有什么用? - 以后再学习服务器端和数据库的时候会有很大的帮助
tips:5部分
1、协议:*https(加密)/*http(未加密)/ftp(传输文件)/ws(直播)... 前两个都属于叫做请求-响应模型
2、主机号|IP地址|域名:域名是需要花钱购买的,主机号|IP地址是免费,127.0.0.1才真的是叫做主机号,只能自己访问自己
3、端口号:https默认端口为443,http默认端口为80,只有默认端口可以省略不写
4、***文件的相对路径|路由:百度加密了
5、***查询字符串|请求消息:前端传输到后端的东西,前端对后端说的话,就是form表单提交带来的东西
属性: 获取url的5个部分的内容
1.协议:location.protocal;
2.域名:location.hostname;
3.端口:location.port;
5.路由:location.pathname;
6.请求消息:location.search;
跳转:location="新url" - 替换当前窗口,可以后退
跳转后,禁止后退:location.replace("新url") - 替换当前窗口,禁止后退
刷新:location.reload();
3.*****event事件对象
1、事件周期:从事件发生,到所有事件处理函数执行完毕的全过程
3个阶段:
1、捕获阶段:由外向内,记录要发生的事件有哪些
2、目标优先触发:目标元素->当前点击的实际发生事件的元素
3、冒泡触发:由内向外,依次执行我们之前记录着的要发生的事件
2、*****获取事件对象event:
主流:会自动作为事件处理函数的第一个形参传入
老IE:event; - 老IE全局有这个变量
兼容:event;//不光老IE可用,主流也可用
得到了事件对象event的作用:
1、获取鼠标的坐标:
获取鼠标相对于屏幕的坐标:e.screenX/Y
获取鼠标相对于窗口/客户端/文档显示区域的坐标:e.clientX/Y
*获取鼠标相对于网页的坐标:e.pageX/Y
2、阻止事件冒泡:- 鄙视面试中
主流:e.stopPropagation();
老IE:e.cancelBubble=true;
兼容:e.cancelBubble=true;//不光老IE可用,主流也可用
3、*****利用冒泡/事件委托 - 开发中常用,提升网页性能,有了它我们的事件函数也可以换为箭头函数了
优化:如果多个子元素定义了相同 或 相似的事件操作,最好只给父元素定义一次
为什么:每一次绑定一个事件函数,其实都是创建了一个事件对象,创建的事件对象越多,网站性能就越差
淘汰了this,他水性杨花,当前元素
认识一个目标元素target:你点击的是哪一个,他永远就是那一个,不会变化的
主流:e.target;
老IE:e.srcElement;
兼容:e.srcElement;//不光老IE可用,主流也可用
4、阻止浏览器的默认行为:比如:a标签默认就可以跳转,提交按钮可以提交表单,右键自带一个弹出框,F12自带一个控制台,F11自带全屏功能,F5自带刷新功能
主流:e.preventDefault();
老IE:e.returnValue=false;
兼容:e.returnValue=false;//不光老IE可用,主流也可用
新事件:
1、右键事件 - window.oncontextmenu
2、键盘事件:一般来说用于游戏开发较多+都要搭配上键盘的键码
window.onkeydown - 按住和按下,任何键盘按键都可以触发
window.onkeypress - 按住和按下,只有字母、数字、回车、空格可以触发,其他按键不行
window.onkeyup - 松开,任何键盘按键都可以触发(比手速的游戏)
5、获取键盘的键码:
e.keyCode; - 可以获取到你按了那个键,每个键都有自己对应的键码,但是不需要记忆,要么输出看,要么百度搜个表
十一.递归
1.递归:
简单来说就是再函数之中再一次调用了函数自己,迟早有一天会停下来专门用于遍历层级不明确的情况 - DOM树和数据(children只能找到儿子层,找不到孙子层)
2.使用
function 函数名(root){
1、第一层要做什么直接做
2、判断有没有下一层,如果有下一层则再次调用此方法,只不过传入的实参是自己的下一层
}
函数名(实际的根)
算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点
缺陷:不要过多使用,性能相对较差,同时开启大量的函数调用,浪费内存,我们只在一个情况:层级不明确
递归 vs 纯循环
递归:优点:简单易用
缺点:性能低
纯循环:优点:几乎不占用性能
缺点:难的一批
2、绑定事件:3种方式:
1、在HTML上书写事件属性
<elem on事件名="函数名(实参)"></elem>
缺点:
1、不符合内容与样式与行为的分离原则
2、无法动态绑定,一次只能绑定一个元素
3、不支持绑定多个函数对象
2、*在js中使用事件处理函数属性
elem.on事件名=function(){操作}
优点:
1、符合内容与样式与行为的分离原则
2、动态绑定,一次能绑定多个元素
缺点:
1、不支持绑定多个函数对象
3、*在js中使用事件API:如果不用考虑老IE,他也不错
主流:elem.addEventListener("事件名",callback);
老IE:elem.attachEvent("on事件名",callback);
兼容:
if(elem.addEventListener){
elem.addEventListener("事件名",callback);
}else{
elem.attachEvent("on事件名",callback);
}
优点:
1、符合内容与样式与行为的分离原则
2、动态绑定
3、支持绑定多个函数对象
缺点:有兼容性问题
十二.正则表达式
1、*****正则表达式:定义字符串中字符出现规则的表达式
作用:切割 替换 验证
语法:
1、最简单的正则就是关键字原文 "no" -> /no/后缀
后缀:g:找全部 i:忽略大小写
2、备选字符集:/^[备选字符集]$/
tips:
1、一个中括号,只管一位字符
2、问题:正则表达式默认只要满足就不管后续了,而我们做验证的人,希望的是用户从头到尾按照我们的要求来,希望从头到尾完全匹配:
解决:前加^,后加$,两者同时使用,代表要求从头到尾完全匹配/^[备选字符集]$/ - 只要做验证必加
特殊:如果备选字符集中的ascii码是连续的,那么可用-省略掉中间部分
比如:
一位数字:[0-9];
一位字母:[A-Za-z];
一位数字、字母、下划线:[0-9A-Za-z_]
一位汉字:[\u4e00-\u9fa5]
除了xxx之外的:[^0-9] - 很少使用,范围太广
3、预定义字符集:前辈们提前定义了一些字符集,方便我们程序员 - 简化了备选字符集
一位数字:\d ===>[0-9]
一位数字、字母、下划线:\w ===> [0-9A-Za-z_]
一位空白字符:\s
一位除了换行外的任意字符:. - 很少使用,范围太广了
建议:优先使用预定义字符集,预定义满足不了我们再用备选字符集补充
问题:不管是备选字符集,还是预定义字符集,一个都只管一位
4、量词:规定一个字符集出现的次数:
1、有明确数量:
字符集{n,m}:前边相邻的字符集,至少n个,最多m个
字符集{n,}:前边相邻的字符集,至少n个,多了不限
字符集{n}:前边相邻的字符集,必须n个
2、无明确数量: 字符集?:前边相邻的字符集,可有可无,最多1个
字符集*:前边相邻的字符集,可有可无,多了不限
字符集+:前边相邻的字符集,至少一个,多了不限
5、选择和分组:
选择:在多个规则中选一个 规则1|规则2
分组:将多个字符集临时组成一组子规则 (规则1|规则2)
6、指定匹配位置
^:开头
$:结尾
特殊:两者同时使用,前加^,后加$,表示从头到尾要求完全匹配 - 只要你做验证
7、密码强度验证:2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
(?![]+$):在已有的正则范围基础上除去某几个范围
/^[0-9A-Za-z]{2,4}$/
预判公式:
(?![0-9]+$) -> 不能全由数字组成,可能有大写、小写、汉字、日文、特殊符号...
(?![a-z]+$) -> 不能全由小写组成,可能有数字、大写、汉字、日文、特殊符号...
(?![0-9a-z]+$) -> 不能全由数字组成、也不能全由小写组成、也不能全由数字和小写的组合组成
比如:
/(?![0-9a-z]+$)(?![A-Za-z]+$)[0-9A-Za-z]{2,4};//2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
/(?![0-9a-z]+$)(?![A-Za-z]+$)(?![A-Z0-9]+$)[0-9A-Za-z]{2,4}/;//必须三者都有
/(?![0-9A-Za-z]+$)[0-9A-Za-z_]{2,4}/;//至少要有下划线
2、*****支持正则表达式的字符串API
1、切割:var arr=str.split("固定切割符"/RegExp);
2、*****替换:很有可能出现在笔试之中
1、基本替换法:
str=str.replace(/正则表达式/后缀,"新内容");
//replace支持支持正则,并且搭配上后缀g就可以找到全部
//缺陷:替换的新内容是一个固定的
2、高级替换法:
str=str.replace(/正则表达式/后缀,function(a,b,c){
console.log(a);//正则匹配到的关键字
console.log(b);//正则匹配到的关键字的下标
console.log(c);//原字符串
return 判断a关键字的长度,而返回不同的星星数量
});
3、格式化
格式化:手机号
var phone="13452390312";
var reg=/(\d{4})\d{4}(\d{3})/;
phone=phone.replace(reg,(a,b,c)=>{
return b+"****"+c;
})
console.log(phone);
3、正则对象:
创建:
1、直接量:var reg=/正则表达式/后缀;
2、构造函数:var reg=new RegExp("正则表达式","后缀");
API:
1、验证:var bool=reg.test(用户输入的);
十三.对象
1、*****Object:对象 - Array/String/RegExp/Date... 对象具有属性和方法,都是预定义好的,现在我们可以学习自定义对象 - js是基于原型的面向对象语言
面向对象 - 三大特点:封装、继承、多态
1、***开发方式:
1.面向过程:过程 - 开始->结束
2.面向对象:对象(属性和方法),js有一句话万物皆对象。
使用场景:以后任何操作都要封装在一个对象之中,但是新手并不太推荐,难度较大
为什么要面向对象:现实生活中所有的数据都必须包含在一个事物之中才有意义
2、*****封装/创建/定义:封装自定义对象:3种
1、*直接量方式:
var obj={
"属性名":属性值,
...,
"方法名":function(){操作},//可以简化为箭头函数
...
}
强调:
1、其实属性名和方法名的""可以不加,建议加上,数据格式JSON,他必须在键上加上""
2、访问对象的属性和方法:
obj.属性名; === obj["属性名"];
obj.方法名(); === obj["方法名"]();
使用.去访问对象的属性和方法,更简单
***js中一切都是对象,除了undefined和null,一切对象的底层都是hash数组
3、访问到不存在的属性,返回undefined
4、可以随时随地的添加新属性和新方法
obj.属性名=新值;
obj.方法名=function(){};
5、如果我希望遍历出对象所有的东西,必须使用for in,obj[i]才能拿到,不要使用.会出问题
6、***如果你希望在对象的方法里,使用对象自己的属性,写为this.属性名
*****难点:this的指向:
1、单个元素绑定事件,this->这个元素
2、多个元素绑定事件,this->当前元素
3、定时器中的this->window
4、箭头函数this->外部对象
5、函数中this->谁在调用此方法,this就是谁
6、构造函数之中this->当前正在创建的对象
2、预定义构造函数方式:
var obj=new Object();//空对象
//需要自己后续慢慢添加属性和方法
obj.属性名=新值;
obj.方法名=function(){};
一次只能创建一个对象,适合创建单个元素的时候,第二种方法完全是垃圾
3、自定义构造函数方式:2步
1、创建自定义构造函数
function 类名(name,age,hobby){
this.name=name;
this.age=age;
this.hobby=hobby;
}
千万不要在里面创建方法,每个对象都会创建出一个相同的方法,浪费内存
2、调用构造函数创建对象
var obj=new 类名(实参,...);
面向对象
优点:
1、所有的属性和方法都保存在一个对象之中 - 更符合现实更有意义
2、每个功能特地分开写 - 便于以后维护
3、铁锁链舟 - 一个方法触发多个方法联动
缺点:对新手不友好,尤其是this的指向问题
3、*****继承:父对象的成员(属性和方法),子对象可以直接使用
继承的作用:代码重用!提高代码的复用性,节约了内存空间!提升了网站的性能!
何时继承:只要多个子对象公用的属性和【方法】,都要集中定义在父对象之中
1、***如何找到原型对象(父对象):保存了一类子对象共有属性和共有方法
1、对象名.__proto__; //必须先有一个对象
2、构造函数名.prototype;//构造函数名几乎人人都有,除了Math和Window,new 构造函数名();//Array、String、Date、RegExp...
2、*面试题:两链一包:作用域链和【原型链】和闭包
每个对象都有一个属性:__proto__,可以一层一层的找到每个人的父亲,形成的链式结构,就称之为叫做原型链
可以找到父对象的成员(属性和方法),作用:找共有属性和共有方法
最顶层的是Object的原型,上面放着方法toString,人人都可以使用toString
JS万物皆对象
3、有了原型对象,可以设置共有属性和共有方法
1、原型对象.属性名=属性值;
2、原型对象.方法名=function(){}
1、*****继承面试笔试题:
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.属性名;
共有:
修改:原型对象.属性名=新属性值;千万不要觉得,自己能拿到,就能直接修改,这样很危险,并没有修改原型的东西,而是在本地添加了一个同名属性
删除:delete 原型对象.属性名;如果你对着本地直接删除,那么此操作直接无效
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(this[i]===key){
return i;
}
}
return -1;
}
}
var arr1=[1,2,3,4,5];
var arr2=[2,4,6,8,10];
4、*如何判断x是不是一个数组:4种方法:千万别用typeof(),只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object。
1、判断x是否继承自Array.prototype:
Array.prototype.isPrototypeOf(x);
结果为true,说明是数组,结果为false,说明不是数组
2、判断x是不是由Array这个构造函数创建的
x instanceof Array;
结果为true,说明是数组,结果为false,说明不是数组
3、Array.isArray(x); - ES5新增的方法,只有数组可以这么使用
结果为true,说明是数组,结果为false,说明不是数组
4、*输出【对象的字符串】形式
在Object的原型上保存着最原始的toString方法
原始的toString输出形式:[object 构造函数名]
***多态:子对象觉得父对象的成员不好用,就在本地定义了同名函数,覆盖了父对象的成员,不严格定义:同一个方法,不同的人使用,效果不同,有多种形态
固定套路:
Object.prototype.toString.call(x)==="[object Array]"
5、实现自定义继承:
1、两个对象之间设置继承
子对象.__proto__=父对象
2、多个对象之间设置继承
构造函数名.prototype=父对象;
在创建对象之前就设置好继承关系
2、class关键字:简化面向对象(封装、继承、多态)
class 类名 extends 老类{
constructor(name,age,hobby,...){//放在constructor里面的都是自有属性
super(name,age);
this.hobby=hobby;
}//放在constructor外面的都是共有方法
//还会继承到老类所有的API,也可以添加新的
}
4、*****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自动释放,局部变量也就释放了
*****闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
何时使用:希望保护一个可以【反复使用的局部变量】的时候
如何使用:
1、两个函数进行嵌套
2、外层函数创建出受保护的变量
3、外层函数return出内层函数
4、内层函数再操作受保护的变量
强调:
1、判断是不是闭包:有没有两个函数嵌套,返回了内层函数,内层函数再操作受保护的变量
2、外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
3、同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏
1、三个事件需要防抖节流
共同点:触发的速度飞快
1、elem.onmousemove - 鼠标移动事件
2、input.oninput - 每次输入/改变都会触发
3、onresize - 每次窗口改变大小都会触发
防抖节流的公式:
function fdjl(){
var timer=null;
return function(){
if(timer!==null){clearTimeout(timer);timer=null;}
timer=setTimeout(()=>{
操作
},500)
}
}
var inner=fdjl();
elem.on事件名=function(){
inner();
}
5.******两链一包:
1、作用域链:以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
2、原型链:每个对象都有一个属性叫做.proto,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
作用:查找属性和方法,哪怕自己没有也会顺着原型链向上找,怪不得人人都能用toString(),因为他在最顶层
3、闭包:希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
作用:专门用于防抖节流