JavaScript高频手写面试题

对于Web开发人员来说,理解和掌握JavaScript是非常重要的,JavaScript 开发者常常会遇到一系列手写代码的挑战,旨在测试开发者的语言掌握程度、编程技巧以及解决问题的能力。本文将介绍一些常见的JavaScript手写题,以及它们的简要解析。

一、实现一个函数计算斐波那契数列

 1、用for循环来实现,时间复杂度为O(n),空间复杂度为O(1)

function fibonacci2(n) {  
  if (n <= 1) return n;
  let a = 0;  
  let b = 1;  
  for (let i = 2; i <= n; i++) {  
    let temp = a + b;  
    a = b;  
    b = temp;  
  }  
  return b;  
}

2、优雅一点的写法

const cache = [0, 1];

function fabonacci(n) {
  return typeof cache[n] === 'number' ? cache[n] : cache[n] = fabonacci(n - 1) + fabonacci(n - 2);
}

二、实现数组的map方法

function myMap(arr, callback) {
  // 检查参数是否正确
  if (!Array.isArray(arr) || !arr.length || typeof callback !== 'function') {
      return [];
  } else {
      let result = [];
      for (let i = 0, len = arr.length; i < len; i++) {
          result.push(callback(arr[i], i, arr))
      }
      return result
  }
}

三、实现数组的map方法

又叫质数,在大于1的自然数中,除了1和它本身以外不再有其他因数。要判断一个数是否是素数,最直观的方法就是将这个数从2开始,一直到该数的平方根的所有数中,分别进行取余运算。如果有任意一个数可以整除这个数字,那么它就不是素数。

function isPrime(num) {
  //判断数字是否大于1
  if (num <= 1) {
    return false;
  }
  //从2开始,一直到该数的平方根的所有数中,分别进行取余运算
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      return false;
    }
  }
  return true;
}

四、实现instanceof

如果 target 为基本数据类型直接返回 false;判断 Fn.prototype 是否在 target 的隐式原型链上。

const _instanceof = (target, Fn) => {
    if ((typeof target !== 'object' && typeof target !== 'function') || target === null)
        return false;

    let proto = target.__proto__
    while (true) {
        if (proto === null) return false
        if (proto === Fn.prototype) return true
        proto = proto.__proto__
    }
}

function A() {}
const a = new A()
console.log(_instanceof(a, A)) // true
console.log(_instanceof(1, A)) // false

五、实现call方法

var name = '公众号:前端技术营';
var obj = {
    name: '张三'
};

function fn(a, b, c) {
    console.log(`${a} ${b} ${c} ${this.name}`);
};
// 模拟call
Function.prototype.myCall = function(){
  let target = arguments[0]|| window || global
  target.fn = this
  let args = Array.from(arguments).slice(1)
  let result = target.fn(...args)
  delete target.fn
  return result 
}

fn.myCall(obj, 'my', 'name', 'is'); // my name is 张三
fn.myCall(null, 'my', 'name', 'is'); // my name is 公众号:前端技术营
fn.myCall(undefined, 'my', 'name', 'is'); // my name is 公众号:前端技术营

六、实现apply方法

var name = '公众号:前端技术营';
var obj = {
    name: '张三'
};

function fn(a, b, c) {
    console.log(`${a} ${b} ${c} ${this.name}`);
};
// 模拟apply
Function.prototype.myApply = function(){
  let target = arguments[0]|| window || global;
  target.fn = this;
  let args = Array.from(arguments)[1];
  let result = target.fn(...args);
  delete target.fn;
  return result;
}

fn.myApply(obj, ['my', 'name', 'is']); // my name is 张三
fn.myApply(null, ['my', 'name', 'is']); // my name is 公众号:前端技术营
fn.myApply(undefined, ['my', 'name', 'is']); // my name is 公众号:前端技术营

七、实现bind方法

var name = '公众号:前端技术营';
var obj = {
    name: '张三'
};

function fn(a, b, c) {
    console.log(`${a} ${b} ${c} ${this.name}`);
};
// 模拟call
Function.prototype.myBind = function(context) {
    const _this = this
    const args = [...arguments].slice(1);
    // 返回一个函数
    return function F() {
        // 因为返回了一个函数,我们可以 new F(),所以需要判断
        if (this instanceof F) {
            // 忽略传入的this
            return new _this(...args, ...arguments)
        }
        // 直接调用,将两边的参数拼接起来

        return _this.apply(context, args.concat(...arguments))
    }
}

const fn1 = fn.myBind(obj, 'my', 'name', 'is');
fn1(); // my name is 张三

const fn2 = fn.myBind(null, 'my', 'name', 'is');
fn2(); // my name is 公众号:前端技术营

const fn3 = fn.myBind(undefined, 'my', 'name', 'is');
fn3(); // my name is 公众号:前端技术营

八、实现new关键字

function myNew() {
    // 创建一个空对象
    let obj = new Object();
    // shift删除数组第一个元素,并返回一个元素。原有arguments数组第一个参数是构造函数,返回值为构造函数。
    const Constructor = [].shift.call(arguments);
    // 将 obj 的原型指向构造函数,这样 obj 就可以访问到构造函数原型中的属性
    obj.__proto__ = Constructor.prototype;
    // 执行构造函数的方法
    const ret = Constructor.apply(obj, arguments);
    // 返回构造函数中有返回值且返回值是个引用类型,没有返回值则默认返回这个对象
    return typeof ret === 'object' ? ret : obj;
}

function Person(name, age) {
    this.name = name;
    this.age = age
    // 模拟有返回值且是个对象
    return {
        name: name,
        address: '上海市浦东新区'
    };
}

const a = myNew(Person, '公众号:前端技术营', 18);
console.log(a.name); // 公众号:前端技术营
console.log(a.address); // 上海市浦东新区
console.log(a.age); // undefined

九、实现函数柯里化

函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

function curry(fn, args) {
  // 获取函数需要的参数长度
  let length = fn.length;

  args = args || [];

  return function() {
    let subArgs = args.slice(0);

    // 拼接得到现有的所有参数
    for (let i = 0; i < arguments.length; i++) {
      subArgs.push(arguments[i]);
    }

    // 判断参数的长度是否已经满足函数所需参数的长度
    if (subArgs.length >= length) {
      // 如果满足,执行函数
      return fn.apply(this, subArgs);
    } else {
      // 如果不满足,递归返回科里化的函数,等待参数的传入
      return curry.call(this, fn, subArgs);
    }
  };
}

// es6 实现
function curry(fn, ...args) {
  return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
}

十、实现object.create()方法

function objectCreate(obj){
  function F(){};  // 构造函数
  F.prototype = obj; // obj 作为构造函数原型的属性
  return new F(); // 返回一个实例对象 就像 `let ff = new F();  return ff`
}

十一、数组去重

// 1.利用Set
const res = Array.from(new Set(arr));
 
// 2.两层for循环+splice
const unique = arr => {
  let len = arr.length;
  for (let i = 0; i < len; i++) {
    for (let j = i + 1; j < len; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        len--;
        j--;
      }
    }
  }
  return arr;
}
 
// 3.利用indexOf
const unique = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
  }
  return res;
}
 
// 4.利用include
const unique = arr => {
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i]);
  }
  return res;
}
 
// 5.利用filter
const unique = arr => {
  return arr.filter((item, index) => {
    return arr.indexOf(item) === index;
  });
}
 
// 6.利用Map
const unique = arr => {
  const map = new Map();
  const res = [];
  for (let i = 0; i < arr.length; i++) {
    if (!map.has(arr[i])) {
      map.set(arr[i], true);
      res.push(arr[i]);
    }
  }
  return res;
}

十二、数组求和

// 1.利用for循环
function getSum(arr){    
  let sum = 0;
  for(var i = 0;i<arr.length;i++){
      sum += arr[i]; 
  }
  return sum ;
}

// 2.利用reduce
const sum = arr.reduce(function (prev, cur) {
    return prev + cur;
}, 0);

// 3.利用forEach
function getSum(arr) {
  let sum = 0;
  arr.forEach((val) => {
    sum += val;
  });
  
  return sum;
};

// 4.使用递归实现数组求和
function sum(arr) {
  if (arr.length === 0) {
    return 0;
  }
  return arr[0] + sum(arr.slice(1));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_35430208

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值