day11
一、BOM的常用对象
1.1 history对象
保存了当前窗口的历史纪录(过去的url)
- 前进:history.go(1);
- 后退:history.go(-1);
- 刷新:history.go(0);
<script>
// 前进
history.go(1)
// 后退
history.go(-1)
// 刷新
history.go(0)
</script>
1.2 location对象
保存了当前窗口的正在打开的url(现在的url)
- 一个url的组成部分:
- 协议:*https(加密)/*http(未加密)/ftp(传输文件)/ws(直播)... 前两个都属于叫做请求-响应模型
- 主机号|IP地址|域名:域名是需要花钱购买的,主机号|IP地址是免费,127.0.0.1才真的是叫做主机号,只能自己访问自己
- 端口号:https默认端口为443,http默认端口为80,只有默认端口可以省略不写
- 文件的相对路径|路由:百度加密了
- 查询字符串|请求消息:前端传输到后端的东西
- 获取url的5个部分的内容,可以输入location对象查看:
- 协议:location.protocal;
- 域名:location.hostname;
- 端口:location.port;
- 路由:location.pathname;
- 请求消息:location.search;
<script>
console.log(location);//href:"http://127.0.0.1:5500/%E4%BD%9C%E4%B8%9A/%E6%9D%A8%E7%8E%89%E5%8D%8E%20-%20%E7%AC%AC%E4%B8%89%E5%91%A8%E4%BD%9C%E4%B8%9A/day11/02-location%E5%AF%B9%E8%B1%A1.html"
// 协议
console.log(location.protocol);//http:
// 域名
console.log(location.hostname);//127.0.0.1
// 端口号
console.log(location.port);//5500
// 路由 console.log(location.pathname);///%E4%BD%9C%E4%B8%9A/%E6%9D%A8%E7%8E%89%E5%8D%8E%20-%20%E7%AC%AC%E4%B8%89%E5%91%A8%E4%BD%9C%E4%B8%9A/day11/02-location%E5%AF%B9%E8%B1%A1.html
// 请求消息
console.log(location.search);//
</script>
- 跳转:location="新url" - 替换当前窗口,可以后退
- 跳转后,禁止后退:location.replace("新url") - 替换当前窗口,禁止后退
- 刷新:location.reload();
二、DOM
2.1 查找元素
var elem=document.querySelector("任意css选择器的");
- 缺陷:只能找到单个元素,如果匹配到了多个,也只会返回第一个,没找到null
var elems=document.querySelectorAll("任意css选择器的");
- 优点:
- 找到了是一个集合,没找到是一个空集合
- 复杂查找时,非常简单
- 返回的是一个静态集合NodeList
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<script>
var li1 = document.querySelector('li')
console.log(li1)//找到的是第一个ul里面的第一个li
// 要想找到第二个ul里面的所有li
var lis = document.querySelectorAll('ul')[1].querySelectorAll('li')
console.log(lis);
</script>
2.2 document.getXXX 和 document.queryXXX的区别
- 后者更适合复杂查找
- 前者返回的是一个动态集合:每一次DOM发生变化,他都会悄悄的再次查找,让页面和数据保持一致,但是效率也就低下了 - 不支持forEach
- 后者返回的是一个静态集合:每一次DOM发生变化,他不会悄悄的再次查找,让页面和数据没有保持一致,但是效率也就高了 - 支持使用forEach
2.3 操作样式表
- 获取你想要操作的样式表
var sheet=document.styleSheets[i];
- 获取此样式表种所有的样式规则
var rules=sheet.cssRules;
- 数出你想要操作的那个规则的
var rule=rules[i];
- 操作
console.log(rule.style.css属性名)
rule.style.css属性名="css属性值";
2.4 DOM分类:
- 核心DOM:即可以操作HTML又可以操作XML,但是语法相对比较繁琐
- HTML DOM:只可以操作HTML
- XML DOM:只可以操作XML
2.5 操作属性
- 获取属性值:
- 核心DOM:elem.getAttribute("属性名");
- HTML DOM:elem.属性名;
- 设置属性值:
- 核心DOM:elem.setAttribute("属性名","属性值");
- HTML DOM:elem.属性名="属性值";
- 删除属性:设置属性值为空字符串,确实某些属性可以算是删除,但是只是删除了属性值,属性名还在,而有的属性哪怕只有一个属性名,也会具有作用(比如:href、disabled、readonly)
- 核心DOM:elem.removeAttribute("属性名");
- HTML DOM:elem.属性名=""; - 属性节点删不干净
- 判断有没有:只能判断有没有,不能判断是什么,推荐用elem.getAttribute("属性名");去获取到值,自己再写比较运算
- 核心DOM:elem.hasAttribute("属性名");
- HTML DOM:elem.属性名!="";
2.6 元素上树的三种方式:
- 父元素.appendChild(elem);//在父元素末尾处追加一个子元素elem
- 父元素.insertBefore(elem,已有子元素);//在父元素追加一个子元素elem,但是放在已有子元素的前面
- 父元素.replaceChild(elem,已有子元素);//在父元素追加一个子元素elem,但是会替换掉已有子元素
2.7 删除元素:
elem.remove();
三、let
let 变量名=值;
作用:
- 解决了声明提前
- 带来了块级作用域,一个{}就是一个块,此变量只能在那个{}里面使用
- 如果用let去当作下标绑定事件,那么他会记录着你当前元素的下标,不再需要自定义下标了 - 其实forEach的那个形参i就是let创建的
四、类数组转为普通数组:接住=Array.from(类数组对象);
五、案例
<div id="menu"></div>
<span>>></span>
<div id="content"></div>
<script>
// 开关门效果
var span = document.querySelector('span')
span.onclick = function(){
span.innerText = span.innerText == '>>'?'<<':'>>'
content.style.display = span.innerText == '>>'?'block':'none'
}
</script>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<section>
<div class="active"></div>
<div></div>
<div></div>
</section>
<script>
// 选项卡
var lis = document.querySelectorAll('li')
var divs = document.querySelectorAll('div')
lis.forEach((li,i)=>{
li.onclick = function(){
divs.forEach(div=>div.className = '')
divs[i].className = 'active'
}
})
</script>
<ul id="ul">
<li>name1</li>
<li>name2</li>
<li>name3</li>
<li>name4</li>
<li>name5</li>
<li>name6</li>
<li>name7</li>
<li>name8</li>
<li>name9</li>
<li>name10</li>
</ul>
<script>
// 摇号功能
// 拿到所有的li
var lis = document.querySelectorAll('li')
// 创建一个新数组用来装排序的数
var num = []
// 死循环 - 当num的长度和lis的长度一致时退出循环
while(num.length < lis.length) {
// 创建随机数
var r = parseInt(Math.random()*(lis.length))
// 如果num中没有这个数则向num中加入这个数
if(num.indexOf(r) == -1) {
num.push(r)
}
}
// 给li设置自定义属性
lis.forEach((li,i)=>li.setAttribute('nn',num[i]))
// 把类数组变为数组
lis = Array.from(lis)
// 对数组进行排序
lis.sort((a,b)=>a.getAttribute('nn') - b.getAttribute('nn'))
// 上树
lis.forEach(li=>ul.appendChild(li))
</script>
<select id="lsel" size="5" multiple>
<option>Argentina</option>
<option>Brazil</option>
<option>Canada</option>
<option>Chile</option>
<option>China</option>
<option>Cuba</option>
<option>Denmark</option>
<option>Egypt</option>
<option>France</option>
<option>Greece</option>
<option>Spain</option>
</select>
<div>
<button id="btn1">>></button>
<button id="btn2">></button>
<button id="btn3"><</button>
<button id="btn4"><<</button>
</div>
<select id="rsel" size="5" multiple>
</select>
<script>
// 国家的移动
// 设置左右空数组
var larr = [];
var rarr = [];
// 拿到每一个国家并且放在左数组中
larr = lsel.innerHTML.trim().slice(8,-9).split('</option>\n\t\t<option>');
// 拿到所有的button
var btns = document.querySelectorAll('button');
// 遍历btns
btns.forEach(btn=>{
// 点击事件
btn.onclick = function(){
// 判断按钮
switch(this.innerText){
case '>>':
// 把左数组的内容全部拼接到右数组
rarr = rarr.concat(larr);
// 左数组设置为空
larr = [];
break;
case '<<':
larr = larr.concat(rarr);
rarr = [];
break;
case '>':
// 拿到sel1里面的option
var op1 = lsel.children;
// 倒着循环
for(var i = op1.length - 1; i >= 0; i--) {
// 判断option是否被选中
if(op1[i].selected) {
// 如果这个被选中就把这个option给删除了并且拼接到右数组
rarr = rarr.concat(larr.splice(i,1));
}
}
break
default :
var op2 = rsel.children;
for(var i = op2.length - 1; i >= 0; i--) {
if(op2[i].selected) {
larr = larr.concat(rarr.splice(i,1));
}
}
}
// 数组按照字符串pk排序
larr.sort();
rarr.sort();
// 判断左数组的长度是否为0
if(larr.length == 0) {
// 为0则设置左边select内容为空
lsel.innerHTML = '';
} else {
lsel.innerHTML = '<option>' + larr.join('</option><option>') + '</option>';
}
// 右
if(rarr.length == 0) {
rsel.innerHTML = '';
} else {
rsel.innerHTML = '<option>' + rarr.join('</option><option>') + '</option>';
}
}
})
</script>
day12
一、递归
简单来说就是再函数之中再一次调用了函数自己,迟早有一天会停下来
- 何时:专门用于遍历层级不明确的情况 - DOM树和数据(children只能找到儿子层,找不到孙子层)
- 如何使用:
function 函数名(root){
第一层要做什么直接做
判断有没有下一层,如果有下一层则再次调用此方法,只不过传入的实参是自己的下一层
}
函数名(实际的根)
- 算法:深度优先!优先遍历当前节点的子节点,子节点遍历完毕才会跳到兄弟节点
- 缺陷:不要过多使用,性能相对较差,同时开启大量的函数调用,浪费内存
- 递归 vs 纯循环
- 递归:优点:简单易用
缺点:性能低 - 纯循环:优点:几乎不占用性能
缺点:难
- 递归:优点:简单易用
二、绑定事件:3种方式
- 在HTML上书写事件属性
<elem on事件名="函数名(实参)"></elem>
缺点:
- 不符合内容与样式与行为的分离原则
- 无法动态绑定,一次只能绑定一个元素
- 不支持绑定多个函数对象
- 在js中使用事件处理函数属性
elem.on事件名=function(){操作}
优点:
- 符合内容与样式与行为的分离原则
- 动态绑定,一次能绑定多个元素
缺点: - 不支持绑定多个函数对象
- 在js中使用事件API:如果不用考虑老IE,他也不错
- 主流:elem.addEventListener("事件名",callback);
- 老IE:elem.attachEvent("on事件名",callback);
- 兼容:
if(elem.addEventListener){
elem.addEventListener("事件名",callback);
}else{
elem.attachEvent("on事件名",callback);
}
<script>
// 主流浏览器
btn.addEventListener('click',function(){
console.log(1);
})
// 老IE(8-)
btn.attachEvent('onclick',function(){
console.log(1);
})
// 兼容写法
if (btn.addEventListener) {
btn.addEventListener('click',function(){
console.log(1);
})
} else {
btn.attachEvent('onclick',function(){
console.log(1);
})
}
</script>
优点:
- 符合内容与样式与行为的分离原则
- 动态绑定
- 支持绑定多个函数对象
缺点:有兼容性问题
三、select&option只有他们可以简化创建元素&上树
select.add(new Option("innerHTML","value"));
四、练习
// 递归-DOM 给#ul的孩子设置边框
function getArea(root) {
for(var i = 0; i < root.length; i++) {
root[i].style.border = "1px solid #fc1"
if(root[i].children.length > 0) {
getArea(root[i].children)
}
}
}
getArea(ul.children)
<script>
// 递归 - 数据
var json=[{"name":"杨俊杰","children":[{"name":"杨俊"},{"name":"杨杰"}]},
{"name":"冯小龙"},
{"name":"胡畔","children":[{"name":"胡田","children":[{"name":"胡半"}]}]},
{"name":"袍哥","children":[{"name":"钟哥","children":[{"name":"小钟","children":[{"name":"钟种"}]}]}]}
];
// 拿到所有人的姓名
function getName(root) {
root.forEach(obj=>{
console.log(obj.name);
if(obj.children) {//没有就是undefined
getName(obj.children)
}
})
}
getName(json)
</script>
<script>
// 纯循环 - 设置#ul和所有它孩子的边框
function getArea(root) {
//
var org = ul
while(1) {
root.style.border = "1px solid #fc1"
if(root.firstElementChild) {
root = root.firstElementChild
} else if (root.nextElementSibling) {
root = root.nextElementSibling
} else {
while(!root.parentNode.nextElementSibling&&root!=org.lastElementChild) {
root = root.parentNode
}
root = root.parentNode.nextElementSibling
if(root == org.lastElementChild) {
root.style.border = "1px solid #fc1"
break
}
}
}
}
getArea(ul)
</script>
<select name="provs">
<option>—请选择—</option>
<option>北京市</option>
<option>天津市</option>
<option>河北省</option>
<option>重庆省</option>
</select>
<select name="cities" class="hide">
</select>
<script>
// 二级联动
var cities=[//JSON:javascript object notation数据
[], /*0号下标没有元素*/
[{"name":'东城区',"value":101},
{"name":'西城区',"value":102},
{"name":'海淀区',"value":103},
{"name":'朝阳区',"value":104},],
[{"name":'河东区',"value":201},
{"name":'河西区',"value":202},
{"name":'南开区',"value":203}],
[{"name":'石家庄市',"value":301},
{"name":'廊坊市',"value":302},
{"name":'保定市',"value":303},
{"name":'唐山市',"value":304},
{"name":'秦皇岛市',"value":305}],
[{"name":'渝中区',"value":401},
{"name":'九龙坡区',"value":402},
{"name":'沙坪坝区',"value":403},
{"name":'渝北区',"value":404},
{"name":'江北区',"value":405}]
];
// 拿到两个select
var sels = document.querySelectorAll('select')
// 第一个select选中项改变时候
sels[0].onchange = function(){
// 拿到当前项的下标
var i = this.selectedIndex
// 判断是否为为请选择项 如果是
if(i > 0){
// 清除第二个li里面所有的内容
sels[1].innerHTML = ''
// 让第二个select显示
sels[1].className = ''
// 在数组中对应下标的内容做遍历
cities[i].forEach(obj=>{
/* // 创建标签
var opt = document.createElement('option')
// 设置属性和内容
opt.innerHTML = obj.name
opt.value = obj.value
// 上树
sels[1].appendChild(opt) */
// 简写以上内容
sels[1].add(new Option(obj.name,obj.value))
})
} else {
// 清除第二个li里面所有的内容
sels[1].innerHTML = ''
// 让第二个select隐藏
sels[1].className = 'hide'
}
}
</script>
<!--根据数据渲染多级联动-->
<div id="category">
</div>
<script type="text/javascript">
var categories=[
{"id":10,"name":'男装',"children":[
{"id":101,"name":'正装'},
{"id":102,"name":'T恤'},
{"id":103,"name":'裤衩'}
]},
{"id":20,"name":'女装',"children":[
{"id":201,"name":'短裙'},
{"id":202,"name":'连衣裙'},
{"id":203,"name":'裤子',"children":[
{"id":2031,"name":'长裤'},
{"id":2031,"name":'九分裤'},
{"id":2031,"name":'七分裤'}
]},
]},
{"id":30,"name":'童装',"children":[
{"id":301,"name":'帽子'},
{"id":302,"name":'套装',"children":[
{"id":3021,"name":"0-3岁"},
{"id":3021,"name":"3-6岁","children":[
{"id":2031,"name":'长裤'},
{"id":2031,"name":'九分裤'},
{"id":2031,"name":'七分裤',"children":[
{"id":101,"name":'正装'},
{"id":102,"name":'T恤'},
{"id":103,"name":'裤衩'}
]}
]},
{"id":3021,"name":"6-9岁"},
{"id":3021,"name":"9-12岁"}
]},
{"id":303,"name":'手套'}
]}
];
// 创建函数
function getSel(arr) {
// 创建一个select标签
var sel = document.createElement('select')
// 向select里面添加一个请选择option标签
sel.add(new Option('-请选择-',-1))
// 遍历数组并且设置option
arr.forEach(obj=>{sel.add(new Option(obj.name,obj.id))})
// 当select里面的内容发生改变得到时候
sel.onchange = function(){
// 拿到当前下标
var i = this.selectedIndex
// 循环判断当前select有没有下一个兄弟 有就删除
while(sel.nextElementSibling) {
sel.nextElementSibling.remove()
}
// 判断是否为请选择项
if (i > 0) {
// 判断是否有children
if (arr[i-1].children) {
// 如果有再次调用函数
getSel(arr[i-1].children)
}
}
}
// 上树
category.appendChild(sel)
}
getSel(categories)
</script>
<script type="text/javascript">
var json = [
{ "name": "杨俊杰", "children": [{ "name": "杨俊" }, { "name": "杨杰" }] },
{ "name": "冯小龙" },
{ "name": "胡畔", "children": [{ "name": "胡田", "children": [{ "name": "胡半" }] }] },
{ "name": "袍哥", "children": [{ "name": "钟哥", "children": [{ "name": "小钟", "children": [{ "name": "钟种" }] }] }] }
];
// 创建函数
function getUl(arr,parent) {
// 创建ul
var ul = document.createElement('ul')
// 遍历数组
arr.forEach(obj=>{
// 创建li
var li = document.createElement('li')
// 设置li的内容
li.innerHTML = obj.name
// 上树
ul.appendChild(li)
// 判断对象是否有children
if (obj.children) {
// 再次调用函数
getUl(obj.children,li)
}
})
// 上树
parent.appendChild(ul)
}
getUl(json,bd)
</script>
<div id="data1"></div>
<script type="text/javascript">
var data = [ { "id": 1001, "name": '可口可乐', "price": 2.5, "count": 3000 }, { "id": 1003, "name": '百事可乐', "price": 2.5, "count": 5000 }, { "id": 1011, "name": '非常可乐', "price": 2.3, "count": 1000 }, { "id": 1012, "name": '天府可乐', "price": 2.3, "count": 1000 }, { "id": 1013, "name": '地府可乐', "price": 2.3, "count": 1000 }, { "id": 1014, "name": '阳间可乐', "price": 2.3, "count": 1000 }, { "id": 1015, "name": '阴间可乐', "price": 2.3, "count": 1000 }, ];
//根据数据生成一个表格
// 创建一个table
var table = document.createElement('table')
// 生成表头
// 创建数组
var arr = ['序号','名称','价格','数量','操作']
// 创建行
var tr = document.createElement('tr')
// 遍历数组
arr.forEach(val=>{
// 创建列
var td = document.createElement('td')
// 设置每一列的内容
td.innerHTML = val
// 上树
tr.appendChild(td)
})
// 上树
table.appendChild(tr)
// 表体
// 遍历
data.forEach(obj=>{
// 创建行
var tr = document.createElement('tr')
// 遍历对象
for(var i in obj) {
// 创建列
var td = document.createElement('td')
// 设置列的内容
td.innerHTML = obj[i]
// 上树
tr.appendChild(td)
}
// 设置操作列
// 创建列
var td = document.createElement('td')
// 设置列的内容
td.innerHTML = '<button onclick = "del(this)">x</button>'
// 上树
tr.appendChild(td)
// 上树
table.appendChild(tr)
})
// 上树
data1.appendChild(table)
// 删除函数
function del(btn) {
var bool = confirm('你确定要删除' + btn.parentNode.parentNode.firstElementChild.nextElementSibling.innerHTML + '吗?')
if (bool) {
btn.parentNode.parentNode.remove()
}
}
</script>
day13
一、事件周期
从事件发生到所有事件处理函数执行完毕的全过程
- 三个阶段:
- 捕获阶段:由外向内,记录要发生的事件有哪些
- 目标优先触发:目标元素就是当前点击的实际发生事件的元素
- 冒泡触发:由内向外,依次执行之前记录的要发生的事件
<div id="max">
<div id="middle">
<div id="min"></div>
</div>
</div>
<script>
var divs = document.querySelectorAll('div')
divs.forEach(div=>{
div.onclick = function(){
alert(div.id)
}
})
</script>
二、获取事件对象event
- 主流:会自动作为事件处理函数的第一个形参传入
- 老IE(8-):event
- 兼容:event
<button id="btn">按钮</button>
<script>
btn.onclick = function(e){
console.log(e);//主流浏览器
console.log(event);//老IE
// 兼容写法
e = e||event
console.log(e);
console.log(event);
}
</script>
三、得到event可以做的
3.1 获取鼠标的坐标:
- 鼠标相对于屏幕的坐标:e.screenX/Y
- 鼠标相对于窗口/客户端/文档显示区域的坐标:e.clientX/Y
- 鼠标相对于网页的坐标:e.pageX/Y
<script>
// 获取鼠标相对于屏幕的坐标
/* window.onmousemove = function(e){
console.log(e.screenX);
console.log(e.screenY);
} */
// 获取鼠标相对于窗口/客户端/文档显示区域的坐标
/* window.onmousemove = function(e){
console.log(e.clientX);
console.log(e.clientY);
} */
// 获取鼠标相对于网页的坐标
window.onmousemove = function(e){
console.log(e.pageX);
console.log(e.pageY);
}
</script>
3.2 阻止事件冒泡:
- 主流:e.stopPropagation()
- 老IE:e.cancelBubble=true
- 兼容:e.cancelBubble=true
<script>
// 阻止事件冒泡 主流浏览器
var divs = document.querySelectorAll('div')
/* divs.forEach(div=>{
div.onclick = function (e) {
alert(this.className)
e.stopPropagation()
}
}) */
// 阻止事件冒泡 老IE
for(var i = 0; i < divs.length; i++) {
divs[i].onclick = function (e) {
alert(this.className)
var e = event
e.cancelBubble = true
}
}
</script>
3.3 利用冒泡排序/事件委托
1. 开发中常用,提升网页性能,有了它我们的事件函数也可以换为箭头函数了
2. 优化:如果多个子元素定义了相同或者相似的事件操作,最好只给父元素定义一次,因为每绑定一个事件函数,其实都是创建了一个事件对象,创建的事件对象越多,网站性能越差。
3. 目标元素target:你点击的是哪一个,他永远就是那一个,不会变化的
- 主流:e.target
- 老IE:e.srcElement
- 兼容:e.srcElement
3.4 阻止浏览器的默认行为:
比如:a标签默认就可以跳转,提交按钮可以提交表单,右键自带一个弹出框,F12自带一个控制台,F11自带全屏功能,F5自带刷新功能
- 主流:e.preventDefault()
- 老IE:e.returnValue=false
- 兼容:e.returnValue=false
<script>
// 阻止a标签默认的跳转
a.onclick = (e) => {
e.preventDefault()
}
// 阻止表单提交
/* form.onsubmit = function(){
return false
} */
submit.onclick = e => e.preventDefault()
// 阻止鼠标右键事件
window.oncontextmenu = e => e.preventDefault()
</script>
四、鼠标和键盘事件
- 右键事件:window.oncontextmenu
- 键盘事件:
- window.onkeydown - 按住和按下,任何键盘按键都可以触发
- window.onkeypress - 按住和按下,只有字母、数字、回车、空格可以触发,其他按键不行
- window.onkeyup - 松开,任何键盘按键都可以触发(比手速的游戏)
- 获取键盘的键盘码: e.keyCode
<script>
// 阻止当按住和按下F12的时候的默认操作
window.onkeydown = e => {
if (e.keyCode == 123) {
e.preventDefault()
}
}
</script>
五、扩展
- 图片的加载速度,比js的执行速度慢,所以js执行完,图片可能还没有加载完,解决:img.onload事件 - 图片加载完毕后才会执行的代码
- 如果事件委托了:
- 判断目标元素是什么标签:xx.nodeName; - 得到标签名,是全大写组成的
- 事件处理函数可以写为箭头函数->this就会失效,所以必须使用目标元素target
- this的指向:
- 单个元素绑定事件,this->这个元素
- 多个元素绑定事件,this->当前元素
- 箭头函数,this->外部对象
day14
一、事件的取消
- 如果你使用的是elem.onclick=()=>{},取消就用elem.onclick=null
- 如果你使用elem.addEventListener("事件名",回调函数),取消就用elem.removeEventListener("事件名",回调函数),事件名和回调函数,必须和添加时的一模一样。
<script>
// 取消绑定事件方法一
/* // btn1绑定点击事件
btn1.onclick = function () {
console.log(1);
}
// 点击btn2的时候取消btn1的点击事件
btn2.onclick = function () {
btn1.onclick = null
} */
// 取消绑定事件方法二
//给btn1绑定点击事件
btn1.addEventListener('click',f1)
btn1.addEventListener('click',f2)
// 点击btn2的时候取消btn1的点击事件
btn2.onclick = function () {
btn1.removeEventListener('click',f1)
}
function f1() {
console.log(11);
}
function f2() {
console.log(22);
}
</script>
二、this的指向
- 单个元素绑定事件this->这个元素
- 多个元素绑定事件this->当前元素
- 箭头函数中的this->外部对象
- 函数中的this->当前正在调用函数的这个人
- 定时器的this->window
<script>
// this的指向
function f1(){
console.log(this);
}
f1();//Window
setInterval(function(){
console.log(this);//Window
},1000)
var th={
"name":"田浩",
}
var ljm={
"name":"刘金明",
}
Object.prototype.zwjs=function(){
return "我的名字叫"+this.name
}
console.log(th.zwjs())
console.log(ljm.zwjs())
</script>
三、ES5强制改变this指向
- call/apply:临时的替换了函数的this - 借用。相当于立刻调用函数,立即执行的。
语法:- 函数名.call(借用的对象,实参,...); - 单独传入每个实参
- 函数名.apply(借用的对象,arr); - 只能传入一个实参要求是一个数组,apply其实会悄悄的打散数组
- bind:永久替换了函数中的this - 买
- 创建了一个和原函数功能完全相同的新函数
- 将新函数的this永久绑定为了指定对象,别人都借不走
- 将新函数的部分参数永久固定
- 语法:
var 新函数=函数名.bind(永久对象,永久实参,...); - 不是立刻执行,需要自己调用 bind绑定的新函数没办法被call/apply再次借走
<script>
// 改变this的指向
function calc(base, bonus1, bonus2) {
console.log(this);
return this.name + "的总工资为:" + (base + bonus1 + bonus2);
}
var th = {
"name": "张三",
}
var ljm = {
"name": "李四",
}
console.log(th.name);//张三
console.log(calc());//Window
// call
console.log(calc.call(th,100,100,100));
console.log(calc.call(ljm,100,100,200));
// apply
console.log(calc.apply(th,[100,100,100]));
console.log(calc.apply(ljm,[100,400,100]));
// bind
var th_calc = calc.bind(th)
console.log(th_calc(500,55,555));
</script>
- 数组获取最大值或最小值
Math.max/min.apply(Math,arr)
- 判断x是不是数组 - 绝对不能使用typeof:只能判断原始/标准类型,不能判断引用类型
Object.prototype.toString.call/apply(x)==="[object Array]";
- 类数组转为普通数组
- 接住=Array.prototype.slice.call/apply(类数组对象)
- 接住=Array.from(类数组对象)
<script>
// 1.数组获取最大值或最小值
var arr = [1,22,3,44,6,23,4,55]
console.log(Math.max.apply(Math,arr));//55
// 2.判断x是不是数组 - 绝对不能使用typeof:只能判断原始/标准类型,不能判断引用类型
var str = '1111'
console.log(Object.prototype.toString.call(arr) == '[object Array]');//true
console.log(Object.prototype.toString.call(str) == '[object Array]');//false
// 3.类数组转为普通数组
var lis = document.querySelectorAll('li')
console.log(Array.prototype.slice.apply(lis));
lis = Array.from(lis)
console.log(lis);
</script>
四、ES6
- 模板字符串:可以直接识别变量,不再需要+运算去拼接了,而且实现了一个简答的js环境,甚至支持再里面书写API
<script>
var price = 5
var count = 3
console.log(`单格为:${price};数量为:${count};总价为:${price*count}`);
</script>
- 解构赋值:解析结构再进行赋值 - 赋值的新方式,并且得到了增强
语法:
- 类似数组的解构赋值: let [a,b,c]=[1,2,3];
let [a,b,c] = [1,2,3]
console.log(a,b,c)//1 2 3
- 类似对象的解构赋值: let {a,b=默认值,c}={c:3,a:1,b:2};//形参可以设置默认值,如果自己传入了,就用自己的
let {name,age,gender} = {name:'张三',age:18,gender:'男'}
console.log(name)//张三
console.log(age)//18
console.log(gender)//男
- 调用函数时,传递实参的顺序无所谓了
function getSalary({n1,n2,n3}) {
return `总薪资为:${n1}+${n2}+${n3}`
}
console.log(getSalary({n2:2000,n1:5000,n3:9000}))
- 函数的返回的结果,可以有多个
function getArr() {
var a = 1
var b = 2
return [a,b]
}
var [a,b] = getArr()
console.log(a,b)//1 2
五、Set和Map新的数据类型
Set:类似于数组的一种数据格式 - 【去重数组,然后再转回数组】
- var s=new Set(arr);
- ...s - 三个点扩展运算符,可以脱掉数组的外套
var arr = [1,2,3,4,5,1,2,3,4,5]
var s = new Set(arr)
console.log(s);//{1, 2, 3, 4, 5}
console.log(...s);//1 2 3 4 5
console.log([...s]);//[1, 2, 3, 4, 5]
- Map:类似于对象的一种数据格式
- var m=new Map();
- 添加:m.set("键","值");
- 获取:m.get("键");
- 清空:m.clear();
- 删除:m.delete("键");
var m = new Map()
m.set('name','张三')
console.log(m);//{'name' => '张三'}
六、for..of循环
for(var v of arr){
v;//值
}
只能遍历索引数组,不能遍历hash数组,意味着也不能遍历对象
<script>
var arr = [1,2,3,4,5,8,8,9,55]
for (var v of arr) {
console.log(v)//v拿到的是数组的每一项
}
</script>
day15
一、正则表达式
定义字符串中字符出现规则的表达式
-
何时使用:切割 替换 【验证】
-
语法:/正则表达式/
-
最简单的正则就是关键字原文 "原文" -> /原文/后缀
-
后缀:
1. g:找全部<br> 2. i:忽略大小写
-
备选字符集:/^[备选字符集]$/
- 一个中括号,只管一位字符
- 正则表达式默认只要满足就不管后续了,解决:前加^,后加/ - 只要做验证必加!
- 一位数字:[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)
- 指定匹配位置
- ^:开头
- $:结尾
- 特殊:两者同时使用,前加^,后加$,表示从头到尾要求完全匹配 - 只要你做【验证】
- 密码强度验证:2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合:/^[0-9A-Za-z]{2,4}$/
- 预判公式:
- (?![0-9]+$) -> 不能全由数字组成,可能有大写、小写、汉字、日文、特殊符号...
- (?![a-z]+$) -> 不能全由小写组成,可能有数字、大写、汉字、日文、特殊符号...
- (?![0-9a-z]+$) -> 不能全由数字组成、也不能全由小写组成、也不能全由数字和小写的组合组成
- /(?![0-9a-z]+)[0-9A-Za-z]{2,4};//2-4位,可以输入数字、字母,但是必须出现一位大写和一位数字的组合
- /(?![0-9a-z]+)(?![A-Z0-9]+$)[0-9A-Za-z]{2,4}/;//必须三者都有
- /(?![0-9A-Za-z]+$)[0-9A-Za-z_]{2,4}/;//至少要有下划线
二、支持正则表达式的字符串API
- 切割:var arr=str.split("固定切割符"/RegExp);
- 替换:
- 基本替换法:
str=str.replace(/正则表达式/后缀,"新内容");
//replace支持支持正则,并且搭配上后缀g就可以找到全部
//缺陷:替换的新内容是一个固定的
- 高级替换法:
str=str.replace(/正则表达式/后缀,function(a,b,c){
console.log(a);//正则匹配到的关键字
console.log(b);//正则匹配到的关键字的下标
console.log(c);//原字符串
return 判断a关键字的长度,而返回不同的星星数量
});
- 格式化:身份证
var id="500215200002022569";
var reg=/(\d{6})(\d{4})(\d{2})(\d{2})(\d{4})/;
id.replace(reg,function(a,b,c,d,e,f,g,h){
//再replace的时候,正则出现了分组,我们会得到更多的形参
//再形参a的后面就会出现n个形参,就看你有多少个分组
//第1个分组获得的内容会保存在第2个形参之中
//第2个分组获得的内容会保存在第3个形参之中
//...
//倒数第二个一定是下标
//倒数第一个一定是原文本身
})
- 格式化:手机号
var phone="15655555555";
var reg=/(\d{4})\d{4}(\d{3})/;
phone=phone.replace(reg,(a,b,c)=>{
return b+"####"+c;
})
console.log(phone);
三、正则对象:
- 创建:
- 直接量:var reg=/正则表达式/后缀;
- 构造函数:var reg=new RegExp("正则表达式","后缀");
- API:
- 验证:var bool=reg.test(用户输入的);
四、扩展
- input的聚焦事件:onfocus
- input的失焦事件:onblur
- form表单的阻止提交:return false
- 实时获取input的内容:oninput
- 鼠标按下事件:onmousedown
- 鼠标松开事件:onmouseup