2022面试题记录

90 阅读9分钟

Vue面试题

  1. 页面与组件生命周期执行顺序先后
页面 beforeCreate
页面 created
页面 beforeMount
组件 beforeCreate
组件 created
组件 beforeMount
组件 mounted
页面 mounted

页面 beforeUpdate
组件 beforeUpdate
组件 updated
页面 updated

页面 beforeDestroy
组件 beforeDestroy
组件 destroyed
页面 destroyed
  1. exprot 和 export default 区别
  • 在 JavaScript ES6 中,export 与 export default 均可用于导出常量、函数、文件、模块
  • export 命令用于规定模块的对外接口,import 命令用于输入其他模块提供的功能。export default 命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,既export default 命令只能使用一次。所以,import 命令后面才不用加大括号,因为只可能唯一对应。export default 命令。一个文件内不能有多个 export default。
  • export { str as newname1, year as newname2, message as newname3 }; export default str
  • (1) 输出单个值,使用 export default (2) 输出多个值,使用 export (3) export default 与普通的 export 不要同时使用
  1. JS 哪些操作会造成内存泄露
  • 意外的全局变量引起的内存泄露:
function fn(){ leak="xxx";//leak 成为一个全局变量,不会被回收}
  • 闭包引起的内存泄漏:闭包会维持函数内部局部变量,使其得不到释放
function bindEvent(){
    var obj=document.createElement("XXX");
    obj.οnclick=function(){
        //Even if it's a empty function
    }
}
  • 没有清理的 DOM 元素引用:
var elements={
    button: document.getElementById("button"),
    image: document.getElementById("image"),
    text: document.getElementById("text")
};
function doStuff(){
    image.src="http://some.url/image";
    elements.button.click():
    console.log(text.innerHTML)
}
function removeButton(){
    document.body.removeChild(document.getElementById('button'))
}
  • 被遗忘的定时器或者回调:
var someResouce=getData();
setInterval(function(){
    var node=document.getElementById('Node');
    if(node){
        node.innerHTML=JSON.stringify(someResouce)
    }
},1000)
  1. 怎样避免内存泄露:
  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
  • 注意程序逻辑,避免“死循环”之类的;
  • 避免创建过多的对象,原则:不用了的东西要及时归还。
  1. 为什么用 Object.prototype.toString.call(obj)检测对象类型?
    • typeof 不能准确判断一个对象变量(typeof null new Map() new Set() Array 得到的结果都是 object)
    • 为了解决 typeof 的问题,使用 Object.prototype.toString.call(obj)能更好的判断变量类型
    • toString 为 Object 的原型方法,而 Array 、Function 等类型作为 Object 的实例,都重写了 toString 方法。不同的对象类型调用 toString 方法时,根据原型链的知识,调用的是对应的重写之后的 toString 方法。而不会去调用 Object 上原型 toString 方法(返回对象的具体类型),所以采用 obj.toString()不能得到其对象类型,只能将 obj 转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用 Object 上原型 toString 方法。
var arr=[1,2,3];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"
    console.log(Object.prototype.toString.call("jerry"));//[object String]
    console.log(Object.prototype.toString.call(12));//[object Number]
    console.log(Object.prototype.toString.call(true));//[object Boolean]
    console.log(Object.prototype.toString.call(undefined));//[object Undefined]
    console.log(Object.prototype.toString.call(null));//[object Null]
    console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object]
    console.log(Object.prototype.toString.call(function(){}));//[object Function]
    console.log(Object.prototype.toString.call([]));//[object Array]
    console.log(Object.prototype.toString.call(new Date));//[object Date]
    console.log(Object.prototype.toString.call(/\d/));//[object RegExp]
    function Person(){};
    console.log(Object.prototype.toString.call(new Person));//[object Object]
  1. 浏览器缓存
  • cookie 和 session 区别
    1. cookie 数据存放在浏览器中,session 数据存放在服务器上
    2. cookie 是不安全的,别人可以分析存放在本地的 cookie 并进行 cookie 诈骗,考虑到安全性能,应尽量使用 session
    3. session 会在一定时间内保存在服务器上。当访问增多时,会比较占用服务器的性能。考虑到服务性能,应尽量使用 cookie
    4. 单个 cookie 保存的数据不能超过 4k,很多浏览器都限制一个站点最多保存 20 个 cookie
  • cookie,sessionStorage,localStorage 区别
    1. cookie,localStorage,sessionStorage 都是在客户端保存数据,存储数据的类型:字符串
    2. webStorage 不会随着 HTTP header 发送到服务器端,所以安全性相对来说比 cookie 高,不必担心截获
    3. 生命周期不同,localStorage 要手动清除,sessionStorage 在浏览器关闭后清除
  • 生命周期 cookie :可设置失效时间,否则默认为关闭浏览器后消失 localStorage :除非被手动清除,否则永久保存 sessionStorage:仅在当前网页会话下有效,关闭页面或浏览器后就会被清除
  1. call 和 apply 的区别
    • call 和 apply 都是用来修改函数中 this 的指向问题
    • 通过 call 和 apply 来 this 的指向时,不传任何参数,则默认为将 this 指向修改为 windows // fn.call() fn.applyy()
    • 需要传递参数时,call 可以直接写多个参数,apply 需要用数组方式传递
      • fn.call(this 指向的对象, 'swimming', 'hiking')
      • fn.apply(this 指向的对象, ['swimming', 'hiking'])
        const obj = {
            name: "obj",
            age: 18,
        }
        function fn() {
            console.log(this);
        }
        fn()  // Window 
        fn.call(obj)   // {name: 'obj', age: 18}
  1. 堆和栈的区别

    • 堆比栈空间大,栈比堆运行速度快。
    • 堆内存是无序存储,可以根据引用直接获取。
    • 基础数据类型比较稳定,而且相对来说占用的内存小。
    • 引用数据类型大小是动态的,而且是无限的。
  2. 数据类型

    • 基本数据类型: Number,String,Boolean,null,undefined,symbol,bigint(后两个为 ES6 新增) 基本数据类型是直接存储在栈中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基 本类型值和执行代码的空间。
    • 引用数据类型: object,function(proto Function.prototype) Array 引用数据类型是存储在堆内存中,占据空间大、大小不固定。引用数据类型在栈中存储了指针
  3. Object.assign 的理解

    • 作用:Object.assign 可以实现对象的合并。
    • 语法:Object.assign(target, ...sources)
    • 解析: Object.assign 会将 source 里面的可枚举属性复制到 target,如果和 target 的已有属性重名,则会覆盖。后续的 source 会覆盖前面的 source 的同名属性。 Object.assign 复制的是属性值,如果属性值是一个引用类型,那么复制的其实是引用地址,就会存在引用共享的问题。
  4. constructor 的理解

    • 创建的每个函数都有一个 prototype(原型)对象,这个属性是一个指针,指向一个对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性是一个指向 prototype 属性所在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(继承自构造函数的 prototype),指向构造函数的原型对象。注意当将构造函数的 prototype 设置为等于一个以对象字面量形式创建的新对象时,constructor 属性不再指向该构造函数。
  5. map 和 forEach 的区别

    • 相同点: 都是循环遍历数组中的每一项 每次执行匿名函数都支持三个参数,参数分别为 item(当前每一项),index(索引值),arr(原数组) 匿名函数中的 this 都是指向 window 只能进行遍历数组
    • 不同点: map()会分配内存空间存储新数组并返回,forEach()不会返回数据。 forEach()允许 callback 更改原始数组的元素。map()返回新的数组。
  6. js 是动态类型语言

    • 使用静态类型的优势 可以尽早发现 bug 和错误 减少了复杂的错误处理 将数据和行为分离 减少单元测试的数量 提供了领域建模(domain modeling)工具 帮助我们消除了一整类 bug 重构时更有信心

    • 使用静态类型的劣势 代码冗长 需要花时间去掌握类型

  7. 变量提升

  • JavaScript 是单线程语言,所以执行肯定是按顺序执行。但是并不是逐行的分析和执行,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段。在编译阶段阶段,代码真正执行前的几毫秒,会检测到所有的变量和函数声明,所有这些函数和变量声明都被添加到名为 Lexical Environment 的 JavaScript 数据结构内的内存中。所以这些变量和函数能在它们真正被声明之前使用。
  1. 作用域

    • 作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
    • ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现。
  2. 变量声明

    - var ——ES5 变量声明方式
    

    在变量未赋值时,变量 undefined(为使用声明变量时也为 undefined) 作用域——var 的作用域为方法作用域;只要在方法内定义了,整个方法内的定义变量后的代码都可以使用

    - let——ES6 变量声明方式
    

    在变量为声明前直接使用会报错 作用域——let 为块作用域——通常 let 比 var 范围要小 let 禁止重复声明变量,否则会报错;var 可以重复声明

    - const——ES6 变量声明方式
    

    const 为常量声明方式;声明变量时必须初始化,在后面出现的代码中不能再修改该常量的值 const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动

  3. javascript 中 arguments 相关的问题

    • 在 js 中,我们在调用有参数的函数时,当往这个调用的有参函数传参时,js 会把所传的参数全部存到一个叫 arguments 的对象里面。它是一个类数组数据
    • Javascrip 中每个函数都会有一个 Arguments 对象实例 arguments,引用着函数的实参。它是寄生在 js 函数当中的,不能显式创建,arguments
  4. 原型 - 构造函数:创建新对象时初始化的函数叫做构造函数.

    - 实例(对象):用 new 调用构造函数创建出来的对象叫做实例,或是实例对象.
    - prototype 属性:也叫原型属性,它是函数独有的,每个函数都有一个 prototype 属性,它是一个指针,指向一个对象,这个对象包含了所有实例共享的属性和方法.
    - _proto_: 实例对象都有的属性,指向了该实例对象对应的原型对象.
    - prototype 与*proto*的区别
      1. prototype 是函数独有的,而*proto*是每个对象都会拥有的(包括函数)
      2. prototype 的作用是保存所有实例公共的属性和方法;*proto*的作用是当访问一个对象的属性时,如果内部没有该属性,就会在它的*proto*属性所指的那个父对象去找,父对象没有,再去父对象的父对象里找…直到 null,即原型链.
    
        // instanceof主要作用就是判断一个实例是否属于某种类型
        let person = function(){}
        let no = new person()
        no instanceof person   //true
        // 手写实现instanceof
        function new_instance_of(leftVaule, rightVaule) {
            let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
            leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
            while (true) {
                if (leftVaule === null) {
                    return false;
                }
                if (leftVaule === rightProto) {
                    return true;
                }
                leftVaule = leftVaule.__proto__
            }
        }
    
  5. hasOwnProperty 这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链。