# “寒冬”三年经验前端面试总结(含头条、百度、饿了么、滴滴等)
# CSS
# 盒模型
怪异模式box-sizing = border-box, width = content+padding+border
标准模式,box-sizing = content-box, width = content
# 垂直居中方法
// 不定宽高
{
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
{
display: flex;
align-items: center;
justify-content: center;
}
{
postiton: fixed;
top: 50%;
left: 50%
transform: translate(-50%, -50%)
}
# 三栏布局
两边固定,中间flex:1
# 选择器权重
!important > inline > id > class > tag
# 清除浮动和BFC|IFC
1.浮动的产生:
浮动元素形成新的BFC,父元素会忽略子元素中所有浮动的元素
2.浮动带来的问题:
高度塌陷
3.解决浮动:
3.1在浮动元素后面加一个元素(标签和伪元素)),让父元素强行包裹
3.2父元素也形成一个BFC(overflow:hidden, float: left|right)
4.BFC能解决的问题
高度塌陷,边距折叠
# position属性
static, relative, absoulte, fixed, sticky(粘性定位)
重绘与重排(回流), 脱离文档流来减少重排
# 自适应的正方形
宽高为屏幕40%的正方形
{
width: 3rem;
height: 3rem;
}
{
width: 40vw;
height: 40vw;
}
{
width: 300rpx;
height: 300rpx;
}
# 用css实现一个三角形
# Javascript
# 防抖和节流
// 区别:目的都是减少事件触发频率,节流会在固定频率内触发,防抖会只执行一次
// 节流
function throttle(func, delay=150){
let start = +new Date(),
timer = 0;
return function(){
let cur = +new Date();
if (cur - start > delay){
timer = setTimeout(()=>{
func();
}, delay);
start = cur;
}
}
}
// 防抖
function debounce(func, delay=150){
let start = +new Date(),
timer = 0;
return function(){
let cur = +new Date();
window.clearTimeout(timer);
timer = setTimeout(()=>{
func();
}, delay);
start = cur;
}
}
# 深拷贝
// 1. js里所有的深拷贝都是最外层的深拷贝,内存还是浅拷贝,比如:object.assign, ..., slice, concat
// 2. JSON.stringify和JSON.parse会忽略函数,时间对象,正则等js内置对象
// 3. 深拷贝的原理是依赖递归逐层拷贝
function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
|| Object.prototype.toString.call(obj) === '[object Array]'
}
function deepCopy(source,hash = new WeakMap()){
// 判断如果参数不是一个对象,返回改参数
if(!isObject(source)) return source;
if(hash.has(source)) return hash.get(source); // 如何拷贝过该对象,则直接返回该对象
// 判断参数是对象还是数组来初始化返回值
let res = Array.isArray(source)?[]:{};
hash.set(source,res); // 哈希表添加新对象
// 循环参数对象的key
for(let key in source){
// 如果该key属于参数对象本身
if(Object.prototype.hasOwnProperty.call(source,key)){
// 如果该key的value值是对象,递归调用深拷贝方法进行拷贝
if(isObject(source[key])){
res[key] = deepCopy(source[key],hash);
}else{
// 如果该key的value值不是对象,则把参数对象key的value值赋给返回值的key
res[key] = source[key];
}
}
}
// 返回返回值
return res;
};
// 测试
let obj1 = {
name:'obj.name',
un:undefined,
nu:null,
arr: [1,2,3],
sy:Symbol(123),
say:function(){
console.log(this.name);
},
reg:/\d{6}/g,
date:new Date(),
child:{
name:'child.name'
}
}
obj1.child.child= obj1.child;
let obj2 = deepCopy(obj1);
console.log(obj1);
console.log(obj2);
console.log(obj2.sy === obj1.sy)
console.log(obj2.arr === obj1.arr)
obj2.name = 'obj2.name';
obj2.say();
# 数组去重,数组乱序
// 数组去重
1. [...new Set(arr)]
2. indexOf+遍历
3. 有序数组,不引用第三方变量,遍历+判断相邻元素是否相等+splice
// 数组乱序
arr.sort(()=>Math.random()-.5)
# 手写call, apply和bind
Object.prototype.myCall = function(context){
if(typeof this != 'function'){
throw new TypeError('this is not a function')
}
context.fn = this;
var result= [];
var args = [];
for(var i = 1; i< arguments.length; i++){
args.push('arr[' + i + ']')
}
result = eval('context.fn(' +args+ ')');
delete context.fn;
return result;
}
Object.prototype.myApply = function(context, arr){
if(typeof this != 'function'){
throw new TypeError('this is not a function')
}
context.fn = this;
var result= [];
if(!arr){
result = context.fn()
}else{
var args = [];
for(var i = 0; i< arr.length; i++){
args.push('arr[' + i + ']')
}
result = eval('context.fn(' +args+ ')');
}
delete context.fn;
return result;
}
Object.prototype.myBind = function(){
if(typeof this != 'function'){
throw new TypeError('this is not a function')
}
var self = this;
var args = Array.prototype.slice.call(arguments,1);
var F = function(){};
F.prototype = this.prototype;
var bound = function(){
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(this instanceof F ? this: context, args.concat(bindArgs))
};
bound.prototype = new F();
return bound;
}
# 继承ES5, ES6
1. 原型链
实例,构造函数,原型对象
2. new的过程, 构造函数Func, 实例obj
var obj = new Func();
相等于
var obj = null;
obj.__propto__ = Func.proptotype;
Func.call(obj);
3. 无new实例化
Func(){
if (!this instanceof Func){
return new Func(arguments);
}
}
# sleep函数
// 链式调用+无new实例化+event loop
// 1. 测试构造函数
Person('li');
// This is li
// 2. 测试sleep
Person('li').sleep(10).eat('danner')
// This is li
// ...等待10ms
// sleep after 10
// eat danner
// 3. 测试sleepFirst和sleep
Person('li').sleepFirst(5000).eat('danner').sleep(3000).eat('food').sleep(3000).eat(123);
// ...等待5s
// sleep before 5000
// This is li
// eat dinner
// sleep after 3000
// eat food
// sleep after 3000
// eat 123
function Person(name){
if (this instanceof Person){
// 设置任务队列
this.queues = [];
// 把初始化的任务放入队列
this.queues.push({
delay: 0,
cb: ()=> console.log(`This is ${name}`)
})
// 异步调用run方法执行队列
setTimeout(()=>{
this.run();
});
}else{
// 无new实例化
return new Person(name);
}
}
Person.prototype.run = async function(){
// 调用for循环阻塞任务队列的执行
for (let i=0, len=this.queues.length; i<len; i++){
let item = this.queues[i];
if (item.delay){
await this.toPromise(item.cb, item.delay)();
}else{
await item.cb();
}
}
}
Person.prototype.toPromise = function(cb, delay){
// 把一个有延迟的函数转化为promise,延迟时间为状态改为resolve的时间
return function(){
return new Promise((resolve, reject)=>{
setTimeout(function(){
cb();
resolve();
}, delay);
})
}
}
Person.prototype.sleep = function(delay){
this.queues.push({
delay,
cb: ()=>console.log(`sleep after ${delay}`)
})
return this;
}
Person.prototype.eat = function(food){
this.queues.push({
delay: 0,
cb: ()=>console.log(`eat ${food}`)
})
return this;
}
Person.prototype.sleepFirst = function(delay){
this.queues.unshift({
delay: delay,
cb: ()=>console.log(`sleep before ${delay}`)
})
return this;
}
# 实现promise
- 一. 先执行Promise的参数,保存结果,当调用then的时候执行真正的回调
- 二. 利用异步让Promise构造函数内部的resolve和reject晚于真正的回调执行
# 原生Promise的实现
class MyPromise{
static FULFILLED = 'fulfilled';
static PENDING = 'pending';
static REJECTED = 'rejected';
constructor(promiseCbk) {
this.state = MyPromise.PENDING;
this.value = null;
this.allCbk = [];
try{
promiseCbk(this.resolve.bind(this), this.reject.bind(this))
}catch(error) {
this.reject(error)
}
}
resolve(value) {
if (this.state === MyPromise.PENDING) {
this.state = MyPromise.FULFILLED;
this.value = value;
setTimeout(() => {
this.allCbk.forEach(item => {
item.onFulfilled(this.value)
})
})
}
}
reject(value) {
if (this.state === MyPromise.PENDING) {
this.state = MyPromise.REJECTED;
this.value = value
setTimeout(() => {
this.allCbk.forEach(item => {
item.onRejected(this.value)
})
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = () => this.value
}
if (typeof onRejected !== 'function') {
onRejected = () => this.value
}
return new MyPromise((resolve, reject) => {
if (this.state === MyPromise.PENDING) {
this.allCbk.push({
onFulfilled: (value) => {
try{
let res = onFulfilled(this.value);
resolve(res)
}catch(error) {
onRejected(error)
}
},
onRejected: (value) => {
try{
let res = onRejected(this.value)
resolve(res)
}catch(error) {
onRejected(error)
}
}
})
}
if (this.state === MyPromise.FULFILLED) {
setTimeout(() => {
try{
let res = onFulfilled(this.value);
resolve(res)
}catch(error){
onRejected(error)
}
})
}
if (this.state === MyPromise.REJECTED) {
setTimeout(() => {
try{
let res = onRejected(this.value)
resolve(res)
}catch(error) {
onRejected(error)
}
})
}
})
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
static reject(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject)
} else {
reject(value)
}
})
}
static all(promises) {
const allReslut = [];
return new MyPromise((resolve, reject) => {
promises.forEach(item => {
item.then(trueRes => {
allReslut.push(trueRes);
if (allReslut.length === promises.length) {
resolve(allReslut)
}
}, falseRes => {
reject(falseRes)
})
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(item => {
item.then(trueRes => {
resolve(trueRes)
}, falseRes => {
reject(falseRes)
})
})
})
}
}
# Promise.all,思路就是轮询
Promise.all = function(arr){
return new Promise((resolve,reject) => {
if(!Array.isArray(arr)){
throw new TypeError(`argument must be a array`)
}
var length = arr.length;
var resolveNum = 0;
var resolveResult = [];
for(let i = 0; i < length; i++){
arr[i].then(data => {
resolveNum++;
resolveResult.push(data)
if(resolveNum == length){
return resolve(resolveResult)
}
}).catch(data => {
return reject(data)
})
}
})
}
# Promise.retry
Promise.retry = function(fn, times, delay) {
return new Promise(function(resolve, reject){
var error;
var attempt = function() {
if (times == 0) {
reject(error);
} else {
fn().then(resolve)
.catch(function(e){
times--;
error = e;
setTimeout(function(){attempt()}, delay);
});
}
};
attempt();
});
};
# eventEmitter|发布订阅|Dom三级事件
class EventEmitter{
store = {}
on(type, cb, once=false){
if (this.store[type] && this.store[type].cb.length){
this.store[type].cb.push(cb);
}else{
this.store[type] = {cb: [cb], once}
}
}
emit(type, payload){
if (this.store[type]){
this.store[type].cb.forEach(item=>{
item(payload);
})
if (this.store[type].once){
delete this.store[type];
}
}
}
off(type, cb){
if (this.store[type]){
var index = this.store[type].cb.findIndex(item=>item===cb);
this.store[type].cb.splice(index, 1);
}
}
once(type, cb){
this.on(type, cb, true);
}
}
# 实现instanceof
function myInstanceof(left,right){
// 实例的原型对象
var proto = left.__proto__;
// 构造函数的原型对象
var protoType = right.prototype;
while(true){
if(proto === null){
return false
}
if(proto == protoType){
return true
}
proto = proto.__proto__
}
}
# 实现数组flat,filter
// 数组扁平化
toString().split(',')
// 展平一级
function flat(arr){
var result = [];
for(var i = 0; i < arr.length; i++){
if(Array.isArray(arr[i])){
result = result.concat(flat(arr[i]))
}else{
result.push(arr[i]);
}
}
return result;
}
//展平多层
function flattenByDeep(array,deep){
var result = [];
for(var i = 0 ; i < array.length; i++){
if(Array.isArray(array[i]) && deep >= 1){
result = result.concat(flattenByDeep(array[i],deep -1))
}else{
result.push(array[i])
}
}
return result;
}
// 数组过滤
Array.prototype.myFilter = function(fn,context){
if(typeof fn != 'function'){
throw new TypeError(`${fn} is not a function`)
}
let arr = this;
let reuslt = []
for(var i = 0;i < arr.length; i++){
let temp= fn.call(context,arr[i],i,arr);
if(temp){
result.push(arr[i]);
}
}
return result
}
Array.prototype.myForeach = function(fn,context){
if(typeof fn != 'function'){
throw new TypeError(`${fn} is not a function`)
}
let arr = this;
for(var i = 0;i < arr.length; i++){
fn.call(context,arr[i],i,arr);
}
}
Array.prototype.myMap = function(fn,context){
if(typeof fn != 'function'){
throw new TypeError(`${fn} is not a function`)
}
let arr = this;
let reuslt = []
for(var i = 0;i < arr.length; i++){
result.push(fn.call(context,arr[i],i,arr));
}
return result;
}
# 函数柯里化
function currying(fn,...args){
if(fn.length <= args.length){
return fn(...args)
}
return function(...args1){
return currying(fn,...args,...args1)
}
}
function add(a,b,c){
return a + b + c
}
add(1,2,3) // 6
var curryingAdd = currying(add);
curryingAdd(1)(2)(3) // 6
// 柯里化+toString隐式转换
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
add(1)(2)(3) // 6
add(1, 2, 3)(4) // 10
add(1)(2)(3)(4)(5) // 15
add(2, 6)(1) // 9
# EventLoop
任务队列分为同步任务,宏任务(定时器),微任务(Promise) 执行优先级:同步>异步,微任务>宏任务 执行顺序:按照加入队列的顺序
console.log(1); // 1.第一个同步任务
setTimeout(() => {
console.log(2); // 7.第一个宏任务
Promise.resolve().then(() => {
console.log(3) // 8.第一个宏任务中的微任务
});
});
new Promise((resolve, reject) => {
console.log(4) // 2.第二个同步立即执行的同步任务
resolve(5)
}).then((data) => {
console.log(data); // 4. 第一个微任务
Promise.resolve().then(() => {
console.log(6) // 5. 第二个微任务
}).then(() => {
console.log(7) // 6.第三个微任务
setTimeout(() => {
console.log(8) //10. 第三个宏任务
}, 0);
});
})
setTimeout(() => {
console.log(9); // 9. 第二个宏任务
})
console.log(10); // 3.同步任务