美文网首页
前端基础-JavaScript面试题汇总

前端基础-JavaScript面试题汇总

作者: 麦西的西 | 来源:发表于2021-03-03 20:55 被阅读0次

    1. js有哪几种数据类型?如何判断数据的类型?

    基本类型:Number, String, Boolean, null, undefined, Symbol(ES6新增)
    引用类型:数组,对象,函数(都是对象类型)
    使用typeof来判断,有局限性。数组、对象用typeof判断都是object类型
    使用Object.prototype.toString.call()来判断,比较完美

    typeof NaN // "number"
    typeof function() {} // "function"
    typeof [] // "object"
    typeof {} // "object"
    
    Object.prototype.toString.call(NaN) // "[object Number]"
    Object.prototype.toString.call([]) // "[object Array]"
    

    2. js创建对象的几种方式

    var obj = {}
    var obj = new Object()
    var obj = Object.create(Object.prototype) // Object.create(obj) 创建一个新对象,新对象的__proto__为obj
    

    3. 深浅拷贝

    常见的拷贝对象方法如Object.assign(obj, obj1),扩展运算符...都是浅拷贝
    JSON.parse(JSON.stringify(obj))是深拷贝。但当obj为undefined或者函数时,会报错
    深拷贝可以使用lodash库的cloneDeep(obj)方法
    或者自己封装一个深拷贝函数。主要实现原理就是:如果是基本数据类型,直接复制即可。如果是引用类型,就遍历所有项,如果是基本类型,直接复制;如果是引用类型,执行递归操作。
    具体实现见另一篇文章:JavaScript深浅拷贝

    4. 变量提升

    函数声明与变量声明经常被JavaScript引擎隐式地提升到当前作用域的顶部
    如果变量名与函数名同名,则函数声明会覆盖变量声明
    变量提升的只是声明部分,不包括赋值部分

    console.log(fun) // 打印结果为: f fun() { console.log(2) }
    var fun = 3
    function fun() {
        console.log(2)
    }
    

    5. 作用域链

    js变量作用域分函数作用域和全局作用域。
    函数执行时,从当前作用域开始搜,没有找到的变量,会向上层查找,直至全局函数。这就是作用域链。

    6. 闭包

    函数嵌套,内部函数能够访问外部函数的局部变量。

    function fun() {
        var count = 2
        return function() {
            console.log(count)
        }
    }
    fun()
    

    优点:长外部函数局部变量生命周期
    缺点:容易造成内存泄漏,被内部函数访问的变量需要手动回收

    7. this指向

    普通函数,this指向调用它的那个对象
    箭头函数,没有具体的this,它的this相当于是从上下文继承的,也就是说定义时候的上下文this.(使用call,apply等任何方式都无法改变this的指向)

    8. call, apply, bind

    call, apply和bind是Function原型上的三个方法,都是为了改变函数体内部this的指向。
    call、apply、bind 三者第一个参数都是this要指向的对象,后面的参数call是一个个的参数列表,apply则是放到数组中的
    bind 是返回一个函数,便于稍后调用。call, apply则是立即调用

    9. 原型链

    每个函数都有一个prototype属性,是一个对象,我们称之为原型对象。原型对象有个constructor属性,指向这个函数。
    prototype上的属性和方法可以被实例对象调用。
    prototype还可以用来实现继承,也就是原型链继承。 只需要将子类构造函数的原型指向父类的一个实例
    每个对象都有__proto__属性,指向其构造函数的原型对象。当我们使用对象的属性时候,如果本身不包含某个属性,就会到其构造函数的原型上查找,而构造函数的原型也是对象,也可以向上查找,直到null为止

    10. js如何实现继承

    // 构造函数继承
    function Student() {
        Person.call(this) // 只能继承父类构造函数里面的属性,不能继承原型上的属性和方法
    }
    
    // 原型链继承
    function Student() {}
    Student.prototype = new Person() // 引用类型的原型属性会被所有的实例共享
    
    // 组合继承,二者结合
    function Student() {
        Person.call(this);
    }
    Student.prototype = new Person() // 调用了两次父类构造函数,生成了两份实例(子类实例的属性,在子类实例的__proto__中也存在)
    
    // 寄生组合继承
    function Student() {
        Person.call(this)
    }
    (function(){
        let Super = function() {}
        Super.prototype = Person.prototype
        Student.prototype = new Super() // 比较完美,但时间较为复杂
    })()
    
    // class继承
    class Student extend Person() {}
    

    11. new操作符具体干了什么

    创建一个空对象
    由this变量引用该对象
    该对象继承构造函数的原型(更改this指向)
    把属性和方法加入到this引用的对象
    最后隐式的返回this

    let obj = {}
    obj.__proto__ = Person.prototype
    Person.call(obj)
    

    12. js如何实现一个类?

    // 使用构造函数,方法需要写在构造函数的原型链上
    function Person(name, age) {
        this.name = name
        this.age = age
    }
    Person.prototype.say = function() {
        console.log(this.name)
    }
    var person = new Person('zs', 18)
    person.say()
    // 使用class
    class Person {
        constructor(name, age) {
            this.name = name
            this.age = age
        }
        say() {
            console.log(this.name)
        }
    }
    var person = new Person('zs', 18)
    person.say()
    

    13. 事件队列

    js执行时,遇到同步任务,就将同步任务按照执行顺序排列到执行栈中。
    遇到异步任务,会将此类异步任务挂起,继续执行执行栈中的任务。等异步任务返回结果后,再按照顺序排列到事件队列中。
    主线程先将执行栈中的同步任务清空,然后检查事件队列中是否有任务,如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,则继续将这个异步任务排列到事件队列中。
    主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个循环往复的过程被称为“Event Loop 事件循环”。

    14. 宏任务与微任务

    (1) 宏任务:setTimeout(), setInterval(), requireAnimationFrame()
    宏任务队列所处的队列就是宏任务队列
    第一个宏任务队列中只有一个任务:执行主线程js代码
    宏任务队列可以有多个
    (2) 微任务:new Promise().then(), process.nextTick()
    微任务所处的队列就是微任务队列
    只有一个微任务队列
    上一个宏任务队列执行完毕后,如果有微任务队列,就会执行微任务队列里的所有任务

    15. js事件传播机制

    js事件传播有三个阶段:事件捕获、目标阶段、事件冒泡
    事件捕获:从外到内进行事件传播
    目标阶段:事件传播到事件目标
    事件冒泡:从内到外进行事件传播
    一般情况下,默认都是冒泡阶段触发事件,因此事件触发的顺序是从内到外。
    取消默认事件:W3C的方法是e.preventDefault(),IE则是使用e.returnValue = false

    16. 什么是事件代理?它有什么好处?

    利用事件冒泡的原理,把事件加到父级上,触发执行效果
    好处:减少事件数量,提高性能
    新添加的元素,依然可以触发该事件

    17. 如何阻止事件冒泡

    阻止冒泡:W3C的方法时ev.stopPropagation(), 早期IE的方法是ev.cancelBubble = true。
    需要注意的是ev.stopPropagation()是用来阻止事件传播的,也可以在捕获阶段使用。

    18. cookies与session

    cookie、session都是用来记录用户状态的。不同的是cookie保存在客户端,session保存在服务器。
    具体来说,用户登录网站,服务器会通过response给客户端一个cookie。
    客户端浏览器会把cookie存起来,等下次再次请求该网站的时候,会把请求的url和cookie一同提交给服务器。
    服务器检查该Cookie, 以此来辨认用户状态。 服务器还可以根据需要修改Cookie的内容。
    session保存在服务器上。 客户端浏览器访问服务器的时候, 服务器把客户端信息以某种形式记录在服务器上。
    这就是session。 客户端浏览器再次访问时只需要从该session中查找该客户的状态就可以了。

    19. localstorage与sessionstorage

    localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。
    sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问
    并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅、是会话级别的存储。只允许同一窗口访问。
    而localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。同源可以读取并修改localStorage数据。

    20. Proxy与Reflect

    Proxy 对象用于创建一个对象的代理, 从而实现基本操作的拦截和自定义
    Reflect 是一个内置的对象, 它提供拦截 JavaScript 操作的方法。
    Reflect并非一个构造函数, 所以不能通过new运算符对其进行调用,
    或者将Reflect对象作为一个函数来调用。 Reflect的所有属性和方法都是静态的( 就像Math对象)。

    21. js垃圾回收机制知道哪些

    标记清除:垃圾回收期会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量,以及被环境中变量引用的变量(闭包)的标记。在完成这些后依然存在的标记就是要删除的变量。
    引用计数:引用计数的策略是跟踪记录每个值被使用的次数。当声明了一个变量,并将一个引用类型赋值给该变量时,这个值的引用次数就加1。如果该变量的值变成了另外一个,则这个值的引用次数减1。当这个值的引用次数为0的时候,就可以将它占用的空间回收。
    IE中JavaScript对象通过标记清除的方式就行垃圾回收,但是BOM和DOM对象是用引用技术的方式回收的。也就是说,只要是涉及BOM和DOM,就有可能出现循环引用问题。

    22. 节流和防抖

    // 节流函数
    // 应用场景: 表单提交(频繁点击按钮,只保存一次)
    function throttle(fn, delay) {
        var lastTime = 0
        return function() {
            var nowTime = Date.now()
            if (nowTime - lastTime > delay) {
                fn()
                lastTime = nowTime
            }
        }
    }
    // 防抖函数
    // 应用场景:邮件校验,输入框模糊查询(等输入完了在执行查询)
    function debounce(fn, delay) {
        var timer = null
        return function() {
            clearTimeout(timer)
            timer = setTimeout(fn, 500)
        }
    }
    

    23. script标签中defer与async的区别

    没有defer或async属性,浏览器会立即下载并执行相应的脚本,并且在下载和执行时页面的处理会停止。
    有了defer属性,浏览器会立即下载相应的脚本,在下载的过程中页面的处理不会停止,等到文档解析完成后脚本才会执行。
    有了async属性,浏览器会立即下载相应的脚本,在下载的过程中页面的处理不会停止,下载完成后立即执行,执行过程中页面处理会停止。
    如果同时指定了两个属性,则会遵从async属性而忽略defer属性。


    24. 什么是内存泄漏,哪些操作会造成内存泄漏?

    内存泄漏指的是不再使用或需要的数据,仍然存在于内存中。
    以下操作会造成内存泄漏:
    setTimeout的第一个参数使用字符串而不是函数
    闭包
    控制台日志
    对象循环引用(两个对象彼此引用且彼此保留,会产生一个循环)

    25. let、const、var

    let声明后会形成一个封闭作用域,使得变量只在这个块作用域内有效
    let不存在变量提升,不可重复声明。
    const代表是一个常量,一旦声明,必须初始化。同样会形成一个封闭作用域。
    const 保证的不是变量的值不变,而是变量指向的内存地址的数据不允许改动。对于引用类型(对象 object,数组 array,函数 function),变量指向的内存地址其实是保存了一个指向实际数据的指针,所以 const 只能保证指针是固定的,至于指针指向的数据就无法控制了,所以使用 const 声明引用类型时要慎重。

    26. Promise是什么,Promise作用?

    Promise是异步编程的一种解决方案。
    具体表达:
    语法上来说,Promise是一个构造函数
    功能上来说,Promsie对象用来封装一个异步操作并获取其结果

    Promise作用:
    指定回调函数的方式更加灵活
    回调函数:必须在启动异步任务前指定
    Promise: 启动异步任务,返回Promise对象,给Promise对象绑定回调函数(可以在异步任务结束后执行)
    支持链式调用,解决回调地狱问题。
    回调地狱:不便于阅读,不便于异常处理
    终极解决方案:async...await(完全没有回调函数)
    关于Promise,可以参考另一篇文章:自定义Promise

    27. async...await

    async...await,是异步程的一种解决方案,与Promise有很大关联。
    async函数返回一个Promise对象。可以使用then方法添加回调函数。
    await用于等待一个Promise对象,只能在async函数内部使用。
    await语句的返回值跟await后的表达式相关。
    表达式为Promise对象:await会暂停执行,等待Promise对象resolve,然后返回解析值并回复async函数的执行。
    非Promise对象:直接返回对应的值。
    关于async...await,可以参考另一篇文章:async...await

    28. Iterator作用,它的遍历过程是什么

    Iterator为不同的数据结构提供统一的访问机制。部署了Iterator接口的数据结构能够使用for...of来进行遍历。
    数组、Set和Map原生具备Iterator接口,不用任何处理,可以被for...of遍历。原因在于,它们部署了Symbol.iterator属性。
    凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。

    Iterator 的遍历过程:
    (1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
    (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
    (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
    (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

    29. 说说对模块化开发的认识

    30. ES6对象新增的方法

    Object.is()比较两个值是否相等,与严格的===基本一致。不同之处只有两个,如下:

    Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
    +0 === -0 // true
    NaN === NaN // false
    

    Object.assign()用于对象的合并
    Object.keys()返回对象的键组成的数组
    Object.values()返回对象的值组成的数组
    Object.entries() 返回一个数组,成员是键值对组成的数组

    const obj = { a: 1, b: 2 }
    Object.entries(obj) // [['a', 1], ['b', 2]]
    

    Object.fromEntries()Object.entries()的逆操作,用于将一个键值对数组转换为对象

    31. ES6数组新增的方法

    Array.from()将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
    Array.of() 将一组值,转换为数组
    Array.prototype.find()找出第一个符合条件的数组成员,如果没有,返回undefined
    Array.prototype.findIndex() 返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
    Array.prototype.fill()
    Array.prototype.includes() 数组是否包含某个元素
    Array.prototype.flat() 数组扁平化
    entries(),keys()和values()用于遍历数组,他们都返回一个遍历器对象。keys()是对键名的遍历,values()是对值的遍历,entries()是对键值对的遍历。

    Array.from(new Set([1, 2])) // [1, 2]
    Array.of(1, 2, 3) // [1, 2, 3]
    new Array(4).fill('1') // [1, 1, 1, 1]
    [1, 2, [3, 4]].flat() // [1, 2, 3, 4]
    

    32. ES11新特性

    (1)?.链合并运算符

    let person = {
        info: {
            name: 'Jack',
            age: 18
        }
    }
    let name = person?.info?.name
    

    (2)??null判断运算符,只有左侧为null或者undefined时,才会返回右侧的值

    let name = person?.info?.name ?? 'Jack'
    

    (3)动态导入import(),适用于按需加载,条件加载
    返回一个Promise对象,加载成功后,模块会作为一个对象,当做then回调的参数

    (4)bigint js只能精确到53个二级制位置

    typeof 1n // bigint
    

    (5)Promise.allSettled()接收一组Promise作为参数,必须等所有的promise返回结果后,才会结束
    (6)String.prototype.matchAll()返回一个正则表达式在当前字符串中所有的匹配
    (7)globalThis 获取顶层对象,无论实在浏览器还是Node里

    33. 基本类型与引用类型区别。为什么引用类型存储在堆里?

    基本类型存储在栈中。
    引用类型会在栈中保存一个引用(指针),实际内容存储在堆中。当我们修改一个引用内容的时候,其他引用这块内容的变量,结果也会发生变化。
    就查询速度而言,栈远快于堆
    stack创建时候,大小是确定的,超过额度大小就会发生栈溢出【当js出现死循环或者错误的递归时候】。
    heap大小是不确定的,需要可以一直累加。

    参考资料:
    2020前端基础包过面试题
    script标签中的defer和async属性

    相关文章

      网友评论

          本文标题:前端基础-JavaScript面试题汇总

          本文链接:https://www.haomeiwen.com/subject/hlaaxltx.html