前端面试题总结

关于前端面试题总结

vue

vue双向数据绑定原理,又称vue响应式原理

是通过数据劫持结合发布者-订阅者模式的方式来实现的。其核心就是通过Object.defineProperty()方法设置set和get函数来实现数据的劫持,在数据变化时发布消息给订阅者,触发相应的监听回调。

watch和watchEffect有什么区别?

  • 执行时机: watchEffect是立即执行的,在页面加载时会主动执行一次;而watch它不会立即执行,但可以配置 immediate,使其主动触发
  • 参数不同: watchEffect只需要传递一个回调函数,不需要传递侦听的数据,它会在页面加载时主动执行一次,来收集依赖;而watch至少要有两个参数(第三个参数是配置项),第一个参数是侦听的数据,第二个参数是回调函数
  • 结果不同: watchEffect获取不到更改前的值;而watch可以同时获取更改前和更改后的值

谈一谈你对Mixin混入的理解?

mixin就是将这些多个相同的逻辑抽离出来,各个组件只需要引入mixin,就能实现写一次代码,各个组件都能用且不会相互影响

  • 在生命周期中: mixin的生命周期会先执行,其次执行组件的生命周期
  • 有数据名或者方法名同名时: 会优先使用组件中的数据或方法名,mixin就会无效

原理分析:

  • 优先递归处理mixins;
  • 先遍历合并parent中的key,调用mergeField方法进行合并,然后保存在变量options;
  • 再遍历child,合并补上parent中没有的key,调用mergeField方法进行合并,保存在变量options;
  • 通过mergeField函数进行合并;
  • 合并策略包括:替换型、合并型、队列型、叠加型;
  • 替换行策略有:props、methods、inject、computed;
  • 合并型策略是data,通过set方法进行合并和重新赋值,就是将新的同名参数替代旧的参数;
  • 队列型策略有生命周期函数和watch,原理是将函数存入一个数据,然后正序遍历依次执行。
  • 叠加型有component、directives、filters,通过原型链进行层层的叠加

Vue 的 nextTick 的原理是什么?

1为什么需要 nextTick

 Vue 是异步修改 DOM 的并且不鼓励开发者直接接触 DOM,但有时候业务需要必须对数据更改--刷新后的 DOM 做相应的处理
 这时候就可以使用 Vue.nextTick(callback)这个 api 了。

2.知识储备(可以不说,但是自己要知道,以防不测)

事件循环中宏任务和微任务这两个概念

常见的宏任务有 script, setTimeout, setInterval, setImmediate(一种执行更加频繁的定时器)

常见的微任务有 ,Promise.then(), async

3.最终答案:

nextTick 的原理是 vue 通过异步队列控制 DOM 更新

nextTick底层是promise,所以是微任务。这个一定要知道

keep-alive的理解

作用: 用来缓存组件内部的状态的,避免重复渲染
生命周期: 被keep-alive包含的组件中,会多出来两个生命周期:activated 与 deactivated。

  • activated: 组件第一次渲染的时候会调用,以及每次激活的时候会调用
  • activated调用时机: 第一次时会在mounted之后、beforeRouteEnter守卫传给 next 的回调函数之前调用,并且给因为组件被缓存了,再次进入缓存路由、组件时,不会触发这些钩子函数,beforeCreate created beforeMount mounted 都不会触发
  • deactivated: 组件离开路由的时候会调用

vue兄弟组件传值

通过中间事件来进行传值,event bus 通过new vue来实现,然后$emit和 $on方式来传值
vuex

路由传值的方式有哪几种

在这里插入图片描述

v-if和v-show的区别

我是这么理解v-if和v-show的,其实v-if的本质就是动态创建和删除元素节点嘛,我一般遇到切换不频繁的话就用v-if,频繁的话就用v-show 因为v-if频繁切换的话会大量的创建和删除元素,会消耗性能。v-show的话就是控制css样式加了一个display: none;

v-if和v-for的优先级

1.在vue2中,v-for的优先级高于v-if,把它们放在同一个标签上时,页面每次渲染的时候都会重复的进行判断是十分消耗性能的

2.在vue3中,又恰好相反v-if的优先级是高于v-for的,同样vue3也不能把它们两者写在一起,正因为v-if优先于v-for,并且v-if又依赖v-for的数据源,在这个情况下将会出现报错的情况。

3.一般我们在处理v-if和v-for连用的场景时我们可采取下列方式:

(1)在循环渲染之前先用filter过滤下不需要的数据,也可以使用computed计算属性进行过滤

(2)如果需要根据条件渲染单个项,可以将 v-if 放在内层元素上。

疑问:将v-for和v-if写在同层级和v-for中嵌套v-if有什么区别呢?

回答:将 v-if 和 v-for 写在同一层级时,Vue 会在渲染组件时,先根据 v-for 遍历出所有的子元素,并将它们都生成对应的 Virtual DOM,然后再根据 v-if 条件判断是否需要将这些元素渲染出来。如果元素不符合条件,则将它们对应的 Virtual DOM 从渲染队列中移除。
将 v-if 放在内层元素上时,Vue 可以先根据 v-for 遍历出所有的子元素,但只有符合 v-if 条件的子元素会被渲染,不符合条件的子元素不会生成对应的 Virtual DOM。这样可以避免不必要的性能消耗,提高页面渲染速度。

Vue 组件 data 为什么必须是函数

  • 因为组件是需要在多个地方使用的
    • 如果data是一个对象,对象是引用类型。 一旦某一个地方修改,就会全部修改
    • ata是一个函数,每一次复用组件的时候就会从这个函数返回一个新的对象。 这样组件在复用的时候就可以做到数据互不干扰。

vue中key值作用

vue在渲染的时候他会比较新的dom和旧的dom是否一致,如果结构一致的话呢vue会复用旧的dom,如果加key的话可以给dom添加一个唯一标识,让vue强制更新dom

vuex的使用和解释

里面有state用来保存数据的,mutations是用来修改state的状态的它里面有两个参数,一个是state还有一个是传入的新值,actions是用来进行异步操作的,modules就是属于把vue进行了一个模块化了提高了可维护性
mutations用this.$store.commit(),actions用this. $store.dispath()来操作数据

vue-router中router和route的区别

router是路由实例对象,他其中包含路由跳转的方法
route是路由信息对象,他包含路由相关的一些信息

Vue中的常见指令有那些?

v-text/v-html/v-for/v-show/v-if/v-else/v-bind/v-on/v-model

vue单页面应用无刷新更新组件怎么实现的

其实vue-router有两种模式hash模式和history模式
hash模式是v-router的默认模式,他不会访问服务器获取页面,他只会修改访问历史记录。
history模式和hash模式的区别在于history模式地址栏不带‘#’号
history模式在用户输入地址时会访问服务器
history还提供了两个方法,pushState和replaceState修改浏览器历史记录
以及popState事件监听状态变化

vue中diff算法的原理

就是在数据发生变化的时候,vue是先根据真实的dom生成一个虚拟dom,当虚拟dom某个节点的数据改变的时候会生成一个新的vnode,然后新的vnode和旧的vnode比较,如果有不一样的地方就直接修改真实的dom上,然后把旧VNode改成新的VNode来实现一个更新的

vue-router 有哪几种导航钩子?

1.全局导航钩子:router.beforeEach(to,from,next)作用:跳转前进行判断拦截、组件内的钩子、单独路由独享组件
2、路由独享钩子可以在路由配置上直接定义 beforeEnter
3、组件内的导航钩子有三种:
beforeRouteEnter 在进入当前组件对应的路由前调用
beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave 在离开当前组件对应的路由前调用

Vue实例的生命周期讲一下, mounted阶段真实DOM存在了嘛?

Vue实例从创建到销毁的过程,就是生命周期。
也就是:开始创建->初始化数据->编译模板->挂载dom->数据更新重新渲染虚拟 dom->最后销毁。这一系列的过程就是vue的生命周期。所以在mounted阶段真实的DOM就已经存在了。
beforeCreate:vue实例的挂载元素el和数据对象data都还没有进行初始化,还是一个 undefined状态
created: 此时vue实例的数据对象data已经有了,可以访问里面的数据和方法, el还没有,也没有挂载dom
beforeMount: 在这里vue实例的元素el和数据对象都有了,只不过在挂载之前还是虚拟的dom节点
mounted: vue实例已经挂在到真实的dom上,可以通过对 dom操作来获取dom节点
beforeUpdate: 响应式数据更新时调用,发生在虚拟dom打补丁之前,适合在更新之前访问现有的 dom,比如手动移除已添加的事件监听器
updated: 虚拟dom重新渲染和打补丁之后调用,组成新的 dom已经更新,避免在这个钩子函数中操作数据,防止死循环。
activated: 当组件keep-alive激活时被调用
deactivated:当组件keep-alive停用时被调用
beforeDestroy: vue实例在销毁前调用,在这里还可以使用,通过this也能访问到实例,可以在这里对一些不用的定时器进行清除,解绑事件。
destroyed:vue实例销毁后调用,调用后所有事件监听器会被移除,所有的子实例都会被销毁。

vue 如何自定义指令?具体的 api 写法?

自定义指令directive方法里面有两个参数,一个是指令的名称,一个是函数,里面的钩子函数有bind、inserted(节点插入的时候触发)、updated(更新时触发)

Vue.directive('focus', {
	//第一个参数永远是el,表示原生的js对象
	bind: function (el) { //当指令绑定到元素上的时候,会立即执行bind函数,只执行一次,此时元素还没有插入到DOM中,focus聚焦此时不会生效
		el.focus()
	},
	inserted: function (el) { //当元素插入到DOM中的时候,会执行inserted函数,只执行一次
		el.focus()
	},
	updated: function () { //当VNode的时候,会执行updated函数,可能出发多次
	}
});

<input type="text" v-focus />

webpack

1、webpack的作用是什么,谈谈你对它的理解?
现在的前端网页功能丰富,特别是SPA(single page web application 单页应用)技术流行后,JavaScript的复杂度增加和需要一大堆依赖包,还需要解决Scss,Less……新增样式的扩展写法的编译工作。
所以现代化的前端已经完全依赖于webpack的辅助了。
现在最流行的三个前端框架,可以说和webpack已经紧密相连,框架官方都推出了和自身框架依赖的webpack构建工具。
react.js+WebPack
vue.js+WebPack
AngluarJS+WebPack
2、webpack的工作原理?
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Sass,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。在3.0出现后,Webpack还肩负起了优化项目的责任。
3、webpack打包原理
把一切都视为模块:不管是 css、JS、Image 还是 html 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。
按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载
4、webpack的核心概念
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。
5、Webpack的基本功能有哪些?
代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
6、gulp/grunt 与 webpack的区别是什么?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。grunt和gulp是基于任务和流(Task、Stream)的。
类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。webpack是基于入口的。
webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
7、webpack是解决什么问题而生的?
如果像以前开发时一个html文件可能会引用十几个js文件,而且顺序还不能乱,因为它们存在依赖关系,同时对于ES6+等新的语法,less, sass等CSS预处理都不能很好的解决……,此时就需要一个处理这些问题的工具。

小程序相关

wxs与wx js有什么区别

wxs可以在wxml中使用,并且可以在wxml中调用
书写格式:

<wxs module="math" src="utils/math.wxs"></wxs>
<view>{{ math.sum(1, 2) }}</view>

JS相关

数组有哪些常用方法,引出下一个问题,slice和splice区别

push, pop, shift unshift
1.splice改变原数组,slice不改变原数组。2.splice除了可以删除之外,还可以插入。3.splice可传入3个参数,slice接受2个参数。

instanceof原理

a instanceof B
检测a的原型链(_ proto _)上有没有b的prototype,有就返回true,没有false

new对象的过程

1.创建一个空对象
2.将空对象的原型指向原来的对象原型
3.为新对象绑定新的this改变this指向
4.判断结果是否为null,如果是null的话就返回新对象,如果不是就返回执行结果

手写new

//定义构造函数
    function Fun(age, name) {
      this.age = age
      this.name = name
      return 1
    }

    function myNew(fn, ...args) {
      //1、先创造空对象
      //其实等于var obj = Object.create({})  
      var obj = {}
      //2、obj的__proto__指向原型
      Object.setPrototypeOf(obj, fn.prototype)
      //3、改变this指向,执行构造函数内部函数
      var result = fn.apply(obj, args)
      //4、判断return
      return result instanceof Object ? result : obj
    }

浏览器渲染机制

首先html代码会转化成dom
然后css代码转化成cssom(CSS Object Model)
然后结合dom和cssom生成一个渲染树
生成布局最后绘制在屏幕上

请描述一下 cookies sessionStorage和localstorage区别

他们区别在于存储大小不一样
cookies只能存储4k,sessionStorage和localstorage可以存储5兆甚至更多
还有存储时间不一样
localstorage是关闭浏览器也会存在,除非手动删除
sessionStorage关闭浏览器就会自动删除
cookies是根据设置的过期时间存储

什么是BFC,什么创建?有什么作用

BFC其实就是块级格式化上下文,让元素成为一个独立的空间不受外界影响也不影响外面布局

如何创建:给父级元素添加样式:
overflow:hidden
display:flex
position: absolute;
position: fixed;

作用:
其实就是解决当父级元素没有高度的时候,子元素浮动会使父元素高度塌陷的问题

var let const 的区别?

var可以重复声明,全局作用域,有变量提升

let和const是块级作用域,没有变量提升

const不能被赋值

promise理解、手写promise

Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,避免了地狱回调
Promise有三种状态: pending(等待态),fulfilled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
Promise拥有一个then方法,用以处理resolved或rejected状态下的值
then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调。
then方法返回一个新的Promise对象,因此可以通过链式调用then方法

function xxx(onResolved) {
  // 这里叫做promise2
  return new Promise((resolve,inject) => {
 
    //计时器模拟请求
    setTimeout(()=>{
         if(){
             resolve(res)         
         } else{
             inject(err)         
         }  
    })
     
 
  });
};
xx.then(fun,fun)

Promise.all和Promise.race的区别,应用场景

all()方法可以完成并行任务, 它接收一个数组,数组的每一项都是一个 promise对象。当数组中所有的 promise的状态都达到 resolved的时候,all方法的状态就会变成 resolved,如果有一个状态变成了 rejected,那么 all方法的状态就会变成 rejected。

race() 接受的参数也是一个每项都是 promise的数组,当最先执行完的事件执行完之后,就直接返回该 promise对象的值。如果第一个 promise对象状态变成 resolved,那自身的状态变成了resolved;反之第一个 promise变成 rejected,那自身状态就会变成 rejected。

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

async await函数

async/await函数是异步代码的新方式
async/await是基于promise实现的
async/await使异步代码更像同步代码
await 只能在async函数中使用,不能再普通函数中使用,要成对出现
默认返回一个promise实例,不能被改变
await下面的代码是异步,后面的代码是同步的

微任务和宏任务的区别

宏任务和微任务是事件循环的两种类型
宏任务一般是处理相对较大的任务,比如定时器,永久定时器、网络请求等
微任务是处理较小的任务
微任务执行时机是比宏任务高的

微任务:process.nextTick、MutationObserver、Promise.then catch finally
宏任务:setTimeout、setInterval、setImmediate、requestAnimationFrame

作用域和作用域链

作用域就是变量和函数起的作用范围

作用域链就是通过js查找的方式变量和函数向上查找的范围

判断数据类型的方法有哪些,有什么区别

  1. typeof:一般判断基本数据类型

  2. instanceof :一般判断引用数据类型,主要的作用就是判断一个实例是否属于某种类型,或者判断一个实例是否是其父类型或者祖先类型的实例。(原型链知识)

  3. constructor:通过原型链继承属性判断。null和undefined是无效的对象,JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object。

  4. Object.prototype.toString.call():较为准确的判断方法。toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型。

原型、原型链?

原型:每个对象中都会有一个固定属性:_ proto__,这个属性指向对象的prototype原型属性

原型链:当访问一个对象的属性的时候,如果这个对象不存在这个属性,那么他就会去他的原型对象上去找,一直找下去,直到找到null为止

this指向问题

  1. 当this出现在全局函数的时候,他指向Window
  2. 当一个函数为对象的属性的时候,他指向这个对象
  3. 当元素绑定事件的时候他会指向被点击的元素

改变this指向(call、apply、bind)

1、apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;
2、apply 、 call 、bind 三者第一个参数都是this要指向的对象,也就是想指定的上下文;
3、apply 、 call 两者都可以利用后续参数传参; 但是传参的方式不一样,apply是数组,call是正常传参形式
4、bind 是返回对应函数,便于稍后调用;apply 、call 则是立即调用。

闭包、闭包的使用场景

闭包就是内部函数可以访问外部函数的变量,他的特点就是函数内再嵌套函数,参数和变量不会被垃圾回收机制回收

缺点:可能会会导致内存泄漏

最常用的场景就是节流和防抖

节流和防抖

防抖

debounce(function () {
	console.log(..)
}, 2000)
debounce(fn, delay) {
	let timer = null;
	return function () {
		if ( timer !== null ) {
			clearTimeout(timer)
		}
		timer = setTimeout(() => {
			fn.call(this);
		}, delay);
	}
}

节流

 
const throttle=(func, delay) => {
	// 缓存一个定时器
	let timer = null
	// 这里返回的函数是每次用户实际调用的节流函数 
	return function(...args) {
		if (!timer) { //判断timer是否有值,如果没有则说明定时器不存在即可继续执行
			timer = setTimeout(() => { //关
				func.apply(this, arguments)
				timer = null; //开
			}, delay)
		}
	}
}
export default throttle

函数防抖:在多次触发事件时,只触发最后一次

函数节流:在多次触发事件时,会减少触发的频率

如何解决内存泄漏?

  1. 避免创建全局的变量
  2. 用定时器时记得清除
  3. 移除事件监听
  4. 使用严格模式

严格模式

  1. 变量需要定义
  2. 禁止使用with语句
  3. 禁止this指向全局变量

js继承方式有哪些?

  • 原型链继承 核心: 将父类的实例作为子类的原型
  • class类继承
  • 借用构造函数(call):在子类构造函数中使用 call() 或 apply() 方法调用父类构造函数,并将子类实例作为参数传递给父类构造函数
  • 组合继承(组合原型链继承和借用构造函数继承):可以继承父类原型上的属性,可以传参,可复用

数组遍历的方法和区别

  1. forEach:遍历开始以后无法停止,如果要遍历整个数组,那就使用这个方法;

  2. map:根据当前数组映射出一个新的数组;

  3. some:遍历整个数组,返回值true就停止循环(返回false继续循环)

     返回值:如果数组中的有一项回调函数返回true,那么结果为true,否则为false;(或者这样理解:数组别遍历完,那么结果为false,否则为true)
    
  4. every:与some相反,返回false就停止循环(返回true就继续循环)

  5. filter:过滤数组,返回一个新的数组

在浏览器中输入地址之后发生了什么过程?

  1. 域名解析,他会先从浏览器缓存里面找,如果找不到就会使用服务器来解析域名
  2. 建立TCP连接 三次握手
  3. 发出请求
  4. 响应请求
  5. 解析html
  6. 呈现页面

三次握手

  1. 第一次握手:客户端向服务器发送SYN同步报文段,请求建立连接
  2. 第二次握手:服务器确认收到客户端的连接请求,并向客户端发送SYN同步报文,表示要向客户端建立连接
  3. 第三次握手:客户端收到服务器端的确认请求后,处于建立连接状态,向服务器发送确认报文,客户端是在收到确认请求后,先建立连接,服务器是在收到最后客户端的确认后,建立连接,发起连接请求的一定是客户端

回流和重绘

回流就是因为元素的尺寸位置发生了变化需要重新排列
重绘就是元素更新了外观,不影响布局

什么时候发生重绘?
背景色改变
样式发生改变的时候

区别
回流必定会引起重绘,重绘一定不会引起回流

JSONP的原理,以及为什么不是真正的 ajax

Jsonp是通过动态创建 script 标签,然后通过标签的 src 属性获取 js 文件 中的 js 脚本,该脚本的内容是一个函数调用,参数就是服务器返回的数据,为了处理这些 返回的数据,需要事先在页面定义好回调函数,本质上使用的并不是 ajax 技术

ajax

原理: Ajax就是通过XmlHttpRequest对象来向服务器发送异步请求,从服务器获得数据,然后用js来操作DOM而更新页面
作用: ajax用来与后台交互
常用的请求方式:post、get、put

  • XMLHttpRequest是ajax的核心机制,是一种支持异步请求的技术。就是js可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。

实现步骤

//创建ajax对象
 	var xhr = new XMLHttpRequest();
//告诉 Ajax 请求地址以及请求方式
	xhr.open('get', 'http://www.example.com');
//发送请求
	xhr.send();
//获取服务器端给与客户端的响应数据
 	xhr.onload = function () {
     	console.log(xhr.responseText);
 	}

ajax和axios的区别

Axios和Ajax都是用于浏览器和服务器之间进行异步数据交换的技术
Axios是一个基于Promise的HTTP库,而Ajax是对原生XHR的封装

import axios from 'axios';
axios.get('/api/data')
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);

websocket

其实是HTML5一种新的协议
特点 : 服务器可以主动向客户端推送消息,客户端也可以主动向服务器发送信息

http和websocket的区别 http协议是短链接,因为请求之后,都会关闭连接,下次请求需要重新打开链接。websocket协议是一种长连接,只需要通过一次请求来初始化连接,然后所有请求和响应都是通过TCP链接进行通信。

JavaScript的同源策略

协议(protocol)相同: 同为https或http
端口相同: 如未指定,http默认为80端口,https默认为443端口
域名相同

前端项目性能优化方案有哪些

精灵图合并,减少HTTP请求;压缩HTML、CSS、JavaScript文件;

http缓存机制

HTTP 缓存主要由 强缓存(强制缓存)和协商缓存
强制缓存在规定时间内有效,可直接使用,且状态码为 200,若已失效则执行协商缓存
协商缓存利用发送给服务器特定字段,由服务器对字段进行校验,来确定是否使用缓存,可使用则状态码为 304

深拷贝和浅拷贝

浅拷贝: 拷贝了一个对象,但是只拷贝的对象的第一层,当对象的属性是引用类型,拷贝的这个属性,依旧是拷贝的地址引用。
深拷贝: 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。

  let obj = {
    name: 'lwf',
    color: ['红色', '蓝色']
  }

  let o = {}
//浅拷贝
  copy = (newObj, oldObj) => {
    for (let k in oldObj) {
      newObj[k] = oldObj[k]
    }
  }

  //深拷贝
   deepCopy = (newObj, oldObj) => {
     //遍历旧的对象
     for (let k in oldObj) {
       //判断k的值是不是数组,数组先判断是因为数组也是Object类型
       if (oldObj[k] instanceof Array) {
         newObj[k] = []
         deepCopy(newObj[k], oldObj[k])
       } else if (oldObj[k] instanceof Object) { //判断是不是对象
         newObj[k] = {}
         deepCopy(newObj[k], oldObj[k])
       } else {
         newObj[k] = oldObj[k]
       }
     }
   }

  deepCopy(o, obj)
  copy(o, obj)

  o.name = 'cll'
  o.color.push('绿色')
  console.log(o);
  console.log(obj);

简单说一下什么是堆什么是栈

  • 堆和栈都是操作系统自带的一个内存空间;
  • 栈是有系统自动分配的内存空间,栈空间里面存储的是可以直接 访问的;
  • 堆是后期程序员可以开辟扩大的,一般由程序员主动分配才释放;
  • 栈优点:速度快;堆优点:内存大;

事件委托

事件委托其实就是把子元素的事件变更到父元素上,其实它的原理就是通过事件冒泡实现的,事件冒泡是从事件源到根节点,从内向外进行传播,如果想要阻止冒泡的话e.stopPropagation()

css相关

解决1px像素问题

原因: 2 倍图的 1px 其实要实现的是 0.5px,直接写 1px 会偏粗,但写 0.5 又会有兼容性问题

1.box-shadow(向元素添加阴影)

-webkit-box-shadow:0 1px 1px -1px rgba(255, 0, 0, 0.5);
box-shadow:0 1px 1px -1px rgba(255, 0, 0, 0.5);

2.伪元素+transform

.scale-1px{
  position: relative;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  bottom: 0;
  background: #000;
  width: 100%;
  height: 1px;
  /*核心是利用transform缩放边框*/
  -webkit-transform: scaleY(0.5);
  transform: scaleY(0.5);
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}

3.transform:scale(0.5)(旋转元素)

div { 
	 height: 1px; 
	 background: #000; 
	 transform: scaleY(0.5); 
	 transform-origin: 0 0;
}

Css实现单行文本、多行文本、截断

单行文本
div {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

什么叫优雅降级和渐进增强?

渐进增强: 一开始针对低版本的浏览器构建页面,然后向上兼容
优雅降级: 一开始针对高版本的浏览器构建页面,向下兼容

行内元素有哪些?块级元素有哪些? 空(void)元素有那些?行内元素和块级元素有什么区别?

行内元素: span、a、label、img、input、select、b、strong

块级元素: div、ul、ol、h1-h6、p、tabel、from、ol

空元素: br、img、hr、input、link、meta

区别:

块级元素会独占一行,宽度会自动填满父元素,相邻的行内元素会排列到一行,直到排不下才会换行,宽度随元素的内容而变化

块级元素可以设置宽高,行内元素设置宽高无效

块级元素可以设置margin、padding,行内元素margin、padding水平方向有效,垂直方向无效

媒体查询

@media screen and (max-width:1200px) { … } /* 中型设备(台式电脑,1200px 以下) /
小屏幕 768px-992px
@media screen and (max-width:992px){ … }/
小型设备(平板电脑,992px 以下) /
超小屏幕 768px屏幕以下
@media screen and (max-width:768px){ … }/
超小型设备(手机,768px 以下) */

手写代码

数组扁平化/拍平

方式1:使用基础的递归遍历
function flatten(arr) {
  let result = [];
  // 此处也可使用for...of遍历
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}
方式2:使用toString方法,再用split转为数组,再map成数字
var arr = [1,2,3,[4,5], [6,7,[8,9,[10,11]]]]
var newArr = arr.toString().split(',')
// arr.toString()结果为'1,2,3,4,5,6,7,8,9,10,11'
var resArr = newArr.map(item => Number(item))
console.log(resArr) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

方式3:直接使用ES6的flat方法
//falt()方法会按照指定的深度递归遍历数组,arr.flat([depth]),参数depth不填时默认值为1
//depth为Infinity表示展开任意深度的嵌套数组。
function flatten(arr) {
  return arr.flat(Infinity);
}

手写ajax原始请求

//1.创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
//2.规定请求的类型、URL 以及是否异步处理请求。
xhr.open('GET',url,true);
//3.发送信息至服务器时内容编码类型
//设置响应头
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
//4.发送请求
xhr.send(null);  
//5.接受服务器响应数据
xhr.onreadystatechange = function () {
    if(xhr.readyState ===4){
    //判断响应状态码  200 404 403 401 500
    //2xx 成功
      if(xhr.status >= 200 && xhr.status < 300){
      
      }else{
     
      }
   }
}

判断字符串回文

function checkStr(str) {
    return str === str.split('').reverse().join('')
}

命名方式中划线改小驼峰

function transName(arr) {
    let res = arr.map(e => {
        let items = e.split('-').map((item, index) => {
            if (index) {
                let first = item.substring(0,1)
                let rest = item.substring(1)
                return first.toUpperCase()+rest
            }else{
                return item.toLowerCase()
            }
        })
        return items.join('')
    })
    return res
}

数组去重

const res = Array.from(new Set(arr));

function unique1 (arr) {
    let newArray = []
    for (let i = 0; i < arr.length; i++) {
      if (newArray.indexOf(arr[i]) === -1) {
        newArray.push(arr[i])
      }
    }
    return newArray
  }

实现日期格式化函数

dateFormat(new Date('2020-12-01'), 'yyyy/MM/dd') // 2020/12/01
dateFormat(new Date('2020-04-01'), 'yyyy/MM/dd') // 2020/04/01
dateFormat(new Date('2020-04-01'), 'yyyy年MM月dd日') // 2020年04月01日
const dateFormat = (dateInput, format) => {
    var day = dateInput.getDate()
    var month = dateInput.getMonth() + 1
    var year = dateInput.getFullYear()
    format = format.replace(/yyyy/, year)
    format = format.replace(/MM/, month)
    format = format.replace(/dd/, day)
    return format
  }

将js对象转化为树形结构

// 转换前:
source = [{id: 1,pid: 0,name: 'body'}, {id: 2,pid: 1,name: 'title'}, {id: 3,pid: 2,name: 'div'}]
// 转换为: 
tree = [{id: 1,pid: 0,name: 'body',children: [{id: 2,pid: 1,name: 'title',children: [{id: 3,pid: 1,name: 'div'}]}}]
function jsonToTree(data) {
    if (!Array.isArray(data)) {
      return result
    }
    // 1. result为一个新的大厅,来存放已经组好队的家庭
    let result = []
    // 2.使用map,将当前对象的id与当前对象对应存储起来
    // PS:相当于我们在大厅喊话,只用一次就可以找到父亲,也就是O(1)
    let map = {}
    data.forEach(item => {
      map[item.id] = item
    })
    // 3.询问每一个人,它的父亲是谁
    data.forEach(item => {
      let parent = map[item.pid]
      // 如果有父亲,就把父亲喊出来,把孩子交给父亲
      if (parent) {
        (parent.children || (parent.children = [])).push(item)
      }
      else {
        // 如果没有父亲,说明这个人就是父亲,直接把他带到排好序的大厅
        result.push(item)
      }

    })
    // 新的大厅为已经排好的家庭
    return result
  }
  console.log(jsonToTree(source))
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值