美文网首页
js面试题

js面试题

作者: maomizone | 来源:发表于2024-01-06 10:08 被阅读0次

    1.for in和for of的区别

    for in

    • 遍历对象可枚举的属性,包括原型链上面的属性

    for of

    • 适用可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)实现了[symbol.iterator]接口
    • 但是不能遍历对象,因为没有迭代器对象
    • 只会输出当前对象的值,不会去找原型链
    const arr = [1,2,3,4]
     
    // for ... in 输出索引
    for (const key in arr){
        console.log(key) // 输出 0,1,2,3
    }
     
    // for ... of 输出value
    for (const key of arr){
        console.log(key) // 输出 1,2,3,4
    }
    

    2.数组和伪数组的区别

    伪数组的关联对象不是数组的prototype属性指向的对象,所以没有数组的方法

    3.浅拷贝和深拷贝的区别

    • 浅拷贝---浅拷贝只拷贝一层,更深层次对象级别的只拷贝引用
    • 深拷贝---深拷贝拷贝多层,每一级别的数据都会拷贝
        // ES5浅拷贝
        let obj = {
                    id: 1,
                    name: 'andy',
                    msg: { title: '标题' },
                    color: ['red', 'green']
                };
    
       let o = {};
    
       for(let k in obj){
           o[k] = obj[k];
       }
    
       // ES6 浅拷贝方法1
       // Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,不包括原型链,复制到目标对象(target)
       let m = {};
       Object.assign(m, obj);
    
       // ES6 浅拷贝方法2
       let n = { ...obj };
    
        // ES5深拷贝方法1
        function deepCopy(oldObj, newObj){
            for(let k in oldObj){
                const value = oldObj[k]
                if(Array.isArray(value)){
                    newObj[k] = []
                    deepCopy(value, newObj[k])
                }else if(typeof value === 'object'){
                    newObj[k] = {}
                    deepCopy(value, newObj[k])
                }else {
                    newObj[k] = value
                }
            }
        }
    
        // ES5深拷贝方法2
        const json = JSON.stringify(obj)
        const obj2 = JSON.parse(json)
    
    

    4.ES6中Symbol的应用

    • 作为对象的键,对象的键可以是字符串和symbol值
    • Symbol.iterator
    • Symbol.toPrimitive -> 对象转数字Number([val])   对象转字符串String([val])的第一部都是看看有没有这个属性,有的话执行该方法,看看获得的是原始值就转换成功了
    • 使用Symbol来替代常量

    Symbol.toPrimitive

        // 一个没有提供 Symbol.toPrimitive 属性的对象,参与运算时的输出结果
        const obj1 = {};
        console.log(+obj1);     // NaN
        console.log(`${obj1}`); // "[object Object]"
        console.log(obj1 + ""); // "[object Object]"
    
       // 手动赋予了 Symbol.toPrimitive 属性,再来查看输出结果
        const object1 = {
            [Symbol.toPrimitive](hint) {
                console.log(hint);
                if (hint === 'number') {
                    return 42;
                }else if(hint === 'string'){
                    return 'hi';
                }
                return null;
            }
        };
    
        // 左边一个+号就表示执行Number([val])
        console.log(+object1);     // 10      -- hint 参数值是 "number"
        console.log(`${object1}`); // "hi"    -- hint 参数值是 "string"
        console.log(object1 + ""); // null    -- hint 参数值是 "default"
    

    使用Symbol来替代常量

    const tabTypes = {
        basic: Symbol(),
        super: Symbol(),
    }
     
    if (type === tabTypes.basic) {
        return <div>basic tab</div>
    }
     
    if (type === tabTypes.super) {
        return <div>super tab</div>
    }
    

    5.对象的遍历方法

    • for in:遍历对象的key,所有可枚举,包含原型链
    • Object.keys():返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
    • Object.values():返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
    • Object.entries():返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
        // 会过滤属性名为 Symbol 值的属性
        const obj1 = { [Symbol()]: 123, foo: 'abc' }
        for(let k in obj1){
            console.log(k); // 'foo'
        }
        console.log(Object.keys(obj1)); // ['foo']
    

    6.比较两个值是否相等

    • == / === / Object.is()
    • ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
    • ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

    7.手写防抖和节流

    • 占位

    8.commonJS和es6 module的区别

    • 占位

    9.进程和线程

    • 进程:计算机已经运行的程序
    • 线程:操作系统能够运行运算调度的最小单位
    • 说人话:运行一个程序,就默认启动一个进程(可以多进程),每个进程都会启动一个线程来执行程序的代码,这个就叫主线程,所以进程是线程的容器
    • 浏览器多数是多进程的,每开启一个页面就开一个新的进程,避免页面卡死造成全部页面无法响应,需要退出浏览器,而每个进程有多个线程,其中一个线程来负责执行js代码
    • js是单线程的,setTimeout/ajax/dom监听/ui redering是调用浏览器内置api(web api),等浏览器执行完毕后,会将回调函数放入宏任务事件队列中

    10.promise 和 async/await

    • async、await是Promise的一个语法糖
    • async修饰函数,会根据函数的返回值把它包装成Promise对象,返回出去,这样调用函数之后就可以链式调用了
    • 我们可以将await关键字后面执行的代码,看做是包裹在(resolve, reject) => {函数执行}中的代码,这个在源码中叫做excutor函数,是同步函数,立即执行的,目的是将promise实例的resovle方法和reject方法传递给用户,传递的时候用bind显示绑定了this为实例对象
    • await的下一条语句,可以看做是then(res => {函数执行})中的代码,也就是成功的回调,如果状态是rejected则执行不到这里,需要catch里面处理(在源码中then的unfufilled方法和unrejected方法为微任务)
    • 要会手写Promise.all(),Promise源码的then()这个方法要理解
      // 测试async,会将函数的返回值包装成Promise实例后返回
      const test =  async () => {
        // return Promise.resolve('success')
        return Promise.reject('fail')
        // return 123
      }
    
      test().then(value => {
        console.log(value);}, // success/123
      reason => {
        console.log(reason); // fail
      })
    
      Promise.myAll =  args => {
        const result = []
        let fullCount = 0
        let iteratorIndex = 0
    
        const promise2 = new Promise((resolve, reject) => {
          for(let item of args){
            iteratorIndex++
    
            // 包装一层,避免item不是promise实例
            Promise.resolve(item).then(value => {
              result.push(value)
              fullCount++
    
              // 对于数组是length,map之类是size,所以得自己定义一个iteratorIndex
              if(fullCount === iteratorIndex){
                resolve(result)
              }
            }, reason => {
              reject(reason)
            })
          }
    
          if(iteratorIndex === 0){
            resolve(result)
          }
        })
    
        return promise2
      }
    

    11.宏任务和微任务

    事件循环中维护着以下两个队列:

    • 宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
    • 微任务队列(microtask queue):Promise的then回调(new Promise传入的函数为excutor函数,是在主线程立即执行的,建议看下源码)、 Mutation Observer API、queueMicrotask()等
    • 宏任务执行之前,必须保证微任务队列是空的,如果不为空,那么优先执行微任务队列中的任务(回调)

    12.事件循环

    • 浏览器的事件循环是一个我们编写的JavaScript代码和浏览器API调用(setTimeout/AJAX/监听事件等)的一个桥梁, 桥梁之间他们通过回调函数进行沟通。
    • 具体参考coderwhy大神的这篇文章https://juejin.cn/post/6978019623316750349,把今日头条面试题做出来就说明你理解了大部分

    13.forEach和map的区别

    • forEach无返回值
    • map返回一个新的数组
    • 当数组的元素的基本类型数据,则在循环中改变item并不会影响源数组,如果元素是引用类型数据,则循环中改变元素,会改变源数组

    14.set和map的区别

    • set存储值,或者说键值相同,不能重复,没有get方法
    • map存储键值对,有get方法

    相关文章

      网友评论

          本文标题:js面试题

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