• 2021年前端面试题——JS


    目录:

    DOM事件流有那些阶段?

    解释事件冒泡以及如何阻止它?

    事件委派/事件委托是什么?

    如何理解 JS 中的this关键字?

    更改this指向的方法有那些?

    apply、call、bind 区别?

    讲讲JavaScript 作用域

    js函数声明三种方式

    js变量声明

    JS的数据类型有哪些?

    如何判断JS变量的数据类型?

    什么是闭包?

    谈谈你对JavaScript原型,原型链的理解 

    谈谈你对面向对象编程思想的理解,它有什么特点?

    js 执行机制、事件循环

    同步和异步的区别?

    对前端性能优化有什么了解?一般都通过那几个方面去优化的?

    JavaScript的数组的常用方法

    DOM事件流有那些阶段?

    通常,我们将DOM事件流向分为三个阶段:捕获阶段,目标阶段,冒泡阶段。

    • 捕获阶段是指事件响应从最外层的Window开始,逐级向内层前进,直到具体事件目标元素。在捕获阶段,不会处理响应元素注册的冒泡事件。
    • 目标阶段指触发事件的最底层的元素。
    • 冒泡阶段与捕获阶段相反,事件的响应是从最底层开始一层一层往外传递到最外层的Window。
    参考:事件捕获、事件冒泡以及事件代理

    解释事件冒泡以及如何阻止它?

    事件由具体的dom节点接收,然后这个事件顺着嵌套顺序在父元素上触发。
    防止事件冒泡的一种方法是使用 event.stopPropagation()或 event.cancelBubble (低于 IE 9)。

    参考:10.解释事件冒泡以及如何阻止它?

    事件委派/事件委托是什么?

    事件委派/事件委托适用于未来的元素(动态添加的元素)
    利用事件冒泡,将后代元素上事件的处理程序委派给祖先元素。

    参考:事件委派的使用及作用

    如何理解 JS 中的this关键字?

    this表示当前对象,this的指向是根据调用的上下文来决定的,默认指向window对象。
    上下文环境:全局上下文、函数上下文
    全局上下文中的 this:this 都指向全局对象window
    函数上下文:
    1. 全局函数:this 指向 window 对象 
    2. 作为对象的方法:当函数作为对象的方法调用时,它的 this 值是调用该函数的对象。(箭头函数除外)
    3. 作为构造函数:函数作为构造函数,那函数当中的 this 便 new 出来的对象
    4. 函数调用 apply、call、 bind 时: this 的值就取传入对象的值  (与 apply、call 不同,使用 bind 只会改变一次this的值,无论之后怎么调用)
    5. 箭头函数:this 的值与创建箭头函数的上下文的 this 一致

    也可以用简洁的方式来回答:

    • ES5中:this 永远指向最后调用它的那个对象
    • ES6箭头函数:箭头函数的 this 始终指向函数定义时的 this,而非执行时。

    更改this指向的方法有那些?

    • 使用 ES6 的箭头函数
    • 在函数内部使用 _this = this
    • 使用 apply、call、bind
    • new 实例化一个对象

    apply、call、bind 区别?

    apply 和 call 的区别
    其实 apply 和 call 基本类似,他们的区别只是传入的参数不同。
    call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面
    apply 的所有参数都必须放在一个数组里面传进去
    bind与apply、call最大的区别就是:bind不会立即调用,其他两个会立即调用

    参考:JavaScript 中 call()、apply()、bind() 的用法更改this指向的方法及其区别 

    讲讲JavaScript 作用域

    全局作用域:定义在所有函数之外的变量,其作用范围是在整个脚本中
    局部作用域(函数作用域):使用var定义在函数内部的变量,其作用范围是整个函数结构,超出函数 {} 花括号的范围则不能使用。
    块级作用域:ES6声明变量的方式:let / const 在变量声明的代码段之外是不可见的

    参考:JS作用域与声名提升

    js函数声明三种方式

    //(1)  Function()构造器
    var f =new Function()
    //(2)   函数声明
    function f (){
         console.log(2);
    }
    //(3)   函数表达式
    var f = function() {
          console.log(1);  
    }

    参考:1-1 声明 

    js变量声明

    var、let、const
    var声明的变量会挂载在window上,而let和const声明的变量不会
    var声明变量存在变量提升,let和const不存在变量提升(严格来说,let也存在)
    let和const声明形成块作用域存在暂存死区
    const声明必须赋值

    参考:1-1 声明 、较详细的:JS声明变量的六种方式

    拓展:如果用  const  声明一个对象,改变对象中某个属性的值会发生什么?

    如图:

    为什么    对象中的b的值变了?如果换成直接给  a={b:3}可以吗?

     为什么会报错?

    const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
    对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

     JS的数据类型有哪些?

    基本类型:

    • string(字符串)--原始类型
    • boolean(布尔值)--原始类型
    • number(数字)--原始类型
    • symbol(符号)--原始类型
    • null(空值)undefined(未定义)
    • BigInt(BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值,精度在(2^53-1)范围内,BigInt(10)值为:10n)

     对象类型(引用类型):Array - - (数组)、Function - - (函数)、Date - - (时间)等

     参考:1-2 数据类型的分类:JavaScript的数据类型详细介绍

    如何判断JS变量的数据类型?

    typeof:只能判断string、boolean、number、null、symbol

    instanceof:判断对象类型:测试构造函数的 prototype 是否出现在被检测对象的原型链上

    延展:为什么typeof null为object?

    js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息,而 000代表对象,null的所有机器码均为0,所以,typeof 在判断 null 的时候就出现问题了,由于null 的所有机器码均为0,因此直接被当做了对象来看待。

    参考:1-3 数据类型的判断:

    什么是闭包?

    闭包指的是能够访问另一个函数作用域的变量的函数。
    闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量。

    闭包经典理解

    • 由于var 变量的提升,循环的时候赋值都是用一个i;
    • 因为setTimeout为宏任务,由于JS中单线程eventLoop机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后 才执行setTimeout,但输出i的时候当前作用域没有,往上一级再找,发现了i,此时循环已经结束,i变成了10;
    • 使用let可以解决 let形成块级作用域不会提升
    for (var i = 0; i < 10; i++) {
        //这个时候 i已经 = 10了  var i提升 每次进行赋值都是同一个i;
        setTimeout(() => {
            console.log(i);// 打印10个10
        }, 1000)
    }

    「使用闭包 」

    for (var i = 0; i < 10; i++) {
        ((i) => {
            setTimeout(() => {
                console.log(i);//打印0-9
            }, 1000)
        })(i)
    }

    优点:避免全局变量的污染、希望一个变量长期存储在内存中(缓存变量)
    缺点:内存泄露(消耗)、常驻内存,增加内存使用量

    参考:4 闭包闭包到底是什么?闭包的概念?优缺点?破解前端面试(80% 应聘者不及格系列):从闭包说起

    谈谈你对JavaScript原型,原型链的理解 

    prototype:构造函数的原型对象
    原型链:JS在创建对象的时候,会在新对象上产生一个__proto__的属性,这个属性指向了它构造函数的原型的prototype。由此一级一级向上直到到达Object.prototype.proto === null的这个链条我们称之为原型链。

    参考:用小猪佩奇说明Javascript的原型和原型链

    谈谈你对面向对象编程思想的理解,它有什么特点?

    面向对象编程是一种解决软件复用的设计和编程方法。 这种方法把软件系统中相近相似的操作逻辑和操作 应用数据、状态,以类的型式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。

    面向对象的理解:

    面向对象是一种设计思想

    1. 符合人们的思考习惯
    2. 把执行者变成指挥者
    3. 简化功能,把复杂的事情简单化
    面向对象有三大特征:、封装、继承、多态
     
    拓展:

    什么是封装?

    类是一种封装,将属性和方法封装。
    函数也是一种封装,将具有一定共的逻辑代码封装到一个函数中,使用的时候调用即可

    什么是继承?

    将公共的(共性的)属性和方法放在父类中,子类只关注自己特有的属性和方法。

    js 执行机制、事件循环

    JavaScript 语言的一大特点就是单线程,同一个时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。JavaScript 语言的设计者意识到这个问题,将所有任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous),在所有同步任务执行完之前,任何的异步任务是不会执行的。

    同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入 Event Table 并注册函数。当指定的事情完成时,Event Table 会将这个函数移入 Event Queue。主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的 Event Loop(事件循环)

    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。只要主线程空了,就会去读取"任务队列",这就是 JavaScript 的运行机制

    参考:js 执行机制、事件循环

    拓展:宏任务、微任务

    宏任务:整体代码 script,setTimeout,setInterval
    微任务:Promise,process.nextTick
    我们知道setTimeout这个函数,是经过指定时间后,把要执行的任务(本例中为task())加入到Event Queue中,又因为是单线程任务要一个一个执行,
    如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于你定义的延时时间。https://juejin.cn/post/6844903512845860872#heading-1
    我们还经常遇到setTimeout(fn,0)这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?
    答案是不会的,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。

    参考:这一次,彻底弄懂 JavaScript 执行机制

    同步和异步的区别?

    异步:客户端与服务器请求数据的过程中,可以做其他的事情
    同步:客户端与服务器请求数据的过程中,不能做其他的事情

    参考:同步和异步的区别?

    对前端性能优化有什么了解?一般都通过那几个方面去优化的?

    前端性能优化的七大手段

    1. 减少请求数量
    2. 减小资源大小
    3. 优化网络连接
    4. 优化资源加载
    5. 减少重绘回流
    6. 性能更好的API
    7. webpack优化

    参考:对前端性能优化有什么了解?一般都通过那几个方面去优化的?

    JavaScript的数组的常用方法

    向数组添加元素的方法:

    1. Array.push:向数组的末尾追加       返回值是添加数据后数组的新长度,改变原有数组
    2. Array.unshift:向数组的开头添加    返回值是添加数据后数组的新长度,改变原有数组
    3. splice:向数组的指定index处插入   返回的是被删除掉的元素的集合,会改变原有数组

    向数组删除元素的方法:

    1. pop():从尾部删除一个元素   返回被删除掉的元素,改变原有数组
    2. shift():从头部删除一个元素   返回被删除掉的元素,改变原有数组
    3. splice:在index处删除howmany个元素   返回的是被删除掉的元素的集合,会改变原有数组

    数组排序的方法:

    1. everse():反转,倒置  改变原有数组
    2. sort():按指定规则排序 改变原有数组

    参考:JavaScript的数组的常用方法(一)js常用数组方法

    持续更新呦!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    本博客文章大多为原创,转载请请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    【原】yield的最基本用法
    【转】C#.net拖拽实现获得文件路径
    【原】.Net之美学习笔记-第1章-1.1.1值类型
    【转】怎样将DataGridView中绑定的表的列名改成中文
    【转】WPF获取外部EXE图标最简单的方法
    【转】C# Excel 导入到 Access数据库表(winForm版)
    【转】SQL2008清除日志
    【原】监视程序运行时间
    【转】MSSQL获取指定表的列名信息,描述,数据类型,长度
    【原】接口
  • 原文地址:https://www.cnblogs.com/yingzi1028/p/14382489.html
Copyright © 2020-2023  润新知