inverview
前端面试
项目 | 地址 |
---|---|
晓智科技 | 晓智科技 |
晓智文档 | 晓智文档 |
源码地址 | 源码地址 |
文档源码 | 文档源码 |
Promise
- await 相当于 Promise.then 处理不了 Promise.reject
!(async function () {
const p4 = Promise.reject('err1');
const d = await p4;
console.log('data', d);
})();
微任务执行时机比宏任务时机早
- 宏任务 setInterval,setTimeout,Ajax,Dom
- 微任务 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
- http 是明文传输,敏感信息容易被中间劫持
- https = http + 加密 劫持了也无法解密
加密方式
- 对称加密: 一个 key 同负责加密,解密
- 非对称加密: 一对 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 的优点与缺点
- 原理简单易于学习,用户信息存储在服务端,可以快速封禁某个用户
- 占用服务端内存,硬件成本高
- 多进和多服务时不好同步,需要使用第三方缓存如 redis
- 默认有跨域限制
- jwt 的优点与缺点
- 用户信息存储在客户端,无法快速封禁某个用户
- 服务器秘被泄漏,用户信息全部丢失
- token 体积一搬大于 cookie,会增加请求数据量
http 协议和 udp 协议有什么区别
- http 协议在应用程,TCP UDP 是传输层
- TCP 有连接有断开稳定传输
- UDP 无连接无断开不稳定传输,但效率高
晓智科技公众号