你不知道的前端面试题

inverview

前端面试

项目地址
晓智科技晓智科技
晓智文档晓智文档
源码地址源码地址
文档源码文档源码

Promise

  1. await 相当于 Promise.then 处理不了 Promise.reject
!(async function () {
  const p4 = Promise.reject('err1');
  const d = await p4;
  console.log('data', d);
})();

微任务执行时机比宏任务时机早

  1. 宏任务 setInterval,setTimeout,Ajax,Dom
  2. 微任务 async/await
console.log(100);

setTimeout(() => {
  console.log(200);
});

Promise.resolve().then(() => {
  console.log(300);
});

console.log(400);

进程 process 和线程 thread

// 进程, OS进行资源分配和调度的最小单位,有独立内存空间
// 线程, OS进行运算调度的最小单位,共享进程内存空间

手写 promise TODO 回来重点看

class MyPromise {
  state = 'pending';
  value = undefined;
  reason = undefined;

  resolveCallbacks = [];
  rejectCallbacks = [];

  constructor(fn) {
    const resolveHandler = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.resolveCallbacks.forEach((fn) => fn(this.value));
      }
    };
    const rejectHandler = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.resolveCallbacks.forEach((fn) => fn(this.reason));
      }
    };
    try {
      fn(resolveHandler, rejectHandler);
    } catch (err) {
      rejectHandler(err);
    }
  }
  then(fn1, fn2) {
    fn1 = typeof fn1 === 'function' ? fn1 : (v) => v;
    fn2 = typeof fn2 === 'function' ? fn2 : (e) => e;

    if (this.state === 'pending') {
      const p1 = new MyPromise((resolve, reject) => {
        this.resolveCallbacks.push(() => {
          try {
            const newValue = fn1(this.value);
            resolve(newValue);
          } catch (err) {
            reject(err);
          }
        });

        this.rejectCallbacks.push(() => {
          try {
            const newReason = fn2(this.reason);
            reject(newReason);
          } catch (err) {
            reject(err);
          }
        });
      });
      return p1;
    }

    // fulfilled状态
    if (this.state === 'fulfilled') {
      const p1 = new MyPromise((resolve, reject) => {
        try {
          const newValue = fn1(this.value);
          resolve(newValue);
        } catch (err) {
          reject(err);
        }
      });
      return p1;
    }

    // rejcted
    if (this.state === 'rejcted') {
      const p1 = new MyPromise((resolve, reject) => {
        try {
          const newReason = fn2(this.reason);
          reject(newReason);
        } catch (err) {
          reject(err);
        }
      });
      return p1;
    }
  }
  catch(fn) {
    return this.then(null, fn);
  }
}

MyPromise.resolve = function (value) {
  return new MyPromise((resolve) => resolve(value));
};

MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => reject(reason));
};

MyPromise.all = function (promiseList = []) {
  const p1 = new Promise((resolve, reject) => {
    const result = [];
    const length = promiseList.length;
    let resolveCount = 0;
    promiseList.forEach((p) => {
      p.then((data) => {
        result.push(data);
        resolveCount++;
        if (resolveCount === length) {
          resolve(result);
        }
      }).catch((err) => {
        reject(err);
      });
    });
  });
  return p1;
};

MyPromise.race = function (promiseList) {
  let resolved = false;
  const p1 = new MyPromise((resolve, reject) => {
    promiseList.forEach((p) => {
      p.then((data) => {
        if (!resolved) {
          resolve(data);
          resolved = true;
        }
      }).catch((err) => {
        reject(err);
      });
    });
  });
  return p1;
};

深拷贝

function deepClone(obj = {}) {
  if (typeof obj !== 'object' || obj == null) {
    return obj;
  }

  let result;
  if (obj instanceof Array) {
    result = [];
  } else {
    result = {};
  }

  for (let key in obj) {
    // 保证key 不是原型的属性
    if (obj.hasOwnProperty(key)) {
      result[key] = deepClone(obj[key]);
    }
  }

  return result;
}

const obj = {
  x: 100,
};

if (obj.a == null) {
}

// 相当于
// if(obj.a === null || obj.a === undefined) {}

闭包

function create() {
  let a = 100;
  return function () {
    console.log(a);
  };
}

const fn = create();

const a = 200;
fn(); // 100

闭包函数作为参数

function print(fn) {
  const a = 200;
  fn();
}

const a = 100;
function fn() {
  console.log(a);
}

print(fn);

异步处理

import $ from 'jQuery';

console.log('start');

$.get('https://cnodejs.org/api/v1/topics', function (data) {
  console.log('data', data);
});

console.log('end');

promise 加载图片

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const img = document.createElement('img');
    img.onload = () => {
      resolve(img);
    };
    img.onerror = () => {
      reject(new Error('图片加载失败'));
    };
    img.src = url;
  });
}

事件代理

<div id="div1">
  <a href="#">aaa</a>
  <a href="#">bbb</a>
  <a href="#">ccc</a>
</div>;

const div1 = document.getElementById('div1');

div1.addEventListener('click', (event) => {
  event.preventDefault();
  console.log(event.target);
});

代理函数

function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector;
    selector = null;
  }

  elem.addEventListender(type, (event) => {
    const target = event.target;
    if (selector) {
      // 代理邦定
      if (target.matches(selector)) {
        fn.call(target, event);
      }
      return;
    }
    fn.call(target, event);
  });
}

ajax

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://cnodejs.org/api/v1/topics', false);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText));
  }
};

xhr.send(null);

Promise 版 ajax

function ajax(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      } else if (xhr.status === 400) {
        reject(new Error('请求出错'));
      }
    };
    xhr.send(null);
  });
}

http 和 https

  1. http 是明文传输,敏感信息容易被中间劫持
  2. https = http + 加密 劫持了也无法解密

加密方式

  1. 对称加密: 一个 key 同负责加密,解密
  2. 非对称加密: 一对 key,A 加密之后只能用 B 来解密 https 同时用到对称加密和非对称加密

防抖函数

const oInput = document.getElementById('input');

function debounce(fn, delay = 500) {
  let timer = null;
  return function () {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments);
      timer = null;
    }, delay);
  };
}

oInput.addEventListener(
  'keyup',
  debounce(() => {
    console.log(oInput.value);
  }),
);

节流函数

const div1 = document.getElementById('div1');

function throttle(fn, delay = 200) {
  let timer = null;
  return function () {
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments);
      timer = null;
    }, delay);
  };
}

div1.addEventListener(
  'drag',
  throttle((event) => {
    console.log(event.offsetX);
  }),
);

判断两个对像是否相等

function isObject(obj) {
  return typeof obj === 'object' && obj !== null;
}

function isEqual(obj1, obj2) {
  if (!isObject(obj1) || !isObject(obj2)) {
    return obj1 === obj2;
  }

  if (obj1 === obj2) {
    return true;
  }

  const obj1Keys = Object.keys(obj1);
  const obj2Keys = Object.keys(obj2);

  if (obj1Keys.length !== obj2Keys.length) {
    return false;
  }

  for (let key in obj1) {
    const result = isEqual(obj1[key], obj2[key]);
    if (!result) {
      return false;
    }
  }
  return true;
}

const obj1 = {
  a: 100,
  b: {
    x: 100,
    y: 200,
  },
};

const obj2 = {
  a: 100,
  b: {
    x: 100,
    y: 200,
  },
};

splice 的用法

const arr = [10, 20, 30, 40, 50];
const result = arr.splice(1, 2, 'a', 'b', 'c');

console.log('arr', arr);
console.log('result', result);

map 操作

const arr = [10, 20, 30].map((num, index) => {
  return parseInt(num, index);
});

console.log(arr);

函数场明函数提升

const result = sum(10, 20);

function sum(x, y) {
  return x + y;
}

函数表过式不会变量提升

const result = sum(10, 20);
console.log('result', result);

const sum = function (x, y) {
  return x + y;
};

this 的场影题

const User = {
  count: 1,
  getCount: function () {
    return this.count;
  },
};

console.log(User.getCount());
const func = User.getCount;

console.log(func());

trim 方法

String.prototype.trim = function () {
  return this.replace(/^\s+/, '').replace('/s+$', '');
};

数组拍平

function flatten(arr) {
  const isDeep = arr.some((item) => item instanceof Array);
  if (!isDeep) {
    return arr;
  }

  const result = Array.prototype.concat.apply([], arr);
  return flatten(result);
}

const result = flatten([
  [1, 2],
  [3, 4],
]);

map 类型有序类型

const m = new Map([
  ['k1', 'hello'],
  ['k2', 100],
]);

m.set('name', 'hello');

m.forEach((key, value) => console.log({ key, value }));

set 无序速度快

const set = new Set([10, 20, 30, 40]);
set.forEach((val) => console.log(val));

WeakMap

const vMap = new WeakMap();

const userInfo = {
  name: 'hello',
};

const cityInfo = {
  name: 'world',
};

vMap.set(userInfo, cityInfo);

console.log(vMap.get(userInfo));

reduce 求和函数

const arr = [10, 20, 30, 40, 50];

const sum = arr.reduce((sum, curVal, index, arr) => {
  console.log({ sum, curVal, index, arr });
  return sum + curVal;
}, 0);

console.log('sun', sum);

算法与数据结构

旋转数组 key 步 pop 和 unshift

function rotate1(arr, k) {
  const length = arr.length;
  if (!k || length === 0) return arr;

  const step = Math.abs(k % length);

  for (let i = 0; i < step; i++) {
    const n = arr.pop();
    if (n) {
      arr.unshift(n);
    }
  }
  return arr;
}

const arr = [1, 2, 3, 4, 5, 6, 7];
const arr1 = rotate1(arr, 3);
console.log(arr1);

旋转数组 key 步 slice 和 concat

function rotate2(arr, k) {
  const length = arr.length;
  if (!k || length === 0) return arr;

  const step = Math.abs(k % length);

  const part1 = arr.slice(-step);
  const part2 = arr.slice(0, length - step);

  return part1.concat(part2);
}

const arr = [1, 2, 3, 4, 5, 6, 7];

const arr1 = rotate2(arr, 3);
console.log(arr1);

微任务与宏任务

console.log('start');

setTimeout(() => {
  console.log('timeout');
});

Promise.resolve().then(() => {
  console.log('promise then');
});

console.log('end');

for 和 forEach 那个更快

// for更快
// forEeach每次都要创建一个函数来调用,而for不会创建函数
// 函数需要独立的作用域,会有额外的开销
const arr = [];

for (let i = 0; i < 10000 * 10000; i++) {
  arr.push(i);
}

const length = arr.length;

console.time('for');
let n = 0;

for (let i = 0; i < length; i++) {
  n++;
}

console.timeEnd('for'); // 2.36 for更快

console.time('forEach');
let n1 = 0;
arr.forEach(() => {
  n1++;
});
console.timeEnd('forEach'); //8.78

判断括号是否匹配

function isMatch(left, right) {
  if (left === '{' && right === '}') return true;
  if (left === '[' && right === ']') return true;
  if (left === '(' && right === ')') return true;

  return false;
}

function matchBracket(str) {
  const length = str.length;

  if (length === 0) return true;

  const stack = [];

  const leftSymbols = '([{';
  const rightSymbols = '}])';

  for (let i = 0; i < length; i++) {
    const s = str[i];
    if (leftSymbols.includes(s)) {
      stack.push(s);
    } else if (rightSymbols.includes(s)) {
      const top = stack[stack.length - 1];
      if (isMatch(top, s)) {
        stack.pop();
      } else {
        return false;
      }
    }
  }

  return stack.length === 0;
}

const str = '([{}])';

console.log(matchBracket(str));

栈模拟队列

class Queue {
  stack1 = [];
  stack2 = [];
  constructor() {}

  add(n) {
    this.stack1.push(n);
  }

  delete() {
    let result;
    const stack1 = this.stack1;
    const stack2 = this.stack2;

    while (stack1.length) {
      const n = stack1.pop();
      if (n != null) {
        stack2.push(n);
      }
    }

    // 执行stack pop
    result = stack2.pop();
    while (stack2.length) {
      const n = stack2.pop();
      if (n != null) {
        stack1.push(n);
      }
    }

    return result || null;
  }

  get length() {
    return this.stack1.length;
  }
}

链表反转

// 链表反转
function reverseLinkList(listNode) {
  let prevNode;
  let curNode;
  let nextNode = listNode;

  while (nextNode) {
    if (curNode && !prevNode) {
      delete curNode.next;
    }

    if (curNode && prevNode) {
      curNode.next = prevNode;
    }

    prevNode = curNode;
    curNode = nextNode;
    nextNode = nextNode.next;
  }

  curNode.next = prevNode;

  return curNode;
}

function createLinkList(arr) {
  const length = arr.length;

  if (length === 0) throw new Error('数组为空');

  let curNode = {
    value: arr[length - 1],
  };

  if (length === 1) return curNode;

  for (let i = length - 2; i >= 0; i--) {
    curNode = {
      value: arr[i],
      next: curNode,
    };
  }

  return curNode;
}

const n = createLinkList([1, 2, 3, 4]);

console.log(reverseLinkList(n));

同域多 tab 数据共享

let btn = document.getElementById('btn');

btn.addEventListener('click', () => {
  let obj = {
    id: Math.random(),
    name: 'hello',
  };
  console.log('hello');
  localStorage.setItem('info', JSON.stringify(obj));
});

window.addEventListener('storage', (event) => {
  console.log('key', event.key);
  console.log('value', event.newValue);
});

kao2 洋葱圈模型

const Koa = require('koa2');

const app = new Koa();

app.use(async (ctx, next) => {
  await next();

  const rt = ctx.response.get('X-Response-Time');
  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});

app.use(async (ctx, next) => {
  const start = Date.now();
  await next();

  const ms = Date.now() - start;
  ctx.set('X-Response-Time', `${ms}ms`);
});

app.use(async (ctx) => {
  ctx.body = 'hello world';
});

app.listen(3000);

parseInt 进制转换

// [1,NaN,NaN]
let arr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];

console.log(arr.map(parseInt));

数组转树

let arr = [
  {
    id: 1,
    name: '部门A',
    parentId: 0,
  },
  {
    id: 2,
    name: '部门B',
    parentId: 1,
  },
  {
    id: 3,
    name: '部门C',
    parentId: 1,
  },
  {
    id: 4,
    name: '部门D',
    parentId: 2,
  },
  {
    id: 5,
    name: '部门E',
    parentId: 2,
  },
  {
    id: 6,
    name: '部门F',
    parentId: 3,
  },
];

function convert(arr) {
  const map = new Map();

  let root = null;

  let length = arr.length;
  for (let i = 0; i < length; i++) {
    const { id, name, parentId } = arr[i];

    let treeNode = {
      id,
      name,
    };
    map.set(id, treeNode);

    let parentNode = map.get(parentId);
    if (parentNode) {
      if (parentNode.children == null) parentNode.children = [];
      parentNode.children.push(treeNode);
    }
    if (parentId === 0) root = treeNode;
  }

  return root;
}

console.log(convert(arr));

树转树组

let obj = {
  id: 1,
  name: '部门A',
  children: [
    {
      id: 2,
      name: '部门B',
      children: [
        {
          id: 4,
          name: '部门D',
        },
        {
          id: 5,
          name: '部门E',
        },
      ],
    },
    {
      id: 3,
      name: '部门C',
    },
  ],
};

function convert(root) {
  const map = new Map();

  const arr = [];

  // 广度优先遍历
  const queue = [];
  queue.unshift(root);

  while (queue.length > 0) {
    const curNode = queue.pop();
    if (!curNode) break;

    const { id, name, children = [] } = curNode;

    const parentNode = map.get(curNode);
    const parentId = (parentNode && parentNode.id) || 0;
    const item = { id, name, parentId };
    arr.push(item);

    // 子节点入队
    children.forEach((child) => {
      map.set(child, curNode);
      queue.unshift(child);
    });
  }
  return arr;
}

console.log(convert(obj));

原型

function Foo() {
  Foo.a = function () {
    console.log(1);
  };
  this.a = function () {
    console.log(2);
  };
}

Foo.prototype.a = function () {
  console.log(3);
};

Foo.a = function () {
  console.log(4);
};

Foo.a(); // 4

let obj = new Foo();

obj.a(); // 2;

Foo.a(); // 1

then 交替执行

Promise.resolve()
  .then(() => {
    console.log(0);
    // then中返回promise实例 ‘慢两拍’
    return Promise.resolve(4);
  })
  .then((res) => {
    console.log(res);
  });

Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => {
    console.log(2);
  })
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(5);
  })
  .then(() => {
    console.log(6);
  });

连续赋值

// undefined {n:2}
let a = {
  n: 1,
};

let b = a;

a.x = a = { n: 2 };

console.log(a.x);
console.log(b.x);

对像 key 只能是字符串和 Symbol 其它类型都会调用 toString()

let a = {},
  b = '123',
  c = 123;
a[b] = 'b';
a[c] = 'c';

console.log(a[b]); //c

let a = {},
  b = Symbol('123'),
  c = Symbol('123');
a[b] = 'b';
a[c] = 'c';

console.log(a[b]); // b

let a = {},
  b = { key: '123' },
  c = { key: '123' };
a[b] = 'b';
a[c] = 'c';

console.log(a[b]);

数组偏平化

export function flatten(arr) {
  const result = [];
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      result.push(...item);
    } else {
      result.push(item);
    }
  });
  return result;
}

console.log(flatten([1, 2, [3, 4]]));

数组深度扁平化

export function flattenDeep1(arr) {
  const result = [];
  (arr || []).forEach((item) => {
    if (Array.isArray(item)) {
      const flatItem = flattenDeep1(item);
      result.push(...flatItem);
    } else {
      result.push(item);
    }
  });

  return result;
}

export function flattenDeep2(arr) {
  let result = [];
  (arr || []).forEach((item) => {
    if (Array.isArray(item)) {
      const flatItem = flattenDeep2(item);
      result = result.concat(flatItem);
    } else {
      result = result.concat(item);
    }
  });

  return result;
}

console.log(flattenDeep2([1, 2, [3, [4, [5, [6, ['a', 'b']]]]]]));

获取数据类型

export function getType(x) {
  const originType = Object.prototype.toString.call(x);

  const spaceIndex = originType.indexOf(' ');
  return originType.slice(spaceIndex + 1, -1).toLowerCase();
}

console.log(getType('123'));

函数柯里化

export function curry(fn) {
  const fnArgsLength = fn.length;
  let args = [];

  return function calc(...newArgs) {
    args = [...args, ...newArgs];
    if (args.length < fnArgsLength) {
      return calc;
    } else {
      return fn.apply(this, args.slice(0, fnArgsLength));
    }
  };
}

function add(a, b, c) {
  return a + b + c;
}

const curryAdd = curry(add);
console.log(curryAdd(10)(20)(30));

LazyMan 任务队列

class LazyMan {
  tasks = [];
  constructor(name) {
    this.name = name;
    setTimeout(() => {
      this.next();
    });
  }
  next() {
    const task = this.tasks.shift();
    if (task) task();
  }
  eat(food) {
    const task = () => {
      console.log(`${this.name} eat ${food}`);
      this.next();
    };
    this.tasks.push(task);
    return this;
  }

  sleep(seconds) {
    const task = () => {
      setTimeout(() => {
        console.log(`${this.name} 已经睡完了 ${seconds}s`);
        this.next();
      }, seconds * 1000);
    };
    this.tasks.push(task);
    return this;
  }
}

const m = new LazyMan('tom');
m.eat('苹果').eat('香蕉').sleep(2).eat('葡萄').sleep(1).eat('西瓜');

自定义 InstanceOf

export function MyInstance(instance, origin) {
  if (instance == null) return false; // null undefined

  if (typeof instance !== 'object' && typeof instance !== 'function') {
    return false;
  }

  let tempInstance = instance;
  while (tempInstance) {
    if (tempInstance.__proto__ === origin.prototype) {
      return true;
    }
    tempInstance = tempInstance.__proto__;
  }
  return false;
}

console.log(MyInstance({}, Object));
console.log(MyInstance([], Object));
console.log(MyInstance([], Array));
console.log(MyInstance('123', Number));

自定义 bind

Function.prototype.mybind = function (context, ...bindArgs) {
  const self = this;

  return function (...args) {
    const newAargs = bindArgs.concat(args);
    return self.apply(context, newAargs);
  };
};

function fn(a, b, c) {
  console.log({ a, b, c });
}

const fn1 = fn.mybind({ x: 100 }, 10);
fn1(20, 30);

自定义 call

Function.prototype.myCall = function (context, ...args) {
  if (context == null) context = globalThis;

  if (typeof context !== 'object') context = new Object(context);

  const fnKey = Symbol();
  context[fnKey] = this;

  const result = context[fnKey](...args);
  delete context[fnKey];

  return result;
};

function fn(a, b, c) {
  console.log(a, b, c);
}

fn.myCall({ x: 123 }, 1, 2, 3);

自定义 EventBus 事件总线

class EventBus {
  constructor() {
    this.events = {};
  }

  on(type, fn, isOnce = false) {
    const events = this.events;
    if (events[type] == null) {
      events[type] = [];
    }
    events[type].push({ fn, isOnce });
  }

  once(type, fn) {
    this.on(type, fn, true);
  }

  off(type, fn) {
    if (!fn) {
      // 解邦所有type的函数
      this.events[type] = [];
    } else {
      // 解绑单个fn
      const fnList = this.events[type];
      if (fnList) {
        this.events[type] = fnList.filter((item) => item.fn !== fn);
      }
    }
  }
  emit(type, ...args) {
    const fnList = this.events[type];
    if (fnList == null) return;

    this.events[type] = fnList.filter((item) => {
      const { fn, isOnce } = item;
      fn(...args);

      if (!isOnce) return true;
      return false;
    });
  }
}

const events = new EventBus();

function fn1(a, b) {
  console.log('fn1', a, b);
}

function fn2(a, b) {
  console.log('fn2', a, b);
}

function fn3(a, b) {
  console.log('fn3', a, b);
}

events.on('key1', fn1);
events.on('key1', fn2);
events.once('key1', fn3);

events.emit('key1', 10, 20);

events.off('key1', fn1);
events.emit('key1', 10, 20);
// events.emit('key1',10,20);

LRU 缓存

class LRUCache {
  constructor(length) {
    if (length < 1) throw new Error('无效的length');
    this.length = length;
    this.data = new Map();
  }

  set(key, value) {
    const data = this.data;
    if (data.has(key)) {
      data.delete(key);
    }

    data.set(key, value);

    if (data.size > this.length) {
      const delKey = data.keys().next().value;
      data.delete(delKey);
    }
  }

  get(key) {
    const data = this.data;
    if (!data.has(key)) return null;
    const value = data.get(key);

    data.delete(key);

    data.set(key, value);

    return value;
  }
}

const lruCache = new LRUCache(2);
lruCache.set(1, 1);
lruCache.set(2, 2);

console.log(lruCache.get(1));
lruCache.set(3, 3);

console.log(lruCache.get(2));

lruCache.set(4, 4);
console.log(lruCache.get(1));
console.log(lruCache.get(3));
console.log(lruCache.get(4));

统计 sdk

class MyStatis {
  constructor(productId) {
    this.productId = productId;

    //
    this.performance();
  }

  // 发送统计数据
  send(url, params = {}) {
    params.productId = this.productId;

    const paramsArr = [];
    for (let key in params) {
      const value = params[key];

      paramsArr.push(`${key}=${value}`);

      const newUrl = `${url}?${paramsArr.join('&')}`;

      // 用<img> 发送1. 可跨域;2.兼容性非常好
      const img = document.createElement('img');
      img.src = newUrl;
    }
  }

  // 初始化性能统计
  initPerformance() {
    const url = 'xxxx';
    this.send(url, performance.timing);
  }

  // 初始化错误监控
  initError() {
    // js错误
    window.addEventListener('error', (event) => {
      const { error, lineno, colno } = event;
      this.error(error, { lineno, colno });
    });

    window.addEventListener('unhandledrejection', (event) => {
      this.error(new Error(event.reason), { type: 'unhandledrejection' });
    });
  }

  pv() {
    this.event('pv');
  }

  event(key, value) {
    const url = 'xxx';
    this.send(url, { key, value });
  }

  error(err, info) {
    const url = 'xxx';
    const { message, stack } = err;
    this.send(url, { message, stack, ...info });
  }
}

请说明 Ajax Fetch Axios 三者的区别

  • 三者都用于网络请求,但是不同维度
  • Ajax(Asynchorous javascript and xml) 一种技术统称
  • Fetch 浏览器原生 api
  • Axios,是一个 http 第三方库

防抖

  • 防抖:限制执行次数,多次密集的触发只执行一次
function debounce(fn, delay = 200) {
  let timer = 0;
  return function () {
    if (timer) clearTimeout(timer);

    timer = setTimeout(() => {
      fn.applay(this, arguments);
      timer = 0;
    }, delay);
  };
}

节流

  • 节流:限制执行频率,有节奏的执行
function throttle(fn, delay = 200) {
  let timer = 0;
  return function () {
    if (timer) return;
    timer = setTimeout(() => {
      fn.applay(this, arguments);
      timer = 0;
    }, delay);
  };
}

px 和%

  • px 基本单位,绝对单位()
  • % 相对于父元素的宽度比例

em 和 rem

  • em 相对于当前元素的 font-size
  • rem 相对于根节点的 font-size

vw 和 vh

  • vw 屏幕宽度的 1%
  • vh 屏幕高度的 1%

不适用箭头函数的场景

  • 对像方法
  • 对像原型
  • 构造函数
  • 动态上下文回调函数
  • Vue 生命周期 method

for…in

  • for…in 用于可枚举数据,如对像,数组,字符串

for…of

  • for…of 用于可迭代数据,如数组,字符串,Map,Set

for await…of 有什么作用

  • for await…of 用于遍历多个 promise
function createPromise(val) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(val);
    }, 1000);
  });
}

(async function () {
  const p1 = createPromise(100);
  const p2 = createPromise(200);
  const p3 = createPromise(300);

  const list = [p1, p2, p3];
  for await (let res of list) {
    console.log(res);
  }
})();

js 严格模式有什么特点

  • 全局变量必须先声明
  • 禁止使用 with
  • 创建 eval 作用域
  • 禁止 this 指向 window
  • 函数参数不能重名

http 跨域请求为何发送 options 请求

  • options 请求是跨域请求之前的预检查
  • 浏览器自行发起不需我们干预

for 和 forEach 那个更快

  • for 更快 forEach 每次都会创建一个函数来调用,而 for 不会创建函数
  • 函数需要独立的作用域,会有额外的开销
const arr = [];

for (let i = 0; i < 100 * 10000; i++) {
  arr.push(i);
}

const length = arr.length;

console.time('for');

let n1 = 0;

for (let i = 0; i < length; i++) {
  n1++;
}
console.timeEnd('for');

console.time('forEach');
let n2 = 0;
arr.forEach(() => n2++);
console.timeEnd('forEach');

nodejs 开启多进程

// 主进程
const http = require('http');
const fork = require('child_process').fork;

const server = http.createServer((req, res) => {
  if (req.url === '/get-sum') {
    console.log('主进程id', process.pid);

    // 开启子进程
    const computeProcess = fork('./compute.js');
    computeProcess.send('开始计算');

    computeProcess.on('message', (data) => {
      console.log('主进程接受到的信息:', data);
      res.end('sum is ' + data);
    });

    computeProcess.on('close', () => {
      console.log('子进程报错退出');
      computeProcess.kill();
      res.end('error');
    });
  }
});

server.listen(3000, () => {
  console.log('localhost:3000');
});

// 子进程
function getSum() {
  let sum = 0;
  for (let i = 0; i < 10000; i++) {
    sum += i;
  }
  return sum;
}

process.on('message', (data) => {
  console.log('子进程id', process.pid);
  console.log('子进程接受到消息', data);

  const sum = getSum();
  process.send(sum);
});

jsbrage

const sdk = {
  invoke(url, data = {}, onSuccess, onError) {
    const iframe = document.createElement('iframe');
    iframe.style.visibility = 'hidden';
    document.body.appendChild(iframe);

    iframe.onload = function () {
      const content = iframe.contentWindow.document.body.innerHTML;
      onSuccess(JSON.parse(content));
      iframe.remove();
    };
    iframe.onerror = () => {
      onError();
      iframe.remove();
    };
    iframe.src = `${url}?data=${JSON.stringify(data)}`;
  },
  fn1(data, onSuccess, onError) {
    this.invoke('https://xiaozhi.shop', data, onSuccess, onError);
  },
};

sdk.fn1(
  {},
  () => {},
  () => {},
);

requestIdleCallback 和 requestAnimationFramer 区别

  • requestAnimationFramer 每次渲染完都会执行高优
  • requestIdleCallback 空闲时才执行,低优
const box = document.getElementById('box');

const btn = document.getElementById('btn');

btn.addEventListener('click', () => {
  let curWidth = 100;
  let maxWidth = 400;

  function addWidth() {
    curWidth = curWidth + 3;
    box.style.width = `${curWidth}px`;
    if (curWidth < maxWidth) {
      requestIdleCallback(addWidth);
    }
  }

  addWidth();
});

h5 click 有 300ms 延迟 如果何解决

  • FastClick
  • 浏览器响应式 width=device-width

网络请求中 token 和 cooke 的区别

  • cookie: HTTP 标准,跨域限制,配合 session 使用
  • token:无标准,无跨域限制,用于 jwt

session 和 jwt 那个更好

  • session 的优点与缺点
  1. 原理简单易于学习,用户信息存储在服务端,可以快速封禁某个用户
  2. 占用服务端内存,硬件成本高
  3. 多进和多服务时不好同步,需要使用第三方缓存如 redis
  4. 默认有跨域限制
  • jwt 的优点与缺点
  1. 用户信息存储在客户端,无法快速封禁某个用户
  2. 服务器秘被泄漏,用户信息全部丢失
  3. token 体积一搬大于 cookie,会增加请求数据量

http 协议和 udp 协议有什么区别

  • http 协议在应用程,TCP UDP 是传输层
  • TCP 有连接有断开稳定传输
  • UDP 无连接无断开不稳定传输,但效率高
晓智科技公众号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值