高频js笔试题看这一篇就够了

这篇博客涵盖了JavaScript的多个核心知识点,包括二叉树层次遍历、类的继承(简版与详细实现)、apply方法、双向数据绑定、大整数相加算法、字符串最频繁字符查找、报数问题、数组的flat方法、发布-订阅模式、Object.is、深拷贝(简版与完整实现)等。此外,还涉及了不同方式的异步编程实现(callback、promise、async/await)、Function.prototype.apply()、数组的filter方法、观察者模式、迭代器生成函数以及Object.freeze的实现。
摘要由CSDN通过智能技术生成

二叉树层次遍历

// 二叉树层次遍历

class Node {
   
  constructor(element, parent) {
   
    this.parent = parent // 父节点 
    this.element = element // 当前存储内容
    this.left = null // 左子树
    this.right = null // 右子树
  }
}

class BST {
   
  constructor(compare) {
   
    this.root = null // 树根
    this.size = 0 // 树中的节点个数

    this.compare = compare || this.compare
  }
  compare(a,b) {
   
    return a - b
  }
  add(element) {
   
    if(this.root === null) {
   
      this.root = new Node(element, null)
      this.size++
      return
    }
    // 获取根节点 用当前添加的进行判断 放左边还是放右边
    let currentNode = this.root 
    let compare
    let parent = null 
    while (currentNode) {
   
      compare = this.compare(element, currentNode.element)
      parent = currentNode // 先将父亲保存起来
      // currentNode要不停的变化
      if(compare > 0) {
   
        currentNode = currentNode.right
      } else if(compare < 0) {
   
        currentNode = currentNode.left
      } else {
   
        currentNode.element = element // 相等时 先覆盖后续处理
      }
    }

    let newNode = new Node(element, parent)
    if(compare > 0) {
   
      parent.right = newNode
    } else if(compare < 0) {
   
      parent.left = newNode
    }

    this.size++
  }
  // 层次遍历 队列
  levelOrderTraversal(visitor) {
   
    if(this.root == null) {
   
      return
    }
    let stack = [this.root]
    let index = 0 // 指针 指向0
    let currentNode 
    while (currentNode = stack[index++]) {
   
      // 反转二叉树
      let tmp = currentNode.left
      currentNode.left = currentNode.right
      currentNode.right = tmp
      visitor.visit(currentNode.element)
      if(currentNode.left) {
   
        stack.push(currentNode.left)
      }
      if(currentNode.right) {
   
        stack.push(currentNode.right)
      }
    }
  }
}
// 测试
var bst = new BST((a,b)=>a.age-b.age) // 模拟sort方法

// ![](http://img-repo.poetries.top/images/20210522203619.png)
// ![](http://img-repo.poetries.top/images/20210522211809.png)
bst.add({
   age: 10})
bst.add({
   age: 8})
bst.add({
   age:19})
bst.add({
   age:6})
bst.add({
   age: 15})
bst.add({
   age: 22})
bst.add({
   age: 20})

// 使用访问者模式
class Visitor {
   
  constructor() {
   
    this.visit = function (elem) {
   
      elem.age = elem.age*2
    }
  }
}

// ![](http://img-repo.poetries.top/images/20210523095515.png)
console.log(bst.levelOrderTraversal(new Visitor()))

实现类的继承

类的继承在几年前是重点内容,有n种继承方式各有优劣,es6普及后越来越不重要,那么多种写法有点『回字有四样写法』的意思,如果还想深入理解的去看红宝书即可,我们目前只实现一种最理想的继承方式。

function Parent(name) {
   
    this.parent = name
}
Parent.prototype.say = function() {
   
    console.log(`${
     this.parent}: 你打篮球的样子像kunkun`)
}
function Child(name, parent) {
   
    // 将父类的构造函数绑定在子类上
    Parent.call(this, parent)
    this.child = name
}

/**  1. 这一步不用Child.prototype =Parent.prototype的原因是怕共享内存,修改父类原型对象就会影响子类 2. 不用Child.prototype = new Parent()的原因是会调用2次父类的构造方法(另一次是call),会存在一份多余的父类实例属性3. Object.create是创建了父类原型的副本,与父类原型完全隔离*/
Child.prototype = Object.create(Parent.prototype);
Child.prototype.say = function() {
   
    console.log(`${
     this.parent}好,我是练习时长两年半的${
     this.child}`);
}

// 注意记得把子类的构造指向子类本身
Child.prototype.constructor = Child;

var parent = new Parent('father');
parent.say() // father: 你打篮球的样子像kunkun

var child = new Child('cxk', 'father');
child.say() // father好,我是练习时长两年半的cxk

实现apply方法

apply原理与call很相似,不多赘述

// 模拟 apply
Function.prototype.myapply = function(context, arr) {
   
  var context = Object(context) || window;
  context.fn = this;

  var result;
  if (!arr) {
   
    result = context.fn();
  } else {
   
    var args = [];
    for (var i = 0, len = arr.length; i < len; i++) {
   
      args.push("arr[" + i + "]");
    }
    result = eval("context.fn(" + args + ")");
  }

  delete context.fn;
  return result;
};

实现双向数据绑定

let obj = {
   }
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
   
  configurable: true,
  enumerable: true,
  get() {
   
    console.log('获取数据了')
  },
  set(newVal) {
   
    console.log('数据更新了')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// 输入监听
input.addEventListener('keyup', function(e) {
   
  obj.text = e.target.value
})

参考:前端手写面试题详细解答

实现非负大整数相加

JavaScript对数值有范围的限制,限制如下:

Number.MAX_VALUE // 1.7976931348623157e+308
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MIN_VALUE // 5e-324
Number.MIN_SAFE_INTEGER // -9007199254740991

如果想要对一个超大的整数(> Number.MAX_SAFE_INTEGER)进行加法运算,但是又想输出一般形式,那么使用 + 是无法达到的,一旦数字超过 Number.MAX_SAFE_INTEGER 数字会被立即转换为科学计数法,并且数字精度相比以前将会有误差。

实现一个算法进行大数的相加:

function sumBigNumber(a, b) {
   
  let res = '';
  let temp = 0;

  a = a.split('');
  b = b.split('');

  while (a.length || b.length || temp) {
   
    temp += ~~a.pop() + ~~b.pop();
    res = (temp % 10) + res;
    temp  = temp > 9
  }
  return res.replace(/^0+/, '');
}

其主要的思路如下:

  • 首先用字符串的方式来保存大数,这样数字在数学表示上就不会发生变化
  • 初始化res,temp来保存中间的计算结果,并将两个字符串转化为数组,以便进行每一位的加法运算
  • 将两个数组的对应的位进行相加,两个数相加的结果可能大于10,所以可能要仅为,对10进行取余操作,将结果保存在当前位
  • 判断当前位是否大于9,也就是是否会进位,若是则将temp赋值为true,因为在加法运算中,true会自动隐式转化为1,以便于下一次相加
  • 重复上述操作,直至计算结束

查找字符串中出现最多的字符和个数

例: abbcccddddd -> 字符最多的是d,出现了5次

let str = "abcabcabcbbccccc";
let num = 0;
let char = '';

 // 使其按照一定的次序排列
str = str.split('').sort().join('');
// "aaabbbbbcccccccc"

// 定义正则表达式
let re = /(\w)\1+/g;
str.replace(re,($0,$1) => 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值