Git Product home page Git Product logo

blog's Introduction

Hi there 👋

lawler

blog's People

Contributors

liam61 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

blog's Issues

【面经】字节跳动 二面9:实现简易版 MVVM

实现简易版 MVVM

关键词:Object.defineProperty

class Observer {
  constructor(data) { this.data = data; }

  observe(prop, cbObj) { // 调用了 observe 才会对属性 prop 进行观察
    if (!prop || typeof prop !== 'string') return;
    this.defineReactive(this.data, prop, this.data[prop], cbObj);

    // 由于 new Observer() 出的对象没有 data.a 属性,必须通过 data.data.a 调用,熟悉 vue 的应该知道这一点
    // 为了方便我们直接调用 data.a,把 this.data 的属性代理到 this 上
    this.proxyData(prop, this.data);
  }

  defineReactive(obj, key, value, { getCallback, setCallback }) { // 抽离的对属性进行监控的方法
    Object.defineProperty(obj, key, {
      get() {
        getCallback(value);
        return value;
      },
      set(newValue) {
        if (newValue !== value) {
          setCallback(newValue, value);
          value = newValue;
        }
      }
    });
  }

  proxyData(key, data) {
    Object.defineProperty(this, key, {
      get() { return data[key]; }, // 对调用 data.a 进行劫持,返回内部的 data[key]
      set(newValue) { data[key] = newValue; } // 对 data.a = 2; 进行劫持,实际上的在操作内部的 data[key] = 2;
    });
  }
}

// 测试
const data = new Observer({ a: 1, b: 2 });

const callbackObj = {
  getCallback(value) { console.log(`get: value: ${value}`); },
  setCallback(newValue, oldValue) { console.log(`set: newValue: ${newValue}, oldValue: ${oldValue}`); }
}
data.observe("a", callbackObj);
data.a; // console: 1
data.a = 2; // console: 2 1
data.b; // undefined 因为没有进行 data.observe('b', ...)

【面经】字节跳动 三面8:顺序发送 4 个请求 a,b,c,d,要求按照顺序输出

顺序发送 4 个请求 a,b,c,d,要求按照顺序输出

利用数组缓存,再按照标志依次输出

function getData(urls) {
  // 感谢 @jinxin0112 的指正
  return new Promise((resolve, reject) => {
    const res = [], len = urls.length;
    urls.forEach((url, i) => {
      fetch('http://localhost:8080' + url).then(data => data.json())
        .then(data => {
          res[i] = { data, printed: false }; // 将数据放入缓存数组
          let flag = true;
          for (let j = 0; j < len && flag; j += 1) {
            if (res[j]) { // 如果标志为 j 的有返回值,则继续
              if (!res[j].printed) {
                console.log(res[j].data);
                res[j].printed = true;
                j === len - 1 && resolve(res.map(o => o.data))
              }
            } else { // 无返回值,则跳出
              flag = false;
            }
          }
        }, reject);
    });
  })
}

const listPromise = getData(['/data.json', '/data2.json', '/data3.json', '/data4.json']);
listPromise.then(res => console.log(res));

// server.js 测试:模拟各个请求延迟返回
const express = require('express');
const cors = require('cors');
const app = express();

const getTime = () => Math.floor(Math.random() * 4 * 1000);

app.use(cors({ origin: '*' }));

app.use('/data.json', (req, res) => {
  setTimeout(() => res.end(JSON.stringify({ a: 1 })), getTime());
});

app.use('/data2.json', (req, res) => {
  setTimeout(() => res.end(JSON.stringify({ b: 2 })), getTime());
});

app.use('/data3.json', (req, res) => {
  setTimeout(() => res.end(JSON.stringify({ c: 3 })), getTime());
});

app.use('/data4.json', (req, res) => {
  setTimeout(() => res.end(JSON.stringify({ d: 4 })), getTime());
});

app.listen(8080, () => console.log('the app is running at http://localhost:8080'));

【面经】字节跳动 一面7(3):深度克隆 js 各种数据类型

深度克隆 js 各种数据类型。附加题:实现对象中嵌套数组,数组中嵌套对象

JS 中基本类型

5 种原始类型:Boolean, Number, String, Null, Undefined(暂不考虑 ES6 的 Symbol)还有 Object

// 这里把数组也考虑了进来
function deepClone(someObj) {
  const { toString } = Object.prototype;
  const isObject = o => toString.call(o).slice(8, -1) === 'Object';

  function _deepClone(source) {
    if (Array.isArray(source)) { // 处理数组
      return source.reduce((res, item) => {
        res.push(_deepClone(item));
        return res;
      }, []);
    }

    if (isObject(source)) { // 处理对象
      return Object.keys(source).reduce((res, key) => {
        res[key] = _deepClone(source[key]);
        return res;
      }, {});
    }

    return source; // 处理其他类型
  }

  return _deepClone(someObj);
}

// 测试
console.log(deepClone([1, 2, [3], [4, 5, [6, 7]]]));
console.log(deepClone({ a: 1, b: { c: 3, d: 7, e: { y: 8, z: 4 } } }));
console.log(deepClone(12));
console.log(deepClone(true));
console.log(deepClone(null));
console.log(deepClone(undefined));
console.log(deepClone([1, { a: 9, b: 8 }, [3], [4, 5, [6, 7]]]));
console.log(deepClone({ a: 1, b: { c: [4, 5, [6]], d: 7, e: { y: 8, z: 4 } } }));

【面经】字节跳动 笔试4:格式化数字。正则和非正则实现

格式化数字

要求:使用正则和非正则两种方式实现

评论区大神们,欢迎附上正则版~

// 法1:toLocaleString
// 浏览器提供该 api 对数字进行格式化,不过对于小数的处理使用的是四舍五入
let num = 1234567;
console.log(num.toLocaleString('en-US')); // 1,234,567
num = 12345.6785;
console.log(num.toLocaleString('en-US')); // 12,345.679

// 法2:数组操作
function format(num) {
  if (typeof num !== 'number') return num;

  const [ first, last ] = (num + '').split('.');
  const flen = first.length;
  const tmp = first.split('').reverse().reduce((res, n, i) => {
    if ((i + 1) % 3 === 0  && i !== flen - 1) res.push(n, ',');
    else res.push(n);
    return res;
  }, []).reverse().join('');

  return last ? tmp + '.' + last : tmp;
}

// 法3:递归
function format(num) {
  if (typeof num !== 'number') return num;

  const [first, last] = (num + '').split('.');

  function run(s) {
    if (s.length <= 3) return s;
    return run(s.slice(0, -3)) + ',' + s.slice(-3);
  }

  return last ? run(first) + '.' + last : run(first);
}

console.log(format(123456)); // 123,456
console.log(format(1234567)); // 1,234,567
console.log(format(12345.6785)); // 12,345.6785
console.log(format(123456.78)); // 123,456.78

【面经】字节跳动 笔试2:格式化发布时间

格式化发布时间

找准每段时间的变换点即可

function format(date, testMode) {
  const MIN = 60 * 1000;
  const HOUR = 60 * MIN;
  const DAY = HOUR * 24;
  const WEEK = DAY * 7;
  const time = testMode ? date : Date.now() - date;

  if (time < MIN) return '刚刚';

  if (time < HOUR) return Math.floor(time / MIN) + '分前';

  if (time < DAY) return Math.floor(time / HOUR) + '小时前';

  if (time < WEEK) return Math.floor(time / DAY) + '天前';

  return new Date(testMode ? Date.now() - date : date).toLocaleString();
}

// 测试
console.log(format(60 * 1000 - 1, true)); // 59 秒 999
console.log(format(60 * 1000, true)); // 1 分

console.log(format(60 * 1000 * 59 + 59 * 1000 + 999, true)); // 59 分 59 秒 999
console.log(format(60 * 1000 * 60, true)); // 1 小时

console.log(format(60 * 1000 * 60 * 23 + 60 * 1000 * 59 + 59 * 1000 + 999, true)); // 23 小时 59 分 59 秒 999
console.log(format(60 * 1000 * 60 * 24, true)); // 1 天

console.log(format(60 * 1000 * 60 * 24 * 6, true)); // 6 天
console.log(format(60 * 1000 * 60 * 24 * 7, true)); // 7 天

// 正常使用
console.log(format(1554111847534)); // 发布时的时间戳

【面经】字节跳动 笔试1:实现一个类可以完成事件 on,once,trigger,off

手撸一个事件机制

关键词:发布-订阅模式

其实核心就是维护一个对象,对象的 key 存的是事件 type,对应的 value 为触发相应 type 的回调函数,即 listeners,然后 trigger 时遍历通知,即 forEach 进行回调执行。

class EventTarget {
  constructor() {
    this.listeners = {}; // 储存事件的对象
  }

  on(type, callback) {
    if (!this.listeners[type]) this.listeners[type] = []; // 如果是第一次监听该事件,则初始化数组
      this.listeners[type].push(callback);
  }

  once(type, callback) {
   if (!this.listeners[type]) this.listeners[type] = [];
      callback._once = true; // once 只触发一次,触发后 off 即可
      this.listeners[type].push(callback);
  }

  off(type, callback) {
    const listeners = this.listeners[type];
    if (Array.isArray(listeners)) {
      // filter 返回新的数组,会每次对 this.listeners[type] 分配新的空间
      // this.listeners[type] = listeners.filter(l => l !== callback);
      const index = listeners.indexOf(callback); // 根据 type 取消对应的回调
      this.listeners[type].splice(index, 1); // 用 splice 要好些,直接操作原数组

      if (this.listeners[type].length === 0) delete this.listeners[type]; // 如果回调为空,删除对该事件的监听
    }
  }

  trigger(event) {
    const { type } = event; // type 为必传属性
    if (!type) throw new Error('没有要触发的事件!');

    const listeners = this.listeners[type]; // 判断是否之前对该事件进行监听了
    if (!listeners) throw new Error(`没有对象监听 ${type} 事件!`);

    if (!event.target) event.target = this;

    listeners.forEach(l => {
      l(event);
      if (l._once) this.off(type, l); // 如果通过 once 监听,执行一次后取消
    });
  }
}

// 测试
function handleMessage(event) { console.log(`message received: ${ event.message }`); }

function handleMessage2(event) { console.log(`message2 received: ${ event.message }`); }

const target = new EventTarget();

target.on('message', handleMessage);
target.on('message', handleMessage2);
target.trigger({ type: 'message', message: 'hello custom event' }); // 打印 message,message2

target.off('message', handleMessage);
target.trigger({ type: 'message', message: 'off the event' }); // 只打印 message2

target.once('words', handleMessage);
target.trigger({ type: 'words', message: 'hello2 once event' }); // 打印 words
target.trigger({ type: 'words', message: 'hello2 once event' }); // 报错:没有对象监听 words 事件!

【面经】美团 二面5:用 promise 实现一个请求超时功能

用 promise 实现一个请求超时功能

关键词:promise.then 与 setTimeout 并行

抓住 promise 的状态只能由 pending -> fulfilled,或者 pending -> rejected,并且只能进行一次改变

function promiseWithTimeout(url, timeout = 3000) {
  return new Promise((resolve, reject) => {
    fetch(url).then(data => data.json()).then(data => resolve(data)); // fetch 先得到结果就 resolve
    setTimeout(() => reject(Error('time is out!')), timeout); // 时间到了还没 fetch 到就 reject
  });
}

promiseWithTimeout('http://localhost:8080/data.json')
  .then(data => console.log(data))
  .catch(err => console.error(err));

// server.js 测试
const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors({ origin: '*' }));

app.use('/data.json', (req, res) => {
  setTimeout(() => res.end(JSON.stringify({ a: 1 })), Math.floor(Math.random() * 6 * 1000));
});

app.listen(8080, () => console.log('the app is running at http://localhost:8080'));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.