10个面试高频JavaScript手写知识点,附带详细注解

这篇博客汇总了10道常见的JavaScript面试题,涉及DOM转换、自定义setTimeout/setInterval、条件表达式、AJAX实现、instanceof操作、JSON.parse、LRU缓存以及new关键字的模拟实现。通过这些题目,可以深入理解JavaScript的基础特性和高级用法。
摘要由CSDN通过智能技术生成

10个面试高频手写JS源码

1、树结构转DOM树

树结构如下:

let obj = {
    tag: 'DIV',
    children: [
        { tag: 'SPAN', children: [] },
        {
            tag: 'UL',
            children: [
                { tag: 'LI', children: [] },
                { tag: 'LI', children: [] }
            ]
        }
    ]
}

实现代码:

function Tree2DOM(vnode){
    // 如果是数字类型转化为字符串
    if(typeof vnode === 'number'){
        vnode = String(vnode)
    }
    // 字符串类型直接就是文本节点
    if(typeof vnode === 'string'){
        return document.createTextNode(vnode)
    }
    // 根据它的节点名创建节点
    const dom = document.createElement(vnode.tag)
    // 遍历当前节点的子节点,并添加到它的子节点中,appendChild
    vnode.children.forEach(child=>{dom.appendChild(Tree2DOM(child))})
    return dom
}

结果如图:

image-20220323152602801

2、DOM树转树结构

<div>
    <span></span>
    <ul>
        <li></li>
        <li></li>
    </ul>
</div>

// 将上方的DOM转化为下面的树结构对象

{
    tag: 'DIV',
    children: [
        { tag: 'SPAN', children: [] },
        {
            tag: 'UL',
            children: [
                { tag: 'LI', children: [] },
                { tag: 'LI', children: [] }
            ]
        }
    ]
}

实现代码:

function DOM2tree(dom){
    let obj = {}
    obj.tag = dom.tagName
    obj.children = []
    // 使用children 不使用childNodes 因为childNodes会把所有节点,包括空白节点也遍历
    for(let child of dom.children){
        obj.children.push(DOM2tree(child))
    }
    return obj
}

image-20220323153058413

3、setTimeout 实现 setInterval

function mySetTimeout(fn,delay){
    let timer = null
    const interval = ()=>{
        fn()
        // 利用闭包更新最新的timerID,并且递归调用达到循环的目的
        timer = setTimeout(interval,delay)
    }
    setTimeout(interval,delay)
    return {
        // 传出去一个最新的清楚定时器id的方法
        cancel(){
            clearTimeout(timer)
        }
    }
}

测试代码:

// 使用
const {cancel} = mySetTimeout(()=>{console.log('正在循环');},1000)
setTimeout(()=>{
    cancel()
    //4秒后停止循环
},4000)

4、setInterval 实现 setTimout

function mySetInterval(fn,delay){
      let timer = setInterval(()=>{
        fn()
        clearInterval(timer)  //  执行完后清除掉自身
      },delay)
    }
mySetInterval(()=>{console.log('定时执行');},1000)
mySetInterval(()=>{console.log('定时执行');},1000)

5、a == 1 && a == 2 && a == 3

//第一种方法
let a = {
    i:1,
    toString(){
        return this.i++
    }
}
console.log(a==1&&a==2&&a==3);
// 第二种方法
a = [1,2,3]
a.join = a.shift
console.log(a==1&&a==2&&a==3);
// 第三种方法
let obj = {
    val:1
}
Object.defineProperty(obj,'a',{
    get(){
        return this.val++
    }
})
console.log(obj.a==1&&obj.a==2&&obj.a==3);

6、AJAX原生实现

const ajax = {
      // get请求,输出路径,跟回调函数
      get(url,fn){
        const xhr = new XMLHttpRequest()
        // 第三个参数设置是否异步
        xhr.open('GET',url,true)
        // 发送事件
        xhr.send()
        //事件绑定 处理服务器端返回的结果
        xhr.onreadystatechange = function(){
          if(xhr.readyState === 4){
              // 当返回的为成功时,回调函数
              fn(xhr.response)
          }
        }
      },
      post(url,fn){
        const xhr = new XMLHttpRequest()
        xhr.open("POST",url,true)
        // 设置响应头
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
        xhr.send() // 发送请求体
        // 事件绑定
        xhr.onreadystatechange = function(){
          if(xhr.readyState === 4){
            fn(xhr.response)
          }
        }
      }
    }
    // 测试代码
    ajax.post('http://127.0.0.1:8000/server',function(res){
      console.log(res);  // HELLO,AJAX!  POST
    })
    ajax.get('http://127.0.0.1:8000/server',function(res){
      console.log(res); // {"id":1,"name":"dasda"}
    })
    // 服务器代码
    // app.get('/server',(req,res)=>{
    //   //设置响应头--设置允许跨域
    //   res.setHeader('Access-Control-Allow-Origin','*');
    //   const person = {id:1,name:'dasda'}
    //   res.send(person)
    // })
    // app.post('/server',(request,response)=>{
    //   //设置响应头--设置允许跨域
    //   response.setHeader('Access-Control-Allow-Origin','*');
    //   //设置响应
    //   response.send('HELLO,AJAX!  POST');
    // });

7、instance of 关键字

function MyInstanceOf(son,father){
    // 目标对象的原型对象
    let fp = father?.prototype
    // 自身的原型链
    let sp = son?.__proto__
    // 判断自身的原型是否在目标对象的原型链上
    while(sp){
        if(sp == fp){
            return true
        }
        // 向上找原型
        sp = sp.__proto__
    }
    // 不在原型链上返回false
    return false
}
console.log(MyInstanceOf([],Object));  // true
console.log([] instanceof Object);  // true

8、JSON.parse

function parse(obj){
    // return eval("(" + obj + ")")  // 不安全
    return  (new Function("'use strict'; return " + obj ))()  // 推荐使用第二个 安全
    // return (function(obj){return obj}(obj))  函数声明做不到反序列
}
let xxx = JSON.stringify({name:18})
console.log(parse(xxx));

9、LRU缓存

/*
      请你设计并实现一个满足  LRU (最近最少使用) 缓存 约束的数据结构。
      实现 LRUCache 类:
      LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
      int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
      void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
      函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
    */
    var LRUCache = function(capacity) {
    this.size = capacity
    this.map = new Map()
    };
    LRUCache.prototype.get = function(key) {
        if(this.map.has(key)){
            let res = this.map.get(key)
            this.map.delete(key)
            this.map.set(key,res)
            return res
        }else{
            return -1
        }
    };
    LRUCache.prototype.put = function(key, value) {
        if(this.map.has(key)){
            this.map.delete(key)
            this.map.set(key,value)
        }else{
            if(this.map.size >= this.size){
                this.map.delete(this.map.keys().next().value)
            }
            this.map.set(key,value)
        }
    };

10、new 关键字实现

new 关键字会进行如下的操作:

  1. 创建一个空的简单JavaScript对象(即{});
  2. 为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this
function MyNew(fn,...args){
    // 创建一个空对象
    const obj = {}
    // 将新对象的__proto__指向它构造函数的原型
    Object.setPrototypeOf(obj,fn.prototype)
    // 调用构造函数,传参数初始化,并将this改为obj
    const result = fn.apply(obj,args)
    // 判断构造函数有没有返回值,1.返回的是基础类型,则返回实例对象,2.返回函数则返回函数,3,返回对象则返回它指定的对象
    return result instanceof Object? result:obj
}
function Person(name,age){
    this.name = name
    this.age = age
    return 22 // 实例对象
    // return function(){} // 函数
    // return {} // 指定对象
}
let p1 = new Person('小米',18)  // Person {name: "小米", age: 18}
let p2 = MyNew(Person,'孙悟空',29) // Person {name: "孙悟空", age: 29}
console.log(p1);
console.log(p2);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值