web小结
DOM操作部分
DOM的数据结构是一种树
attribute和property的区别
节点的property
const pList document.querySelectorAll('p')
const p = pList[0]
console.log(p.style.width) //获取样式
p.style.width = '100px' //修改样式
console.log(p.className) //获取class
p.className = 'p1' //修改class
节点的attribute
const pList document.querySelectorAll('p')
const p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name','p1')
p.getAttribute('style')
p.setAttribute('style','font-size:50px;')
区别
property:修改对象属性,不会体现到html结构中(推荐,不会重新渲染DOM)
attribute:修改html属性,会改变html结构。
DOM性能
1.DOM操作非常昂贵,应该避免频繁操作DOM
2.对DOM查询做缓存
3.将频繁操作改为一次性操作
//不缓存DOM查询结果
for(let i=0;i<document.getElementsByTagName('p').length;i++){}
//缓存DOM查询结果
const pList = document.getElementsByTagName('p')
const length = pList.length
for(let i=0;i<length;i++){}
//将频繁操作改为一次性操作
const listNode = document.getrElementById('list')
//创建一个文档片段,此时还没有插入到DOM树中
const frag = document.createDocumentFragment()
//执行插入
for(let x = 0;x<10;x++){
const li = document.createElement('li')
li.innerHTML = 'list"+x
frag.appendChild(li)
}
//都完成之后,再插入到DOM树中
listNode.appendChild(frag)
客户端存储
cookie localstorage 和sessionstorage
cookie
H5之前用来客户端存储数据
document.cookie = 'a:100'
缺点:1.存储大小,最大4KB.
2.http请求时需要发送到服务端,增加请求数据量
3.只能用document.cookie = ’‘来修改,太过简陋
localstorage 和sessionstorage
1.H5新增,专门为了存储而设计,最大可存5M
2.api简单易用:getItem,setItem
3.不会随着http请求发送出去。
区别
localstorage数据会永远存储,除非代码或者手动删除
sessionstorage数据只存储在当前会话,浏览器关闭则清空
一般用localstorage会更多一些
性能优化之防抖与节流
防抖debounce
监听一个input输入框的keyup,但是不能一松开键盘就触发事件,因为可能会频繁调用接口,应该松开若干毫秒后不再按下,才开始调用接口
const input1 = document.getElementById('input')
var timer = null
input1.addEventListener('keyup',function(){
//假如输入123,首先输入1的时候,time是null,触发定时器,
// 500毫秒之后打印,但是在500毫秒之内再次输入了2,触发keyup,此时time!=null
// 就清空了原来应该打印1的定时器,重新执行一个新的定时器打印12,关键在于清空原来的定时器
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
console.log(input1.value)
timer = null
}, 500);
})
// 封装一个方法
function debounce(fn,delay = 500){
// timer在闭包中
let timer =null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn()
// fn.apply(this,arguments) 用这种更完美
timer = null
}, delay);
}
}
input1.addEventListener('keyup',debounce(()=>{
console.log(input1.value)
},1000))
节流throttle
拖拽一个元素时,要随时拿到该元素被拖拽的位置,直接用drag事件,则会频繁触发,很容易导致卡顿。 节流:无论拖拽速度多快,都会每隔100ms触发一次
const div1 = document.getElementById('div1')
var timer = null
div1.addEventListener('drag',function(e){
//存在timer则说明前一次还没执行完,必须前一次执行完,才能执行下一次操作,确保规定时间只执行一次,
// 和防抖的区别在于,防抖是清空原来执行新的,节流是执行原来的,正好相反
if(timer){
return
}
timer = setTimeout(() => {
console.log(e.offsetX,e.offsetY)
timer = null
}, 500);
})
// 封装一个方法
function throttle(fn,delay = 500){
// timer在闭包中
let timer =null
return function(){
if(timer){
return
}
timer = setTimeout(() => {
console.log(this) //this是div1,箭头函数承接上文,就是return的方法
fn.apply(this,arguments) //只是为了绑定事件的参数,fn.apply({},arguments)也可以起到效果
timer = null
}, delay);
}
}
div1.addEventListener('drag',throttle((e)=>{
console.log(this) //this是window 箭头函数承接上文,就是window
console.log(e.offsetX,e.offsetY)
},1000))
div1.addEventListener('drag',throttle(function(e){
console.log(this) //this是div
console.log(e.offsetX,e.offsetY)
},1000))
WEB安全
XSS跨站请求攻击
XSRF跨站请求伪造
1.XSS跨站请求攻击
<body>
<div>123<div>
<script>alert(document.cookie)</script>
</body>
往网页中恶意插入代码,获取用户信息,这样很轻松就获取到了用户的cookie信息,这是很不安全的。
预防
替换特殊字符,如< 变为< >变成>
<script>变为<script>直接显示,不会作为脚本执行
也可以用xss插件 Npm install xss
2.XSRF跨站请求伪造
微信截图_20201014173257.png 微信截图_20201014173325.png
微信截图_20201014173517.png
箭头函数
1.语法编写简洁,但是可读性不是很好。
2.没有自己的this,继承上级的this。
3.没有arguments
4.没有prototype,也就没有Constructor,也就是不能被new.
Flex布局
一、基本概念
采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。
微信截图_20201015151059.png 微信截图_20201015151132.png 微信截图_20201015161833.png
1C:对象以数字123做健名时,会覆盖原来的字符串'123'的健名
2B:symbol是唯一属性
3C:以对象为属性时,会转为字符串'[object,object]',多个对象健名重复,取最后一个。
判断输入的是一个正确的URL
let str = 'https://www.sdafsdfdsf.cn/index.html?name=admin&age=22#video'
let reg = /(^(http|https|ftp):\/\/)?(([\w-]+\.)+[a-z0-9]+)((\/[^/?#]*)+)?(\?[^#]+)?(#.+)?$/i
console.log(reg.exec(str))
console.log(reg.test(str))
正向预查和负向预查
正向预查: ?= 表示一定要匹配, ?=[0-9] 必须匹配数字
负向预查: ?! 表示不能匹配, ?![0-9] 后面不能是数字
正则验证6-16位必须包含大写小写数字的字符串
let reg = /(?!^[a-zA-Z]+$)(?!^[0-9]+$)(?!^[a-z0-9]+$)(?!^[A-Z0-9]+$)(^[a-zA-Z0-9]{6,16}$)/
小括号代表一个组,一个小括号返回一个正则的结果true或者false.
微信截图_20201015172125.png 微信截图_20201016103821.png
答案:
微信截图_20201016104121.png
let obj = new Foo()时,调用了Foo(),当成普通函数执行,就改写了里面的Foo.a的指向,所以最后一个输出是1,new操作之后this就是obj,所以obj.a()输出2,私有属性有a(),就不去共有属性prototype上找了。
手写一个new 函数
function Dog(name){
this.name = name
}
Dog.prototype.bark = function(){
console.log('wangwang'+this.name)
}
function _new(fn, ...arg){
// let obj ={}
// obj.__proto__ = fn.prototype
let obj = Object.create(fn.prototype)
// 上面两句等于Object.create(fn.prototype)
//Object.create(a对象) 等于创建一个空对象,并且让空对象作为a对象所属构造函数的实例,即obj.__proto__ =a对象
fn.call(obj,...arg)
return obj
}
var newDog = _new(Dog,'tom')
newDog.bark()
数据类型转换题
a=?的情况下
满足if(a==1&&a==2&&a==3){}
a= {
n:0,
toString:function(){
++this.n
}
}
非常巧妙,因为当比较的两边数据类型不一样时,都是先转化为数字,对象转换为数字之前,先调用toString()方法转换成字符串,再转换为数字。所以重写它的toString()方法,覆盖原型链上的toString(),连续调用就可以实现以上效果。
再介绍两种方式:
//1
let a= [1,2,3]
a.toString = a.shift
//2
Object.defineProperty(window,'a',{
get:function(){
this.value?this.value++:this.value = 1
return this.value
}
})
各位应该能看懂。
再出一题
微信截图_20201016164545.png接下来三大经典算法
冒泡排序
// 冒泡排序:当前项和后一项比较,如果当前项比后一项大,则让大的靠后
let arr =[12,8,24,16,1,88,66,34,67,99,1]
function getArr(arr){
let count = arr.length
for(let i = 0;i<count-1;i++){
for(let j = 0;j<count-i-1;j++){
if(arr[j]>arr[j+1]){
[arr[j],arr[j+1]] = [arr[j+1],arr[j]]
}
}
}
return arr
}
getArr(arr)
插入排序
//插入排序:打扑克牌,抓一张牌,插到手里,进行排序的过程。
function insert(arr){
let count = arr.length
let newArr = [arr[0]] // 存储第一张牌
for(let i = 1;i<count;i++){ //依次抓牌 i从1开始,因为0已经存进去了
let A = arr[i] //新抓的牌
for(let j= newArr.length-1;j>=0;j--){
let B = newArr[j] //每一次比较手里的牌
if(A>B){
newArr.push(A)
// newArr.splice(j+1,0,A) //如果当前新牌比手里要比较的牌大,就插在后面
break
}
if(j===0){
newArr.unshift(A) //比到第一张了,就插在第一张
}
}
}
console.log(newArr)
return newArr
}
insert(arr)
快速排序
let arr =[12,8,24,16,1,88,66,34,67,99,1]
//快速排序,找到中间项,把他从原来的数组中移除,获取这一项的结果
function quick(arg){
//4.结束递归(ary种小于等于一项)
if(arg.length<=1){
return arg
}
//1.找到数组的中间项,在原有数据中删除
let middleIndex = Math.floor(arg.length/2)
let middleValue = arg.splice(middleIndex,1)[0]
//2.准备左右两个数组,循环剩下数组中的每一项,小的放左大的方右
let arrleft=[],arrRight = [];
for(let i=0;i<arg.length;i++){
if(arg[i]<middleValue){
arrleft.push(arg[i])
} else {
arrRight.push(arg[i])
}
}
//3 递归操作
return quick(arrleft).concat(middleValue,quick(arrRight))
}
let a = quick(arr)
console.log(a)
计算斐波那契
1。普通递归:代码优美逻辑清晰。但是有重复计算的问题,如:当n为5的时候要计算fibonacci(4) + fibonacci(3),当n为4的要计算fibonacci(3) + fibonacci(2) ,这时fibonacci(3)就是重复计算了。运行 fibonacci(50) 会出现浏览器假死现象,毕竟递归需要堆栈,数字过大内存不够。
function fibonacci(n) {
if (n == 1 || n == 2) {
return 1
};
return fibonacci(n - 2) + fibonacci(n - 1);
}
fibonacci(30)
2.方法二:改进递归-把前两位数字做成参数避免重复计算
function fibonacci(n) {
function fib(n, v1, v2) {
if (n == 1)
return v1;
if (n == 2)
return v2;
else
return fib(n - 1, v2, v1 + v2)
}
return fib(n, 1, 1)
}
fibonacci(30)
3.普通for循环
function fibonacci(n) {
var n1 = 1, n2 = 1, sum;
for (let i = 2; i < n; i++) {
sum = n1 + n2
n1 = n2
n2 = sum
}
return sum
}
fibonacci(30)
4.for循环+解构赋值
var fibonacci = function (n) {
let n1 = 1; n2 = 1;
for (let i = 2; i < n; i++) {
[n1, n2] = [n2, n1 + n2]
}
return n2
}
fibonacci(30)