目录
1.箭头函数内的this是静态的,总是指向定义时所在的对象,而不是调用时所在的对象。并且this指向时不可以改变的。
3.箭头函数不能当作构造函数,也就是不可以用new命令,否则报错。
4. 箭头函数 不存在argument对象,即不能使用伪数组去接收参数。
7.JS中localStorage和sessionStorage的区别
3.只能存储字符串,可以将对象JSON.stringfy()编码之后进行存储
1.JS中对事件流的理解
事件流:从页面中接收事件的顺序。事件流分为三个阶段:捕获阶段,当前目标阶段,冒泡阶段。
addEventListenter(事件,回调函数,布尔值(false:冒泡;true:捕获,默认为false))
1.事件捕获
不太具体的节点先接收到事件,而最具体的节点最后接收事件
<div class="box">
父元素
<div class="one">子元素</div>
</div>
<script>
var one = document.querySelector('.one')
one.addEventListener('click', function () {
alert('子元素')
}, true)
var box = document.querySelector('.box')
box.addEventListener('click', function () {
alert('父元素')
}, true)
// 点击子元素,先弹出父元素,再弹出子元素
</script>
2.事件冒泡
最具体的节点先接收事件,不太具体的节点最后接收到事件。
var one = document.querySelector('.one')
one.addEventListener('click', function () {
alert('子元素')
})
var box = document.querySelector('.box')
box.addEventListener('click', function () {
alert('父元素')
})
// 点击子元素,先弹出子元素,再弹出父元素
2.JS中如何阻止事件冒泡和默认行为
事件对象:event对象,(这个对象是浏览器通过回调函数得到的,把这个对象作为参数传递过来)当成形参来看,包含所有与事件相关的信息,比如触发事件的元素,事件的类型...
1.阻止默认行为
1. preventDefault
只要默认的文字,不会跳转的功能。preventDefault存在兼容性问题,ie678。
<a href="https://www.w3school.com.cn/">w3school</a>
// 阻止默认行为
var a = document.querySelector('a')
a.addEventListener('click', function (e) {
e.preventDefault()
})
2.解决兼容性问题
returnValue阻止默认行为
var a = document.querySelector('a')
a.addEventListener('click', function (e) {
e.returnValue = false
})
3.没有兼容性问题
没有兼容性问题,只限于传统方式。
var a = document.querySelector('a')
// 要使用传统的监听方式
a.onclick = function (e) {
return false
}
2.阻止冒泡
- 冒泡好处:实现事件委托
- 冒泡坏处:只想要子元素的逻辑,但是点击子元素的时候,就会触发父元素的逻辑
1.stopPropagation
<div class="box">
父元素
<div class="one">子元素</div>
</div>
var box = document.querySelector('.box')
// 阻止事件冒泡
var one=document.querySelector('.one')
one.addEventListener('click',function(e){
alert('子元素')
e.stopPropagation()
})
box.addEventListener('click',function () {
alert('父元素')
})
2.解决兼容性问题
适合ie678。
// 阻止事件冒泡
var one = document.querySelector('.one')
one.addEventListener('click', function (e) {
alert('子元素')
e.cancelBubble = true
})
box.addEventListener('click', function () {
alert('父元素')
})
3.JS中实现函数的防抖
减少函数调用的次数,限制函数执行的次数。
通过setTimeout的方式,是一定的时间间隔内,将多次触发变成一次触发。
1.手写防抖
- 第一次点击时,不需要进入定时器-->触发事件
- 第一次点击后,再次点击,进入定时器计时;如果频繁点击则不停将定时器清0-->不触发事件
- 等不在频繁点击,两秒后,才能再次触发事件-->触发事件
- 通过函数包裹的形式debounce(getValue, 2000)
- 第二个参数应该是函数,也就是指向debounce要返回一个函数(点击时执行的是这个函数)
- 判断,如果t有值(有定时器),就清除上一个定时器 clearTimeout(t)
- 再次点击就有值了,因为下面有给t赋值(在两秒钟内触发了,就重新计时,清除上一个定时器)
- 第一次点击时,不要延迟(不然会体验差),不进入定时器 firstClick
- 定时器里面:第一次点之后,两秒钟内还触发了,不要它触发事件;两秒后让它触发事件
<input type="text" id="ipt" value="123">
<input type="submit" id="btn" value="提交">
<script>
var ipt = document.querySelector('#ipt')
var btn = document.querySelector('#btn')
// btn.addEventListener('click', getValue)
btn.addEventListener('click', debounce(getValue, 2000))
function getValue (e) {
var val = ipt.value
console.log(val)
console.log(this)//Window
console.log(e)//undefined
// console.log(e[0])//undefined
}
// 限制getValue
function debounce (fn, time) {
//定义最开始的定时器
var t = null
// 点击时,真正执行的是这个函数
return function (e) {
// console.log(e)//PointerEvent,但是我们想要事件对象在getValue中拿到。用e传参
// 可以使用arguments
// console.log(this)//button
// 拿到外部函数的this
var that = this
if (t) {//判断定时器是否生成,如果生成了,清除上一个定时器
clearTimeout(t)
}
// 第一次点击,就是!t,t为true
var firstClick = !t
//如果是第一次点击就立即执行
if (firstClick) {
// fn(e)
// fn(arguments)
// apply改变this指向,从window指向button(getValue)
fn.apply(that, arguments)
}
// 定时器里面,两秒钟后让t为null,进入一个重新的定时器
t = setTimeout(function () {
t = null
}, time)
}
}
// 首先time为null,如何对t进行赋值,t有值后,再次点击,会执行到判断if(t)去
// 第一次提交的时候,不需要进入定时器
</script>
问题:
- e是undefined:因为函数防抖进行包装了的,所以它调用的是debounce中return的函数,拿到的事件对象就在这里;
- 但是我们想要在getValue中拿到事件对象,可以通过传参e;
- 但是还不能够确定只有一个参数,可以后面还有一些形参需要传,可以使用arguments对象(用来接收所有传过去的实参)
- 需要这样才能获取arguments:console.log(e[0]),所以可以通过apply改变this指向,进行传参
- 需要getValue的this也是指向button,使用apply拿到外部函数的this
2.防抖场景
- 登录,发验证码;
- 当用户点击太快,以及发送多次请求;
- 调整浏览器窗口的时候,resize次数过于频繁,造成计算过多,需要一次直接计算到位;
- 搜索框搜索输入,在输入完所有内容之后,等待一两秒用户没有输入,再进行搜索。
4.JS中实现函数的节流
防抖和节流:限制函数执行的次数。
1.节流:
减少一段时间的触发频率(时间戳),控制事件发生的频率,控制在2s发生一次。
<input type="text" id="ipt" value="123">
<input type="submit" id="btn" value="提交">
<script>
var ipt = document.querySelector('#ipt')
var btn = document.querySelector('#btn')
btn.addEventListener('click', throttle(getValue, 2000))
function getValue () {
var val = ipt.value
console.log(val)
}
function throttle (fn, time) {
// 设置时间的初始值(上一次点击的时间)
var begin = 0
return function () {
// 拿到当前的时间戳
var date = new Date().getTime()
// 改变this指向,拿外部函数的this
var that = this
console.log(date - begin)
// 当前-上一次时间
if (date - begin > time) {
fn.apply(that, arguments)
// 当前的时间就变成了上一次点击时间
begin = date
}
}
}
</script>
2.应用场景
- scroll:每隔一秒计算一次位置信息
- 搜索框实时搜索,并且发送请求,展示下拉列表,每隔两秒发送一次请求
5.ES6中对箭头函数(()=>{})的理解
在书写代码的时候就可以确定this指向。
1.基础使用跟函数没有很大区别
//ES6之前,谁调用了函数,this就指向谁
let fun = function (a, b) {
console.log(a + b)//4
}
//ES6之后,使用箭头函数
let fun1 = (a, b) => {
console.log(a + b)//4
}
fun(1, 3)
fun1(1, 3)
2.注意点:
1.箭头函数内的this是静态的,总是指向定义时所在的对象,而不是调用时所在的对象。并且this指向时不可以改变的。
var name = 'zz'
var obj = {
name: 'ls'
}
function fun () {
console.log(this.name)
}
fun()//zz
fun.call(obj)//ls
let fun1 = () => {
console.log(this.name)
}
fun1()//zz
fun1.call(obj)//zz
2.this始终指向函数声明时所在作用域下的this的值。
var name = 'zz'//全局定义name
var obj = {//obj里面定义name属性
name: 'ls'
}
function fun () {
var name = 'AA'//函数里name
console.log(this.name)
}
function fn () {//fn的this指向window
var name = 'BB'
let fun1 = () => {//函数里写了箭头函数
console.log(this.name)
}
fun1()
}
fun()//zz
// 调用:全局调用
fn()//zz
// 调用:this指向函数声明时所在作用域(fn()),指向fn()的this-->window
// 可以通过更改fn作用域下的this,把name改为ls,通过改变外部作用域的this,从而改变箭头函数的this
fn.call(obj)//ls
//fn的this指向obj
3.箭头函数不能当作构造函数,也就是不可以用new命令,否则报错。
let Person = (name, age) => {
this.name = name
this.age = age
}
let child = new Person('zz', 10)
//Person is not a constructor,Person不是构造函数,不能这样使用
4. 箭头函数 不存在argument对象,即不能使用伪数组去接收参数。
可以使用rest参数代替。
function getArg () {
console.log(arguments)
}
getArg(1, 2, 3, 4)//Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
let funArg = () => {
console.log(arguments)
}
funArg(1, 2, 3, 4)//arguments is not defined at funArg
let fun1Arg = (...b) => {
console.log(b)
}
fun1Arg(1, 2, 3, 4)//(4) [1, 2, 3, 4]
3.适用场景
点击之后让box变成newBox
.box {
width: 200px;
height: 200px;
border: 1px solid pink;
}
.newBox {
width: 200px;
height: 200px;
background-color: tomato;
}
<div class="box">我是box</div>
// ES6之前
var box = document.querySelector('.box')
box.addEventListener('click', function () {
this.className = 'newBox'
})
1.不适用的场景
与this有关的回调,事件回调,对象的方法回调。
// ES6之后,
不适用的场景,与this有关的回调,事件回调,对象的方法回调
box.addEventListener('click', () => {
console.log(this)//Window
})
2.适用的场景
与this无关的回调,定时器,数组的方法回调 。
//适用的场景
//与this无关的回调,定时器,数组的方法回调
var box = document.querySelector('.box')
// 定时器的this指向window,给window添加className没有用,所以需要指向box,外面的函数function指向的是box
box.addEventListener('click', function () {
var that = this
setTimeout(function () {
console.log(this)//Window
console.log(that)//box
// this.className = 'newBox'
that.className = 'newBox'
}, 2000)
})
//this指向函数function 所在的作用域下的this
box.addEventListener('click', function () {
setTimeout(() => {
this.className = 'newBox'
}, 2000)
})
6.JS中“use strict”的含义及使用区别
使用严格模式,不会支持一些不规范的语法
1.使用区别
1.使调试更加容易
2.变量在赋值之前必须要声明,防止意外的全局变量
"use strict"
function fun () {
a = 10
}
fun()
console.log(a)//10
// 不加可以显示,加了就会报错
"use strict"
// a is not defined at fun
3.取消this的强制转换
var age = 10
function fun () {
console.log(this.age)
}
fun()//10
// 引用了null或者未定义的值,this值会自动强制到全局变量
fun.apply(null)//10
// 在严格模式下,会抛出错误,this不会指向window
"use strict"
// Cannot read properties of undefined(reading 'age') at fun
4.不允许函数参数重名
function fun (a, b, a) {
console.log(a, b)//这个a拿到的使最后的a
}
fun(1, 2)//undefined 2
"use strict"
// Duplicate parameter name not allowed in this context 不能重复定义
7.JS中localStorage和sessionStorage的区别
1.localStorage
永久的存储在本地,关了还是存在。适合保存在本地的数据
2.sessionStorage
绘画级的存储,关完之后就不存在了。适合敏感账号一次性登录
3.相同点
1.都是保存在浏览器端
2.不会把数据自动的发送给服务器,仅在本地保存
3.只能存储字符串,可以将对象JSON.stringfy()编码之后进行存储
sessionStorage.setItem('a', 123)
console.log(sessionStorage.getItem('a'))//123
console.log(typeof sessionStorage.getItem('a')) //检测:string
4.不同点
1.存储大小限制不同:
- sessionStorage存储的大小为:5M
- localStorage存储大小为:20M
2.数据有效期不同:
- sessionStorage:始终有效,窗口关闭或者浏览器关闭,一直保存,持久保存数据
- localStorage:仅在当前浏览器窗口关闭前有效,会话级存储
localStorage.setItem('b', 234)
3.作用域不同:
- sessionStorage:在不同的浏览器窗口不会进行共享,只有同一个页面中
-
localStorage:在所有的同源的窗口下可以共享的
比如:
http://127.0.0.1:5500/下都可以看见localStorage,但是看不见sessionStorage
8.JS中常见的几种内存泄漏情况
1.内存泄漏
由于疏忽或错误造成程序未能释放已经不再使用的内存。
2.常见内存泄漏
1.意外的全局变量
function fun () {
a = 10
console.log(a)
this.b = 20//this指向window
}
fun()
console.log(a)//10
// 这个a没有销毁,还是可以显示值,是全局变量
console.log(b)//20
// 拿到的是window里面的b属性
解决:需要使用严格模式,此时会报错
'use strict'
//Cannot set properties of undefined (setting 'b')
2.定时器
没有及时清除的DOM元素,所以在不用的时候,及时清除定时器。
// 没有及时清除的DOM元素,当test清除后,要把定时器也清除掉,test=null
var main = document.querySelector('.main')
var time = setInterval(() => {
var test = document.querySelector('.test')
main.removeChild(test)
console.log(test)//<div class="test">nihao</div>
test = null//清除变量test
if (!test) {
clearInterval(time)//清除定时器
}
}, 2000)
//不清除的话会报错
'use strict'
// Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'
3.闭包
'use strict'
function fun (name) {
function fun1 () {//闭包
console.log(name)
}
return fun1()
}
var fn = fun('zz')
fn()
//fn is not a function
// name局部变量,外部应该访问不到,fun1被全局变量引用
// 想要释放内存的话
fn = null
fn()//此时就执行不出来了
4.事件监听
解决:在不使用的时候就取消事件监听。
9.JS中对事件代理的理解及应用场景
事件代理(事件委托):把一个元素响应事件的函数委托到另一个元素上。
事件流:捕获阶段-->目标阶段-->冒泡阶段(事件委托)
事件委托是在冒泡阶段完成的,冒泡好处:可以实现事件委托。
把一个元素或者一组元素委托到它的父级或者更外层上面,真正绑定元素的是外层元素,而不是目标元素
要求:点击li,打印出点击的HTML
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
</ul>
<script>
var lis = document.querySelectorAll('li')
for (let i = 0; i < lis.length; i++) {
// 每一个元素添加点击事件
lis[i].onclick = function (e) {
// e.target返回触发事件的对象
console.log(e.target)//谁触发了,返回谁
console.log(e.target.innerHTML)//谁触发了,返回谁
}
}
</script>
// 可以把点击事件绑定在父级上面,通过冒泡触发事件
let oul = document.querySelector('#list')
oul.onclick = function (e) {
console.log(e.target.innerHTML)
}
只要操作一次DOM,从而提高了程序的性能。
如果用户需要去除,添加事件
// 可以把点击事件绑定在父级上面,通过冒泡触发事件
let oul = document.querySelector('#list')
oul.onclick = function (e) {
console.log(e.target.innerHTML)
}
// 拿到原本btn数量
let num = 4
//拿到btn
let obt = document.querySelector('#btn')
// 写点击事件
obt.onclick = function () {
//加btn数量
num++
//创建li标签
let oli = document.createElement('li')
//添加元素
oli.innerHTML = `item${num}`
//添加元素到oli
oul.appendChild(oli)
}
10.JS中对作用域链的理解
- 查找属性:找原型链
- 查找变量:找作用域链
1.作用域
变量和函数生效的区域。
function fun () {
let a = 10
}
fun()
console.log(a)//a is not defined
// 在全局是无法获取函数内部的变量(闭包除外)
1.全局作用域
任何不在函数中或者大括号中声明的变量,都是在全局作用域下。
var a = 10
function fun () {
console.log(a)//10
}
fun()
全局作用域下的声明的变量可以在程序中任何位置访问。
2.函数作用域(局部作用域)
函数作用域中定义的变量,只能在函数内部访问,不能在函数外部访问。
function fun2 () {
var b = 20
console.log(b)//20
}
fun2()
console.log(b);//b is not defined
3.块级作用域
let和const定义的变量在大括号外面不可以被访问到。
{
let uname = 'zz'
const age = 10
var sex = 'nan'
console.log(uname, age, sex)//zz 10 nan
}
console.log(window)
console.log(sex)//nan
console.log(uname, age)//uname is not defined
2.作用域链
js使用一个变量,首先js引擎会在当前作用域下查找,如果没有找到,去上层作用域寻找,依次类推,直到找到变量或者到达全局作用域;如果没有找到,会直接报错或者隐式声明。
在小作用域内找不到,往外走找全局作用域,找不到就找不到了
var sex = 'nam'
function person () {
var name = 'zz'
function student () {
var age = 10
console.log(name)//zz
// 当前作用域-->上级作用域(找到)
console.log(sex)//nam
// 当前作用域-->上级作用域-->全局作用域(找到)
}
student()
console.log(age)//报错
// 当前作用域person()-->全局作用域(未找到)
}
person()