Web前端高频面试题解析(javascript篇)--- 每日十题(5)

目录

1.JS中对事件流的理解

 1.事件捕获

2.事件冒泡

2.JS中如何阻止事件冒泡和默认行为

1.阻止默认行为

1. preventDefault

2.解决兼容性问题

3.没有兼容性问题

2.阻止冒泡

1.stopPropagation

2.解决兼容性问题

3.JS中实现函数的防抖

1.手写防抖

问题:

2.防抖场景

4.JS中实现函数的节流

1.节流:

2.应用场景

5.ES6中对箭头函数(()=>{})的理解

 1.基础使用跟函数没有很大区别

2.注意点:

1.箭头函数内的this是静态的,总是指向定义时所在的对象,而不是调用时所在的对象。并且this指向时不可以改变的。

2.this始终指向函数声明时所在作用域下的this的值。

3.箭头函数不能当作构造函数,也就是不可以用new命令,否则报错。

4. 箭头函数 不存在argument对象,即不能使用伪数组去接收参数。

3.适用场景

1.不适用的场景

2.适用的场景 

6.JS中“use strict”的含义及使用区别

1.使用区别

1.使调试更加容易

2.变量在赋值之前必须要声明,防止意外的全局变量

3.取消this的强制转换

4.不允许函数参数重名

7.JS中localStorage和sessionStorage的区别

1.localStorage

2.sessionStorage

3.相同点

1.都是保存在浏览器端

2.不会把数据自动的发送给服务器,仅在本地保存

3.只能存储字符串,可以将对象JSON.stringfy()编码之后进行存储

 4.不同点

1.存储大小限制不同:

2.数据有效期不同:

3.作用域不同:

8.JS中常见的几种内存泄漏情况

1.内存泄漏

2.常见内存泄漏

1.意外的全局变量

2.定时器

3.闭包

4.事件监听

9.JS中对事件代理的理解及应用场景

10.JS中对作用域链的理解

1.作用域

1.全局作用域

2.函数作用域(局部作用域)

3.块级作用域

2.作用域链


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.手写防抖

  1. 第一次点击时,不需要进入定时器-->触发事件
  2. 第一次点击后,再次点击,进入定时器计时;如果频繁点击则不停将定时器清0-->不触发事件
  3. 等不在频繁点击,两秒后,才能再次触发事件-->触发事件
  4. 通过函数包裹的形式debounce(getValue, 2000)
  5. 第二个参数应该是函数,也就是指向debounce要返回一个函数(点击时执行的是这个函数)
  6. 判断,如果t有值(有定时器),就清除上一个定时器  clearTimeout(t)
  7. 再次点击就有值了,因为下面有给t赋值(在两秒钟内触发了,就重新计时,清除上一个定时器)
  8. 第一次点击时,不要延迟(不然会体验差),不进入定时器 firstClick
  9. 定时器里面:第一次点之后,两秒钟内还触发了,不要它触发事件;两秒后让它触发事件
<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() 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值