继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

几个JS代码手写题

30秒到达战场
关注TA
已关注
手记 286
粉丝 95
获赞 569

作者:等你下课,原文地址

Github 代码实现案例
/**
 * new2 new关键字的代码实现演示
 * @param {function} func 被new的类 (构造函数)
 */
function new2(func) {
    // 创建了一个实例对象 o,并且这个对象__proto__指向func这个类的原型对象 
    let o = Object.create(func.prototype); 
    // (在构造函数中this指向当前实例)让这个类作为普通函数值行 并且里面this为实例对象 
    let k = func.call(o);
    // 最后再将实例对象返回 如果你在类中显示指定返回值k,
    // 注意如果返回的是引用类型则将默认返回的实例对象o替代掉
    return typeof k === 'object' ? k : o;
}

// 实验
function M() { // 即将被new的类
    this.name = 'liwenli';
}

let m = new2(M); // 等价于 new M 这里只是模拟
console.log(m instanceof M); // instanceof 检测实例
console.log(m instanceof Object);
console.log(m.__proto__.constructor === M);
Object.create 兼容实现
   let obj1 = {id: 1};
    Object._create = (o) => {
        let Fn = function() {}; // 临时的构造函数
        Fn.prototype = o;
        return new Fn;
    }

    let obj2 = Object._create(obj1);
    console.log(obj2.__proto__ === obj1); // true
    console.log(obj2.id); // 1

    // 原生的Object.create
    let obj3 = Object.create(obj1);
    console.log(obj3.__proto__ === obj1); // true
    console.log(obj3.id); // 1
curry 柯理化的实现(递归调用 + valueOf)

知识点:valueOf 浏览器环境下 当我们以log(fn)这种形式取值时,会隐式调用fn自身的valueOf 所以得到的是valueOf的返回值

function fn() {};
fn.valueOf = () => console.log('valueof');
console.log(fn); // valueof
const mul = x => {
    const result = y => mul(x * y); // 递归调用mul
    result.valueOf = () => x;
    return result;
}
console.log(mul(2)(3)); // 6
// 在上面mul每执行一次,就会返回一个valueOf被改写后的新函数result 并且result执行会在里面调用mul(x * y)
// 在result函数的valueOf里保存着 由上一次x * y 传进来的结果x, 也就是上一次x*y 会作为这一次的输出 依然叫x

// 第一次mul(2) 此时 x为2  return result
result 为 result = y => mul(2 * y); 
// 第二次 mul(2)(3) 等价于 第一个mul返回的result(3), result执行 => mul(2 * 3) 再次调用mul 将2*3 = 6 的结果作为mul参数
// 最后mul(6) x = 6 在返回一个新函数result 此时result的valueOf = () => 6

// log(mul(2)(3)) 相当于log的最后返回的result 隐式调用valueOf 返回 6
curry 将多参数函数转换为接收单一参数的函数
        let fn = function(a, b, c) { // 多参数函数
           return a + b + c;
        }
        function curry(fn) {
            let args = []; // 收集参数
            let len = fn.length;
            return function fe() {
                args = args.concat([].slice.call(arguments, 0));
                if (args.length === len) {
                    return fn.apply(null, args);
                }
                return fe;
            }
        }

        console.log(curry(fn)(1)(2)(3)); // 6
收集参数 延迟执行 到达指定次数才执行
   // 参数收集 指定次数后执行
        function fn(...rest) {console.log(rest);};
        function after(fn, time = 1) {
            let params = [];
            return function(...rest) {
                params = [...params, ...rest];
                if (--time === 0) {
                    fn.apply(this, params);
                }
            }
        }
        let newFn = after(fn, 3); // 执行3次 内部fn才会执行
        newFn(2);
        newFn(3);
        newFn(4);
函数节流

throttle 策略的电梯。保证如果电梯第一个人进来后,50毫秒后准时运送一次,不等待。如果没有人,则待机。

  let throttle = (fn, delay = 50) => { // 节流 控制执行间隔时间 防止频繁触发 scroll resize mousemove
        let stattime = 0;
        return function (...args) {
            let curTime = new Date();
            if (curTime - stattime >= delay) {
                fn.apply(this, args);
                stattime = curTime;
            }
        }
    }
防抖动

debounce 策略的电梯。如果电梯里有人进来,等待50毫秒。如果又人进来,50毫秒等待重新计时,直到50毫秒超时,开始运送。

   let debounce = (fn, time = 50) => { // 防抖动 控制空闲时间 用户输入频繁
            let timer;
            return function (...args) {
                let that = this;
                clearTimeout(timer);
                timer = setTimeout(fn.bind(that, ...args), time);
            }
        }
深度拷贝兼容写法
function deepCopy(obj) {
    if (typeof obj !== 'object') return obj;
    if (typeof window !== 'undefined' && window.JSON) { // 浏览器环境下 并支持window.JSON 则使用 JSON
        return JSON.parse(JSON.stringify(obj));
    } else {
        let newObj = obj.constructor === 'Array' ? [] : {};
        for(let key in obj) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
        return newObj;
    }
}

let obj = {a: 1, b: [12]};
let newObj = deepCopy(obj);
newObj.b[1] = 100;
console.log(obj);
console.log(newObj);
Function的bind实现
Function.prototype._bind = function(context) {
    let func = this;
    let params = [].slice.call(arguments, 1);
    return function() {
        params = params.concat([].slice.call(arguments, 0));
        func.apply(context, params);
    }
}

let obj = {id: 24}

function fn1(a) {
    console.log(this, arguments);
}
let foo = fn1._bind(obj, obj.id);
函数组合串联compose(koa reduce中间件)
// 组合串联
let fn1 = (a) => a + 1;
let fn2 = (b) => b + 2;
let fn3 = (c) => c + 3;

let funs = [fn1, fn2, fn3];

let compose = (func) => {
    return arg => func.reduceRight((composed, fn) => fn(composed), arg);
}
console.log(compose(funs)(100)); // 相当于fn1(fn2(fn3(100)))
co函数
function* fn(a) {
  a = yield a;
  let b = yield 2;
  let c = yield 3;
  return a + b + c;
}

function co(fn, ...args) {
  let g = fn(...args);
  return new Promise((resolve, reject) => {
    function next(lastValue) {
        let { value, done } = g.next(lastValue);
        if (done) {
          resolve(value);
        } else {
         if (value instanceof Promise) {
           value.then(next, (val) => reject(val));
         } else {
           next(value)
         }
        }
    }
    next();
  });
}
co(fn, 100).then(value => {
    console.log(value); // 105
});
如何主动中止Promise调用链
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => { // 异步操作
      resolve('start')
  }, 1000);
});

p1.then((result) => {
   console.log('a', result); 
   return Promise.reject('中断后续调用'); // 此时rejected的状态将直接跳到catch里,剩下的调用不会再继续
}).then(result => {
   console.log('b', result);
}).then(result => {
   console.log('c', result);
}).catch(err => {
   console.log(err);
});

// a start
// 中断后续调用
bluebird.promisify实现(将异步函数 转换为promise形式)
// promisify.js
module.exports = {
   promisify(fn){
       return function () {
           var args = Array.from(arguments);
           return new Promise(function (resolve, reject) {
               fn.apply(null, args.concat(function (err) {
                   if (err) {
                       reject(err);
                   } else {
                       resolve(arguments[1])
                   }
               }));
           })
       }
   }
}

// main.js
const fs = require('fs');
const {promisify} = require('./promisify.js');

const readFile = promisify('fs.readFile'); // 转换异步读取

// 异步文件 由回调函数形式变成promise形式
readFile('./1.txt', 'utf8').then(data => { 
   console.log(data);
}).catch(err => {
   console.log(err);
});
window.requestAnimationFrame兼容性处理
window._requestAnimationFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          function(callback){
            window.setTimeout(callback, 1000 / 60);
          };
})();
字符串是否符合回文规则
let str = 'My age is 0, 0 si ega ym.';

方法一

function palindrome(params) {
  params = params.replace(/[\W\s_]/ig, '');
 return params.toLowerCase()  === params.split('').reverse().join('').toLowerCase();
}
console.log(palindrome(str));

方法二

function palindrome(params) {
  params = params.replace(/[\W\s_]/ig, '').toLowerCase();
  for (var i = 0, j = params.length-1; i<j; i++, j--) {
    if (params[i] !== params[j]) {
      return false;
    }
  }
  return true;
}
解构
// 将 destructuringArray([1, [2, 3], 4], "[a, [b], c]") => {a: 1, b: 2, c: 4}
const targetArray = [1, [2, 3], 4];
const formater = "[a, [b], c]";

const destructuringArray = (values, keys) => {
  try {
    const obj = {};
    if (typeof keys === 'string') {
      keys = JSON.parse(keys.replace(/\w+/g, '"$&"'));
    }

    const iterate = (values, keys) =>
      keys.forEach((key, i) => {
        if(Array.isArray(key)) iterate(values[i], key)
        else obj[key] = values[i]
      })

    iterate(values, keys)

    return obj;
  } catch (e) {
    console.error(e.message);
  }
}
数组展平
将[[1, 2], 3, [[[4], 5]]] 展平为 [1, 2, 3, 4, 5]

let arr = [[1, 2], 3, [[[4], 5]]]; // 数组展平
function flatten(arr) {
    return [].concat(
        ...arr.map(x => Array.isArray(x) ? flatten(x) : x)
    )
}
找出数组中重复出现过的元素
// 例如:[1,2,4,4,3,3,1,5,3]
// 输出:[1,3,4]
let arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];

// 方法一
function repeat1(arr){
    var result = [], map = {};
    arr.map(function(num){
    if(map[num] === 1) result.push(num); // 等于1说明之前出现过一次 这次重复出现了
        map[num] = (map[num] || 0) + 1; // 微妙之处 开始第一次出现无值 记为 0 + 1 = 1 下一次从1开始累加
    });
    return result;
}
console.log(repeat1(arr));

// 方法二

function repeat(arr) {
    let result = arr.filter((x, i, self) => {
        return self.indexOf(x) === i && self.lastIndexOf(x) !== i
    }); // 
    return result;
}
console.log(repeat(arr));
将数组中按照数字重复出现的次数进行排序
// 如果次数相同 则按照值排序 比如  2, 2, 2和 1, 1, 1  应排序为 [1, 1, 1, 2, 2, 2]
// 比如 [1,2,1,2,1,3,4,5,4,5,5,2,2] => [3, 4, 4, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2]

let arr = [9, 7, 7, 1, 2, 1, 2, 1, 3, 4, 5, 4, 5, 5, 2, 2];
function sortArray(arr) {
    let obj = {};
    let newArr = [];
    for(let i = 0; i < arr.length; i++) {
      let cur = arr[i];
      if(obj[cur]){
        obj[cur].push(cur);
        continue;
      }
      obj[cur] = [cur];
    }
    for(let k in obj) {
      if(obj.hasOwnProperty(k)) {
        newArr.push(obj[k])
      }
    }
    newArr.sort((a, b) => {
      if(a.length === b.length){
        return a[0] - b[0];
      }
        return a.length - b.length;
    });
    newArr = newArr.reduce((prev, cur) => prev.concat(cur));
    return newArr;
  }
  console.log(sortArray(arr)); // [ 3, 9, 4, 4, 7, 7, 1, 1, 1, 5, 5, 5, 2, 2, 2, 2 ]
不用循环,创建一个长度为 100 的数组,并且每个元素的值等于它的下标。
function createArray(len, arr = []) {

    if (len > 0) {
        arr[--len] = len;
        createArray(len, arr);
    }
    return arr;
}
console.log(createArray(100)); 
根据关键词找出 所在对象id
var docs = [
    {
        id: 1,
        words: ['hello', "world"]
    }, {
        id: 2,
        words: ['hello', "hihi"]
    }, {
        id: 3,
        words: ['haha', "hello"]
    }, {
        id: 4,
        words: ['world', "nihao"]
    }
];
findDocList(docs, ['hello']) // 文档id1,文档id2,文档id3
findDocList(docs, ['hello', 'world']) // 文档id1
function findDocList(docs, word) {
    let ids = [];
    for (let i = 0; i < docs.length; i++) {
        let {id, words} = docs[i];
        let flag = word.every((item) => {
            return words.indexOf(item) > -1;
        });
        flag && ids.push(id);
    }
    return ids;
}
findDocList(docs, ['hello', 'world']);

getElementsByClassName 兼容写法
  function getByClass(cName) {
      if ('getElementsByClassName' in this) {
          return this.getElementsByClassName(cName);
      }
      cName = cName.replace(/(^\s+|\s+$)/g, '').split(/\s+/g);
      let eles = this.getElementsByTagName('*');
     for (let i = 0; i < cName.length; i++) {
        let reg = new RegExp(`(^| )${cName[i]}( |$)`);
        let temp = [];
        for (let j = 0; j < eles.length; j++) {
            let cur = eles[j];
            let {className} = cur;
            if (reg.test(className)) {
                temp.push(cur);
            }
        }
        eles = temp;
     }
     return eles;
  }
  console.log(content.getByClass('c1 c2 '));
插入排序

插入排序 从后往前比较 直到碰到比当前项 还要小的前一项时 将这一项插入到前一项的后面

function insertSort(arr) {
  let len = arr.length;
  let preIndex, current;
  for (let i = 1; i < len; i++) {
    preIndex = i - 1;
    current = arr[i]; // 当前项
    while (preIndex >= 0 && arr[preIndex] > current) {
      arr[preIndex + 1] = arr[preIndex]; // 如果前一项大于当前项 则把前一项往后挪一位
      preIndex-- // 用当前项继续和前面值进行比较
    }
    arr[preIndex + 1] = current; // 如果前一项小于当前项则 循环结束 则将当前项放到 前一项的后面
  }
  return arr;
}
选择排序

选择排序 每次拿当前项与后面其他项进行比较 得到最小值的索引位置 然后把最小值和当前项交换位置

function selectSort(arr) {
  let len = arr.length;
  let temp = null;
  let minIndex = null;
  for (let i = 0; i < len - 1; i++) { // 把当前值的索引作为最小值的索引一次去比较
    minIndex = i; // 假设当前项索引 为最小值索引
    for (let j = i + 1; j < len; j++) { // 当前项后面向一次比小
      if (arr[j] < arr[minIndex]) { // 比假设的值还要小 则保留最小值索引
        minIndex = j; // 找到最小值的索引位置
      }
    }
    // 将当前值和比较出的最小值交换位置
    if (i !== minIndex) {
       temp = arr[i]
       arr[i] = arr[minIndex];
       arr[minIndex] = temp;
    }
  }
  return arr;
}
冒泡排序

冒泡排序 相邻两项进行比较 如果当前值大于后一项 则交换位置

function bubleSort(arr) {
  let length = arr.length;
  let temp = null;
  for (let i = 0; i < length - 1; i++) { // 控制轮数
    let flag = false; // 当前这轮是否交换过标识
    for (let l = 0; l < length - i - 1; l++) { // 控制每轮比较次数
      if (arr[l] > arr[l + 1]) {
        temp = arr[l];
        arr[l] = arr[l + 1];
        arr[l + 1] = temp;
        flag = true; // 如果发生过交换flag则为true
      } 
    }
    if (!flag) { // 优化  如果从头到尾比较一轮后 flag依然为false说明 已经排好序了 没必要在继续下去
      temp = null;
      return arr;
    }
  }
}
快速排序(递归)
function quickSort(arr) {
    if (arr.length <= 1) return arr;
    let midIndex = Math.floor(arr.length / 2);
    let midNum = arr.splice(midIndex, 1)[0];
    let left = [];
    let right = [];
    for(let i = 0; i < arr.length; i++) {
        let cur = arr[i];
        if (cur <= midNum) {
            left.push(cur);
        } else {
            right.push(cur);
        }
    }
    return quickSort(left).concat(midNum, quickSort(right));
}

let arr = [2, 4, 12, 9, 22, 10, 18, 6];
quickSort(arr);
数组去重几种方法
const arr = [1, 2, 1, 2, 3, 4, 2, 1, 3];

// 1 ES6
let newArr = [...new Set(arr)];

// 2
Array.prototype.unique2 = function() {
    let newArr = [];
    let len = this.length;
    for(let i = 0; i < len; i++) {
        let cur = this[i];
        if(newArr.indexOf(cur) === -1) {
            newArr[newArr.length] = cur;
        }
    }
    return newArr;
}
console.log(arr.unique1());

// 3
Array.prototype.unique3 = function() {
    let newArr = this.slice(0);
    let len = this.length;
    let obj = {};
    for(let i = 0; i < len; i++) {
        let cur = newArr[i];
        if(obj[cur]) {
            newArr[i] = newArr[newArr.length - 1];
            newArr.length--;
            i--;
            continue;
        }
        obj[cur] = cur;
    }
    return newArr;
}
console.log(arr.unique3());

// 4 最快
Array.prototype.unique4 = function() {
    let json = {}, newArr = [], len = this.length;
    for(var i = 0; i < len; i++) {
        let cur = this[i];
        if (typeof json[cur] == "undefined") {
            json[cur] = true;
            newArr.push(cur)
        }
    }
    return newArr;
}
console.log(arr.unique4());
求解

在一个数组中 如 a、b两项, 要保证a和b的差 加上 a和b索引差 的结果max 是数组中其他两项max 中的最大值 找出符合条件两项a, b的值 let max = (a - b) + (a的索引- b的索引); 求a b

害怕手写代码 只能硬着头皮

后续会持续更新...

打开App,阅读手记
2人推荐
发表评论
随时随地看视频慕课网APP