面试手写JS-two

198 阅读3分钟

更多详细代码见:Github

面试手写JS - 掘金 (juejin.cn)

手写深拷贝

  • 我首先想到的就是
JSON.parse(JSON.stringify());

但是他有一些问题

  1. 无法解决循环引用的问题
  2. 无法拷贝一些特殊的对象,诸如 RegExp, Date, Set, Map等
  3. 无法拷贝函数(划重点)。

面试够用版本

function deepClone(obj){
  if(typeof obj !== 'object' || obj === null){
    return obj
  }
  let copy = {}
  if (obj.constructor === Array){
    copy = []
  }
  for( let key of obj){
    if(obj.hasOwnProperty(key)){
      obj[key] = deepClone(obj[key])
    }
  }
  return copy
}

手写new

过程

  • 创建了一个全新的对象
  • 将创建的新对象的proto 指向 构造函数的原型prototype
  • 执行构造函数,并通过call,apply改变this的指向
  • 确保返回的是一个对象。返回值为Object类型则作为new方法的返回值返回。否则返回上述全新的对象
function myNew(fn, ...args) {
    let instance = Object.create(fn.prototype);
    let res = fn.apply(instance, args); // 改变this指向

    // 确保返回的是一个对象(万一fn不是构造函数)
    return typeof res === 'object' ? res: instance;
}

实现一个call

  • 将函数作为对象的参数
  • 执行&删除这个函数
  • 指定this到函数并传入给定参数执行函数
  • 如果不传入参数,默认指向window
Function.prototype.myCall = function(context = window, ...args){
  let key = Symbol('key')
  context[key] = this
  let result = context[key](...args)
  delete context[key]
  return result
}
function f(a,b){
  console.log(a+b)
  console.log(this.name)
 }
 let obj={
  name:1
 }
 f.myCall(obj,1,2) //否则this指向window
 // 理解了,相当于this函数是在context的内部执行的。所以this自然而然地就继承了

实现一个apply

  • 传入的参数为数组
Function.prototype.myApply = function(context = window , ...args){
  let key = Symbol('key')
  context[key] = this
  let result = context[key](args)
  delete context[key]
  return result
}
function f(a,b){
  console.log(a,b)
  console.log(this.name)
 }
 let obj={
  name:'张三'
 }
 f.myApply(obj,[1,2])  //arguments[1]

实现一个bind

  • bind返回了一个函数。可以有两种调用地方式。一种是直接调用。一种是通过 new 的方式,我们先来说直接调用的方式

  • 对于直接调用来说,这里选择了 apply 的方式实现,但是对于参数需要注意以下情况:因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来

  • 最后来说通过 new 的方式,对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this

Function.prototype.myBind = function (context, ...outerArgs) {
  let self = this
  return function F(...innerArgs){
    if(self instanceof F){
      return new self(...outerArgs,...innerArgs)
    }
    return self.apply(context,[...outerArgs,...innerArgs])
  }
}
function f(a,b){
  console.log(a+b)
  console.log(this.name)
 }
 let obj={
  name:1
 }
//  f.myCall(obj,1,2) 
 f.myBind(obj, 1)(2)

Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto

function create(proto) {
  function F() {}
  F.prototype = proto;

  return new F();
}

实现继承的那些事

最推荐

function Parent5 () {
  this.name = 'parent5';
  this.play = [1, 2, 3];
}

function Child5(){
  Parent5.call(this)
  this.type = 'woi'
}
Child5.prototype = Object.create(Parent5.prototype)
Child5.prototype.constructor = Child5
console.log(new Child5())

JSON.parse JSON.stringfy

正则匹配

必需 (总是学不会)

对象的扁平化

代码见Github仓库

重点 add(1)(2)(3)

网上也找了一些代码示例,写得什么玩意呀

结果正确版本

// console.log(add(1)(2)(3)(4, 5)())
function addCurry(...args){return args.reduce((a,b)=> {
    return a+b
  }
)}
function currying(fn){
	let args=[]
	return function temp(...newArgs){
		if(newArgs.length){
			args = [...args, ...newArgs]
			return temp
		} else {
			let val = fn.apply(this, args)
			args = []
			return val
		}
	}
}
let add = currying(addCurry)
console.log(add(1)(2)(3)(4, 5)())
console.log(add(1)(2)(3, 4, 5)())
console.log(add(1)(2, 3, 4, 5)())

但还是有一点问题 最后的 () 没有去掉。 有大哥能解决吗?

实现一个JS函数得柯里化

柯里化的定义:接收一部分参数,返回一个函数接收剩余参数,接收足够参数后,执行原函数

核心思路:通过函数的 length 属性,获取函数的形参个数,形参的个数就是所需的参数个数

function curry(fn,myargs){
  // console.log(fn)
  // console.log(fn.length)
  var length = fn.length
  var myargs = myargs || []
  return function(...args){
    newArgs = myargs.concat(args)
    console.log(newArgs)
    if (newArgs.length < length){
      return curry.call(this,fn,newArgs)
    }else{
      return fn.apply(this,newArgs)
    }
  }
}

function multiFn(a, b, c) {
  return a * b * c;
}
var multi = curry(multiFn);

console.log(multi(2)(3)(4))
// multi(2,3,4);
// multi(2)(3,4);
// multi(2,3)(4)

快速排序

核心就是利用递归得思想。从数组中取一个值,遍历后大的放值的右栈,小的放在左栈。而后对左右栈执行相同的操作

function quickSort(args){
  let length = args.length
  if (length<=1) {
    return args
  }
  let mid = Math.floor(length/2)
  let midVal = args[mid]
  let left = []
  let right = []
  for(let i = 0 ; i < length ; i++){
    if( i === mid ){
      continue
    }
    if (args[i] <= midVal){
      left.push(args[i])
    }else{
      right.push(args[i])
    }
  }
  return quickSort(left).concat([midVal]).concat(quickSort(right))
}
console.log(quickSort([2,3,1,6]))