likes
comments
collection
share

js 常见手写题

作者站长头像
站长
· 阅读数 45
  • 手写call
Function.prototype.myCall = function(thisArg, ...args) {
    const fn = Symbol('fn')        // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
    thisArg = thisArg || window    // 若没有传入this, 默认绑定window对象
    thisArg[fn] = this              // this指向调用call的对象,即我们要改变this指向的函数
    const result = thisArg[fn](...args)  // 执行当前函数
    delete thisArg[fn]              // 删除我们声明的fn属性
    return result                  // 返回函数执行结果
}

//测试
foo.myCall(obj)  
  • 手写 apply
Function.prototype.myApply = function(thisArg, args) {
    const fn = Symbol('fn')        // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
    thisArg = thisArg || window    // 若没有传入this, 默认绑定window对象
    thisArg[fn] = this              // this指向调用call的对象,即我们要改变this指向的函数
    const result = thisArg[fn](...args)  // 执行当前函数
    delete thisArg[fn]              // 删除我们声明的fn属性
    return result                  // 返回函数执行结果
}

//测试
foo.myApply(obj, [])
  • 手写bind
Function.prototype.myBind = function (thisArg, ...args) {
    var self = this
    // new优先级
    var fbound = function () {
        self.apply(this instanceof self ? this : thisArg, args.concat(Array.prototype.slice.call(arguments)))
    }
    // 继承原型上的属性和方法
    fbound.prototype = Object.create(self.prototype);

    return fbound;
}

//测试
const obj = { name: '写代码像蔡徐抻' }
function foo() {
    console.log(this.name)
    console.log(arguments)
}

foo.myBind(obj, 'a', 'b', 'c')()
  • 防抖
function debounce(func, wait) {
    let timeout = null
    return function() {
        let context = this
        let args = arguments
        if (timeout) clearTimeout(timeout)
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, wait)
    }
}
  • 节流
function throttle(func, wait) {
    let timeout = null
    return function() {
        let context = this
        let args = arguments
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null
                func.apply(context, args)
            }, wait)
        }

    }
}


function throttle(func, wait) {
    var prev = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - prev > wait) {
            func.apply(context, args);
            prev = now;
        }
    }
}
  • 实现new
// new是关键字,这里我们用函数来模拟,new Foo(args) <=> myNew(Foo, args)
function myNew(foo, ...args) {
  // 创建新对象,并继承构造方法的prototype属性, 这一步是为了把obj挂原型链上, 相当于obj.__proto__ = Foo.prototype
  let obj = Object.create(foo.prototype)  
  
  // 执行构造方法, 并为其绑定新this, 这一步是为了让构造方法能进行this.name = name之类的操作, args是构造方法的入参, 因为这里用myNew模拟, 所以入参从myNew传入
  let result = foo.apply(obj, args)

  // 如果构造方法已经return了一个对象, 那么就返回该对象, 一般情况下,构造方法不会返回新实例,但使用者可以选择返回新实例来覆盖new创建的对象 否则返回myNew创建的新对象
  return typeof result === 'object' && result !== null ? result : obj
}

function Foo(name) {
  this.name = name
}
const newObj = myNew(Foo, 'zhangsan')
console.log(newObj)                 // Foo {name: "zhangsan"}
console.log(newObj instanceof Foo)  // true
  • 继承
function Parent(name) {
    this.name = [name]
}
Parent.prototype.getName = function() {
    return this.name
}
function Child() {
    // 构造函数继承
    Parent.call(this, 'zhangsan') 
}
//原型链继承
// Child.prototype = new Parent()
Child.prototype = Object.create(Parent.prototype)  //将`指向父类实例`改为`指向父类原型`
Child.prototype.constructor = Child

//测试
const child = new Child()
const parent = new Parent()
child.getName()                  // ['zhangsan']
parent.getName() 

jsonp

/**
     * 手写jsonp并返回Promise对象
     * 参数url,data:json对象,callback函数
     */
    function jsonp(url, data = {}, callback = 'callback') {
      // 处理json对象,拼接url
      data.callback = callback
      let params = []
      for (let key in data) {
        params.push(key + '=' + data[key])
      }
      console.log(params.join('&'))
      // 创建script元素
      let script = document.createElement('script')
      script.src = url + '?' + params.join('&')
      document.body.appendChild(script)
      // 返回promise
      return new Promise((resolve, reject) => {
        window[callback] = (data) => {
          try {
            resolve(data)
          } catch (e) {
            reject(e)
          } finally {
            // 移除script元素
            script.parentNode.removeChild(script)
            console.log(script)
          }
        }
      })

    }
    jsonp('http://photo.sina.cn/aj/index', {
      page: 1,
      cate: 'recommend'
    }, 'jsoncallback').then(data => {
      console.log(data)
    })

ajax promise

function getJson(url) {
	let promise = new Promise((resolve, reject) => {
  	let xhr = new XMLHttpRequest()
    xhr.open('GET', url, true)
    xhr.onreadystatechange = function() {
    	if (this.readyState !== 4) return
      if (this.status === 200) {
      	resolve(this.response)
      } else {
      	reject(new Error(this.statusText))
      }
    }
    
    xhr.onerror = function() {
    	reject(new Error(this.statusText))
    }
    
    xhr.responseType = 'json'
    xhr.setRequestHeader('Accept', 'application/json')
    xhr.send(null)
  })
  return promise
}

深拷贝

if(obj.constructor === Date) return new Date(obj);

if(obj.constructor === RegExp) return new RegExp(obj)

let obj ={
  a: 15,
  b: 2,
  c: {
    d:1,
    e:3,
    fn: function(){
      console.log(1111111111111)
    },
    date: new Date(),
    reg: new RegExp(/^\d+$/g)
  }
}

function deepClone(obj) {
  let map = new WeakMap()
  function dc(obj) {
    let res, exitObj
    if (obj.constructor === Date) {
      return new Date(obj)
    }else if (obj.constructor === RegExp) {
      return new RegExp(obj)
    }else if (Array.isArray(obj)) {
      res = []
    } else if (Object.prototype.toString.call(obj) === '[object Object]'){
      res = {}
    }else{
      return obj
    }
    exitObj = map.get(obj)
    if (exitObj) {
      return exitObj
    }
    map.set(obj, res)
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {  //symbol 换成Reflect.ownKeys
        if (obj[key] && typeof obj[key] === 'object'){
          res[key] = dc(obj[key])
        }else{
          res[key] = obj[key]
        }
      }
    }
    return res
  }
  return dc(obj)
}

let res = deepClone(obj)
			res.a = 5555
			console.log(obj)
			console.log(res)

深度优先遍历  和 广度优先遍历

  • 其实简单来说 深度优先就是自上而下的遍历搜索 广度优先则是逐层遍历, 如下图所示

深度优先

js 常见手写题

广度优先

js 常见手写题

对于算法来说 无非就是时间换空间 空间换时间

  1. 深度优先不需要记住所有的节点, 所以占用空间小, 而广度优先需要先记录所有的节点占用空间大
  2. 深度优先有回溯的操作(没有路走了需要回头)所以相对而言时间会长一点

深度优先采用的是堆栈的形式, 即先进后出

广度优先则采用的是队列的形式, 即先进先出

const data = [    {        name: 'a',        children: [            { name: 'b', children: [{ name: 'e' }] },
            { name: 'c', children: [{ name: 'f' }] },
            { name: 'd', children: [{ name: 'g' }] },
        ],
    },
    {
        name: 'a2',
        children: [
            { name: 'b2', children: [{ name: 'e2' }] },
            { name: 'c2', children: [{ name: 'f2' }] },
            { name: 'd2', children: [{ name: 'g2' }] },
        ],
    }
]

// 深度遍历, 使用递归
function getName(data) {
    const result = [];
    data.forEach(item => {
        const map = data => {
            result.push(data.name);
            data.children && data.children.forEach(child => map(child));
        }
        map(item);
    })
    return result.join(',');
}

// 广度遍历, 创建一个执行队列, 当队列为空的时候则结束
function getName2(data) {
    let result = [];
    let queue = data;
    while (queue.length > 0) {
        [...queue].forEach(child => {
            queue.shift();
            result.push(child.name);
            child.children && (queue.push(...child.children));
        });
    }
    return result.join(',');
}

console.log(getName(data))
console.log(getName2(data))

promise

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
    let reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
    let promise2 = new Promise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
      };
    });
    return promise2;
  }
  catch(fn){
    return this.then(null,fn);
  }
}
function resolvePromise(promise2, x, resolve, reject){
  if(x === promise2){
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  let called;
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') { 
        then.call(x, y => {
          if(called)return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          if(called)return;
          called = true;
          reject(err);
        })
      } else {
        resolve(x);
      }
    } catch (e) {
      if(called)return;
      called = true;
      reject(e); 
    }
  } else {
    resolve(x);
  }
}
//resolve方法
Promise.resolve = function(val){
  return new Promise((resolve,reject)=>{
    resolve(val)
  });
}
//reject方法
Promise.reject = function(val){
  return new Promise((resolve,reject)=>{
    reject(val)
  });
}
//race方法 
Promise.race = function(promises){
  return new Promise((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(resolve,reject)
    };
  })
}
//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
Promise.all = function(promises){
  let arr = [];
  let i = 0;
  function processData(index,data){
    arr[index] = data;
    i++;
    if(i == promises.length){
      resolve(arr);
    };
  };
  return new Promise((resolve,reject)=>{
    for(let i=0;i<promises.length;i++){
      promises[i].then(data=>{
        processData(i,data);
      },reject);
    };
  });
}

数组扁平化排序

let arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10]

arr.toString().split(',').sort((a,b)=>a-b).map(Number)
			
while(arr.some(item => Array.isArray(item))) {
  arr = [].concat(...arr)
}

数组转对象

const arr  = [
    {
        username:    'makai',
        displayname: '馆长',
        email:       'guanzhang@coffe1891.com'
    },
    {
        username:    'xiaoer',
        displayname: '小二',
        email:       'xiaoer@coffe1891.com'
    },
    {
        username:    'zhanggui',
        displayname: '掌柜',
        email:       null
    },
];

function callback(acc, person) {
    //下面这句用到了扩展运算符...acc,表示把acc对象的属性“肢解”开,和新的属性一起
    //以一个新的对象返回
    return {...acc, [person.username]: person};
}
const obj = arr.reduce(callback, {});//这里的初始值为{}
console.log(obj);

浮点数三位一个逗号

function format(number){
  if(number.indexOf('.') == -1) {
    return number.replace(/\B(?=(\d{3})+$)/g, ',')
  } else {
    return number.replace(/\B(?=(\d{3})+.)/g, ',')
  }
}

观察者模式

class Subject{
  constructor(arg) {
    this.observers = [] 
  }

  add(observer) {
    this.observers.push(observer)
  }
  notify(...args) {
    this.observers.forEach(item => item.update(...args))
  }
}
class Observer {
  update(...args) {
    console.log(...args)
  }
}
let sub = new Subject()
let observer1 = new Observer()
let observer2 = new Observer()
sub.add(observer1)
sub.add(observer2)
sub.notify('4564')

树状菜单

let arr = [
  { id: 1, name: '广东', pid: 0 },
  { id: 2, name: '广州', pid: 1 },
  { id: 3, name: '天河', pid: 2 },
  { id: 4, name: '白云', pid: 2 },
  { id: 5, name: '广西', pid: 0 },
  { id: 6, name: '玉林', pid: 5 },
  { id: 7, name: '北流', pid: 6 },
  { id: 8, name: '深圳', pid: 1 },
  { id: 9, name: '东莞', pid: 1 },
  { id: 10, name: '松山湖', pid: 9 },
]

function getMenu(data) {
  let ans = []
  let map = new WeakMap()
  if (data && data.length) {
    data.forEach(item => map[item.id] = item)
    data.forEach(menu => {
      let parent = map[menu.pid]
      if (parent) {
        parent.chidlren = parent.chidlren || []
        parent.chidlren.push(menu)
      } else {
        // 是顶级
        ans.push(menu)
      }
    })
  }
  return ans
}

console.log(getMenu(arr))

数组查找路径

const data = [  {    name: 'a',    children: [      { name: 'b', children: [{ name: 'e' }] },
      { name: 'c', children: [{ name: 'f' }] },
      { name: 'd', children: [{ name: 'g' }] },
    ],
  },
  {
    name: 'a2',
    children: [
      { name: 'b2', children: [{ name: 'e2' }] },
      { name: 'c2', children: [{ name: 'f2' }] },
      { name: 'd2', children: [{ name: 'g2' }] },
    ],
  }
]
function fn(data, name, path) {
  if (!path) path = []
  for(let i =0;i<data.length;i++) {
    let item = data[i]
    let temp = [...path]
    temp.push(item.name)
    if (item.name === name) return temp
    if (item.children) {
      let res = fn(item.children, name, temp)
      if (res) return res
    }
  }
}

持续更新~~~

转载自:https://juejin.cn/post/7038773226402480142
评论
请登录