前端常见功能的手写

47 阅读2分钟

实现数组的 push、filter、map 方法

js
复制代码
// push
Array.prototype.myPush = function (...args) {
  const length = this.length
  for (let i = 0; i < args.length; i++) {
    this[this.length + i] = args[i]
  }
  return this.length
}

// filter
Array.prototype.myFilter = function (callback) {
  const newArr = []
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i], i, this)) {
      newArr.push(this[i])
    }
  }
  return newArr
}

// map
Array.prototype.myMap = function (callback) {
  const newArr = []
  for (let i = 0; i < this.length; i++) {
    newArr.push(callback(this[i], i, this))
  }
  return newArr
}

const list = [1, 2, 3, 4, 5]

console.log(list.myPush(6)) // 6
console.log(list.myFilter((i) => i > 3)) //[ 4, 5, 6 ]
console.log(list.myMap((i) => i + 1)) // [ 2, 3, 4, 5, 6, 7 ]

js 实现红黄绿循环打印

  • 用 promise 实现
js
复制代码
const task = (timer, light) =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      if (light === 'red') {
        red()
      } else if (light === 'green') {
        green()
      } else if (light === 'yellow') {
        yellow()
      }
      resolve()
    }, timer)
  })
const step = () => {
  task(1000, 'red')
    .then(() => task(2000, 'green'))
    .then(() => task(3000, 'yellow'))
    .then(step)
}
step()
  • 用 async/await 实现
js
复制代码
const task = (light, timeout) => {
  return new Promise((resolve) => {
    setTimeout(() => resolve(console.log(light)), timeout)
  })
}

const taskRunner = async () => {
  await task('red', 1000)
  await task('green', 2000)
  await task('yellow', 3000)
  taskRunner()
}

taskRunner()

手写 new

答案

function funcNew(obj, ...args) {
    const newObj = Object.create(obj.prototype);
    const result = obj.apply(newObj, args);
    return (typeof result === 'object' && result !== null) ? result : newObj;
}

解读

先通过一个例子来理解 new 的作用吧:


function Person(name) {
    this.name = name;
}

Person.prototype.sayName = function() {
    console.log(this.name);
} 

const p = new Person('orange')
console.log(p.name) // 'orange'
p.sayName(); // 'orange'

代码中我们新建了一个对象 Person,它具有属性 name,且在 Person.prototype 上定义了函数 sayName

当我们通过 new 创建一个新的实例 p 时,便同时具有了属性 p.namep.sayName(),关系如下图:

image.png

知道了原理,就可以自己实现了。也就是说,自己写一个函数 funcNew(),使得 const p = new Person('orange')const p = funcNew('orange') 得到的 p 完全相同,于是得到了答案中的代码。

答案中最后一行代码如何理解?

前面的例子我们只考虑了 Person 中没有返回值的情况,如有有返回值,new 一个实例将会受到 Person 中返回值的影响。比如说:


/**
 * --- Person 中 return 一个对象,p 为该对象 ---
 */

function Person(name) {
    this.name = name;
    return {age: 35}
}

Person.prototype.sayName = function() {
    console.log(this.name);
} 

const p = new Person('orange')
console.log(p) // { age: 35 }
console.log(p.name) // undefined
p.sayName(); // TypeError: p.sayName is not a function

/**
 * --- Person 返回非对象,return 不影响结果 ---
 */

function Person(name) {
    this.name = name;
    return 'free'
}

Person.prototype.sayName = function() {
    console.log(this.name);
} 

const p = new Person('orange')
console.log(p) // Person { name: 'orange' }
console.log(p.name) // orange
p.sayName(); // orange

上面的例子中,如果返回了一个对象,我们需要返回该对象;如果不是对象,则 return 没用,正常处理。

深拷贝

// deepClone
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null 或者不是对象和数组,直接返回
        return obj;
    }
    let res;
    if (obj instanceof Array) {
        res = [];
    } else {
        res = {};
    }

    for (let key in obj) {
        // 判断自身中是否包含自身属性
        if (obj.hasOwnProperty(key)) {
            res[key] = deepClone(obj[key])
        }
    }
    return res;
}
// 验证
o = {a: 1, d: {c: '4'}};
res = deepClone(o);
console.log(res);
console.log(res == o);