总结(前端面试题)

CSS(3) & HTML(5) 篇

flex布局

​ 设为 Flex 布局以后,子元素的floatclearvertical-align属性将失效。

​ 1,Flex-direction:弹性容器中子元素排列方式(主轴排列方式)

​ 2,Flex-wrap:设置弹性盒子的子元素超出父容器时是否换行

​ 3,Flex-flow:是flex-direction和flex-wrap简写形式

​ 4,Align-item:设置弹性盒子元素在侧轴上的对齐方式

​ 5,Align-content:设置行对齐

​ 6,Justify-content:设置弹性盒子元素在主轴上的对齐方式

水平垂直居中

​ 1,子元素相对于父元素绝对定位,子元素top,left设置为50%,子元素的margin-top和margin-left减去各自宽高的一半

​ 2,子元素相对于父元素绝对定位,子元素上下左右全为0,子元素margin:auto

​ 3,父元素设置display:table-cell vertical-align:middle,子元素设置margin:auto

​ 4,子元素相对定位,子元素top,left值为50%,transform:translate(-50%,-50%)

​ 5,子元素相对父元素绝对定位,子元素top,left为50%,transform:translate(-50%,-50%)

​ 6,父元素设置弹性盒子,display:flex,justfy-content🤙enter,align-item:center

清除浮动

​ 高度塌陷:当所有的子元素浮动的时候,且父元素没有设置高度,这时候父元素就会产生高度塌陷。

​ 1,给父元素单独定义高度。 缺点:无法进行响应式布局

​ 2,父级定义overflow:hidden,zoom:1(针对ie6)。 缺点:超出部分被隐藏,布局时要注意

​ 3,给浮动元素后面添加一个空标签,clear:both,height:0,overflow:hidden

​ 缺点:增加空标签,不利于页面优化

​ 4,父级定义overflow:auto 缺点:内部宽高超过父级div时,会出现滚动条

​ 5,给塌陷的元素添加伪对象:

		.father :after{
			content:'';
			clear:both;
			display:block;
			height:0;
			overflow:hidden;
			visibility:hidden
		}
BFC

​ BFC 全称为 块格式化上下文。它是块级盒布局出现的区域,也是浮动层元素进行交互的区域。

​ 1,使BFC内部浮动元素不会到处乱跑

​ 2,和浮动元素产生边界

​ bfc特性:

​ 同一个bfc下margin会重叠

​ 计算bfc高度时会算上浮动元素

​ bfc不会影响到外部元素

​ bfc内部元素是垂直排列的

​ bfc区域不会与float元素重叠

​ 如何创建BFC

​ position:设为absolute或者fiexd

​ float不为none

​ overflow设置为hidden

​ display设置为inline-block或者inline-table或者flex

三栏布局

​ 两边固定中间自适应

​ 1,margin负值法:左右两栏均左浮动,左右两栏采用负得margin值。中间栏 被宽度为100%的浮动元素包起来

​ 2,自身浮动法:左栏左浮动,右栏右浮动,中间栏放最后

​ 3,绝对定位法:左右两栏采用绝对定位,分别固定于页面的左右两侧,中间的主体栏用左右margin值撑开

​ 4,flex 左右固定宽,中间flex:1

​ 5,网格布局

​ 6,table布局

两栏布局

​ 1,侧边栏左浮动+正文overflow:auto

​ 2,定位实现两栏布局

​ 3,纯浮动形式

​ 4,浮动+常规流

动画

​ 1,Animation-name为@keyframes动画名称

​ 2,animation-duration 动画需要花费的时间

​ 3,animation-timing-function 动画如何完成一个周期

​ 4,animation-delay 动画启动前的延迟时间

​ 5,animation-iteration-count 动画播放次数

​ 6,animation-direction 是否轮流反向播放动画

盒模型

​ 1,Content-box 标准盒模型 width不包括padding和border

​ 2,Border-box 怪异盒模型 width包括padding和border

HTML5新特性

​ 1,新增语义化标签

img

​ 2,新增表单类型

​ 3,表单元素

​ 4,表单属性

​ 5,表单事件

​ 6,多媒体标签:video,audio,source,embed,track

网页中有大量图片加载很慢 你有什么办法进行优化

​ 1,图片懒加载,在图片未可视区域加一个滚动条,判断图片的位置与浏览器顶端和页面的距离,如果前者小于后者,优先加载

​ 2,图片预加载,将当前展示图片的前一张和后一张先下载

​ 3,使用csssprite或者svgsprite

Px,rem,em的区别

​ 1,px,绝对长度单位,像素px是相对于显示器屏幕分辩率来说的

​ 2,em,相对长度单位,相当于当前对象内文本的字体大小

​ em的值并不是固定的,会继承父级元素的字体大小,字体大小参照与父元素的大小决定

​ 3,rem,相对于HTML根元素的font-size

​ 1em=1rem=16px

​ 在body中加入font-size:62.5% 这样直接就是原来的px数值除以10加上em就可以

请简述媒体查询

​ 媒体查询扩展了media属性,根据不同媒体类型设置不同的css样式,达到自适应的目的

Css继承特性主要是文本方面

​ 1,所有元素可继承:visibility,cursor

​ 2,块级元素可继承:text-indent,text-align

​ 3,列表元素可继承:list-style,list-style-type,list-style-position,list-style-image

​ 4,内联元素可继承:letter-spacing,word-spacing,line-height,color,font,font-family,font-size,Font-style,font-variant,font-weight,text-decoration,text-transform,direction

link标签和import标签的区别

​ 1,link属于HTML,而@import属于css

​ 2,页面被加载时,link会同时被加载,而@import引用的css会等页面加载完成后加载

​ 3,link是HTML标签,所有没有兼容性,而@import只有IE5以上可以识别

​ 4,link方式样式的权重高于@import

画一条0.5px的线

移动端开发时,由于屏幕retina,即高清屏幕, 当写 1px 时, 实际的线宽为 2px. 会显得很粗。此时就有了 0.5px 的需求: 主要应对 iPhone

<style>
    .parent{
        position:relative;
        &:after{
            /*绝对定位到父元素最低端,可以通过left/right的值控制边框长度或者定义width:100%*/
            position:absolute;
            bottom:0;
            left:0;
            right:0;
            /*初始化边框*/
            content:'';
            box-sizing:border-box;
            height:1px;
            border-bottom:1px solid rgba(0,0,0,0.2);
            /*以上代码实现了一个边框为1px的元素,下面实现0.5边框*/
            /*元素y方向缩小为原来的0.5*/
            transform:scaley(0.5); 
            /*css属性让你更改一个元素变形的原点*/
            transform-origin:0 0;
        }
    }
</style>
用css画一个三角形
<style>
    #triangle-up{
        width:0;
        height:0;
        border-left:50px solid transparent;
        border-right:50px solid transparent;
        border-bottom:100px solid red;
    }
</style>
<body>
    <div id="triangle-up"></div>
</body>

JS篇

Js中null与undefined区别

​ null:一个值被定义了,但是是空值

​ undefined:变量声明未赋值

​ 相同点:用if判断时,都会被转换成false

​ 不同点:number转换值不同。 number(null)==0,number(undefined)==NaN

继承

​ 说一说 JS 中的常用的继承方式有哪些?以及各个继承方式的优缺点

​ 1,原型继承,把父类的实例作为子类的原型

​ 缺点:子类的实例共享了父类构造函数的引用类型,不能传参

​ 2,组合继承,在子函数中运行父函数,要利用call改变this,再在子函数的prototype里面new father(),使farther的原型中的方法也得到继承,最后改变son的原型中的constructor

​ 缺点:调用两次父类的构造函数,造成了不必要的消耗,父类方法可复用

​ 优点:可以传参,不共享父类引用属性

​ 3,寄生组合继承,

​ 4,es6的extend 子类只有继承父类,可以不写constructor,一旦写了,则在constructor中的第一句话必须是super

原型

​ 原型分为隐式原型和显示原型,每个对象都有一个隐式原型,指向自己构造函数的显示原型

原型链

​ 多个_ _proto__组成的集合成为原型链。由多级父对象,逐级继承,形成的链式结果。

​ 所有实例的_ _proto__都指向他们构造函数的prototype

​ 所有的prototype都是对象,自然它的_ _proto__指向的是object()的prototype

​ 所有的构造函数的隐式原型指向Function()的显示原型

​ object的隐式原型是null

this指向

​ 1,obj.fun() this—>obj

​ 2,fun() , (function(){…})() , 多数的回调函数,定时器函数 this—>window

​ 3,new Fun() this—>new正在创建的新对象

​ 4,类项名.prototype.共有方法=function(){…} this—>将来谁调用指谁

​ 5,箭头函数中的this —>箭头函数外部作用域中的this

​ 6,DOM或JQ中事件处理函数中的this—>当前正在触发事件的DOM元素对象

​ 7,jQuery.fn.自定义函数=function(){…} this—>将来调用这个函数的.前的 JQ子对象

​ 8,new Vue()中 methods 中的函数中的this —>当前new Vue()对象

设计模式

​ 单例模式,不管创建多少个对象都只有一个模式

​ 工厂模式,代替new创建一个对象,这个对象想工厂制作一样 ,批量制作属性相同的实例对象

​ 构造函数模式,

​ 发布订阅者模式,

​ 迭代器模式,

​ 代理模式

call, apply, bind, new

​ 1,call实现思路:

​ 判断是否是函数调用,不是则抛异常

​ 通过新对象(context)来调用函数

​ 给context创建一个fn设置为需要调用的函数

​ 结束调用后删除fn

Function.prototype.myCall=function(context){
	//先判断调用myCall是否是一个函数
  	//这里的this就是调用myCall的
	if(typeof this !== 'function'){
		throw new TypeErrr('Not a Function')
	}
	//不传参默认为window
	contetx=context||window;
	//保存this
	context.fn=this;
	//保存参数
  	//Array.from把伪数组对象转化为数组
	let args=Array.from(arguments).slice(1);
	//调用函数
	let result=context.fn(...args);
	//删除fn
	delete context.fn;
	return result;
}

2,apply的实现思路:

​ 判断对象是否是函数调用,不是抛异常

​ 通过新对象(context)来调用函数

​ 给context创建一个fn设置为需要调用的函数

​ 结束调用后删除fn

Function.prototype.myApply=function(context){
	if(typeof this !=='function'){
		throw new TypeError('Not a Function')
	}
	 //不传参默认为window
	let context =context||window;
	//保存this
	context.fn=this;
	let result;
	
	// 需要判断是否存在第二个参数
	if(arguments[1]){
		result=context.fn(...arguments[1])
	}else{
		result=context.fn()
	}
	delete context.fn;
	return result;
}

3,bind 的实现思路:

​ 判断是否是函数调用,不是抛异常

​ 返回函数

​ 判断函数的调用方法,是否是被new出来的

​ new出来的返回空对象,但是实例的_ proto__指向 _this的 prototype

​ 完成函数柯里化

​ Array.prototype.slice.call()

Function.prototype.myBind=function(context){
	// 判断是否是一个函数
	if(typeof this !=='function'){
		throw new TypeError('Not a Function')
	}
	
	// 保存调用bind的函数
	const_this=this;
	// 保存参数
	const args=Array.prototype.slice.call(arguments,1)
	
	// 返回一个函数
	return function F(){
		// 判断是不是new出来的
		if(this instanceof F){
			// 如果是new出来的
      		// 返回一个空对象,且使创建出来的实例的__proto__指向_this的prototype,
      		//且完成函数柯里化
			return new_this(...args,...arguments);
		}else{
			// 如果不是new出来的改变this指向,且完成函数柯里化
			return _this.apply(context,args.concat(...arguments))
		}
	}
}

​ 4,new 的实现思路:

​ 字面量创建对象和new创建对象的区别,new内部都实现了什么

​ 字面量:更简单,方便阅读,不需要作用域解析,速度快

​ new内部:创建一个新对象,使用对象的_ _proto__指向原函数prototype

​ 改变this的指向,并执行该函数,执行结果保存起来,作为result

​ 判断执行函数的结果是不是null或者undefined,

​ 如果是返回之前的新对象,不是返回result

function myNew(fn,...args){
	 // 创建一个空对象
	let obj={};
	// 使空对象的隐式原型指向原函数的显式原型
	obj.__proto__=fn.prototype;
	// this指向obj
	let result=fn.apply(obj,args);
	//判断执行函数的结果是不是null或者undefined
	//如果是返回之前的新对象,不是返回result
	return result instanceof Object?result:obj;
}
防抖截流实现

​ 防抖:n秒后再执行该事件,若n秒内被重复触发,则重新计时

//防抖:只要不是最后一次触发,就不执行异步请求
function debounce(func,ms){
	let timer;
	return function(...args){
		//如果timer不是undefined,就清除,然后再次执行setTimeOut
		if(timer) clearTimeOut(timer);
		//重新开始下一轮等待
		timer=setTimeOut(()=>{
			func.apply(this,args)
		},ms)
	}
}

​ 节流:n秒内只运行一次,若在n秒内反复触发,只生效一次

//节流:第一次发送请求后,只要响应没有回来。就不能发送第二次请求
function throttle(func,ms){
	//开关变量
	let canRun=true;
	return function(...args){
		if(!canRun) return;
		//点击后,需要把开关关上
		canRun=false;
		setTimeOut(()=>{
			func.apply(this,args)
			canRun=true;
		},ms)
	}
}
var let const 有什么区别

​ var

​ var声明的变量可进行变量提升,let和const不会

​ var可以重复声明

​ var在非函数作用域中定义是挂载到window上的

​ let

​ let声明的变量只在局部起作用

​ let防止变量污染

​ let声明的变量不可再声明

​ const

​ 具有let的所有特征

​ 不可被修改

​ 如果使用const声明的是对象的话,可以修改对象里面的值

事件循环(event loop)

​ 1,执行宏任务script

​ 2,进入script后,所有的同步任务主线程执行

​ 3,所有的宏任务放入宏任务执行队列

​ 4,所有的微任务放入微任务执行队列

​ 5,先清空微任务队列

​ 6,再取一个宏任务执行,再清空微任务队列

​ 7,依次循环

​ 宏任务:script,setTimeOut,setInterval,setImmediate

​ 微任务:promise.then,process.nextTick,Object.observe,mutationObserver

promise使用及实现

​ promise对象代表一个异步操作,有三种状态:

​ 1,pending :初始状态,不是成功或者失败的状态

​ 2,fulfilled :意味操作成功完成

​ 3,rejected:意味操作失败

​ 优点:

​ 1,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,避免回调地狱

​ 2,promise对象提供统一的接口,使得控制异步操作更加容易

​ 缺点:

​ 1,无法取消promise,一旦新建就会立即执行,无法中途取消

​ 2,如果不设置回调函数,promise内部抛出的错误,不会反映到外部

​ 3,当处于pending状态时,无法得知进展到哪一个阶段

function 前一项任务(){ //不要写回调函数的形参变量了!
	return new Promise(
		function(door){
			原异步任务代码
			原异步任务最后一句话执行完:
			door()开门!->自动执行后续.then()中的下一项任}
		)
	}
//调用时只要后一项任务也返回new Promise()对象,则可以继续.then: 
//前一项任务().then(后一项任务).then(下一项任务)...
//简易版promise
class MyPromise {
      constructor(executor) {
        // 规定状态
        this.state = "pending"
        // 保存 `resolve(res)` 的res值
        this.value = undefined
        // 保存 `reject(err)` 的err值
        this.reason = undefined
        // 成功存放的数组
        this.successCB = []
        // 失败存放的数组
        this.failCB = []


        let resolve = (value) => {
          if (this.state === "pending") {
            this.state = "fulfilled"
            this.value = value
            this.successCB.forEach(f => f())
          }
        }
        let reject = (reason) => {
          if (this.state === "pending") {
            this.state = "rejected"
            this.value = value
            this.failCB.forEach(f => f())
          }
        }

        try {
          // 执行
          executor(resolve, reject)
        } catch (error) {
          // 若出错,直接调用reject
          reject(error)
        }
      }
      then(onFulfilled, onRejected) {
        if (this.state === "fulfilled") {
          onFulfilled(this.value)
        }
        if (this.state === "rejected") {
          onRejected(this.value)
        }
        if (this.state === "pending") {
          this.successCB.push(() => { onFulfilled(this.value) })
          this.failCB.push(() => { onRejected(this.reason) })
        }
      }
    }

//Promise.all函数
Promise.all=function(promise){
	let list=[];
	let count=0;
	function handle(i,data){
		list[i]=data;
		count++;
		if(count == promise.lenght){
			resolve(list)
		}
	}
	return Promise(resolve,reject)=>{
		for(let i=0;i<promise.lenght;i++){
			promise[i].then(res=>{
				handle(i,res)
			},err=>reject(err))
		}
	}
}
promise并行执行和顺序执行

​ 1,Promise.all并行执行promise

​ (1)getA和getB并行执行,然后输出结果,如果有一个错误,就抛出错误

//每一个promise都必须返回resolve结果才正确
//每一个promise都不处理错误
const getA=new Promise((resolve,reject)=>{
	//模拟异步任务
	setTimeout(function(){
		resolve(2);
	},1000)
})
.then(result=>result)

cosnt getB=new Promise((resolve,reject)=>{
	setTimeout(function(){
		//resolve(3)
		reject('Error in getB')
	},1000)
})
.then(result=>result)

Promise.all([getA,getB]).then(data=>{
	console.log(data)
})
.catch(e=>console.log(e))

​ (2)getA和getB并行执行,然后输出结果,总是返回resolve结果

//每一个promise自己处理错误
const getA=new Promise((resolve,reject)=>{
	//模拟异步任务
	setTimeout(function(){
		resolve(2);
	},1000)
})
.then(result=>result)
.catch(e=>e)

const getB =new Promise((resolve,reject)=>{
	setTimeout(function(){
		//resolve(3)
		reject('Error in getB')
	},1000)
})
.then(result=>result)
.catch(e=>e)

Promise.all([getA,getB]).then(data=>{
	console.log(data)
})
.catch(e=>console.log(e))

​ 2,顺序执行promise

​ (1)先getA然后执行getB ,最后addAB

//连续使用then链式操作
function getA(){
      return  new Promise(function(resolve, reject){ 
      setTimeout(function(){     
            resolve(2);
        }, 1000);
    });
}
 
function getB(){
    return  new Promise(function(resolve, reject){       
        setTimeout(function(){
            resolve(3);
        }, 1000);
    });
}
 
function addAB(a,b){
    return a+b
}

function getResult(){
    var  obj={};
    Promise.resolve().then(function(){
        return  getA() 
    })
    .then(function(a){
         obj.a=a;
    })
    .then(function(){
        return getB() 
    })
    .then(function(b){
         obj.b=b;
         return obj;
    })
    .then(function(obj){
       return  addAB(obj['a'],obj['b'])
    })
    .then(data=>{
        console.log(data)
    })
    .catch(e => console.log(e));

}
getResult();

//使用promise构建队列
function getResult(){
    var res=[];
    // 构建队列
    function queue(arr) {
      var sequence = Promise.resolve();
      arr.forEach(function (item) {
        sequence = sequence.then(item).then(data=>{
            res.push(data);
            return res
        })
      })
      return sequence
    }

    // 执行队列
    queue([getA,getB]).then(data=>{
        return addAB(data[0],data[1])
    })
    .then(data => {
        console.log(data)
    })
    .catch(e => console.log(e));

}

getResult();

//使用async、await实现类似同步编程
function getResult(){
 async function queue(arr) {
  let res = []
  for (let fn of arr) {
    var data= await fn();
    res.push(data);
  }
  return await res
}

queue([getA,getB])
  .then(data => {
    return addAB(data[0],data[1])
  }).then(data=>console.log(data))

}
闭包

​ 什么是闭包?闭包的作用?闭包的应用?

​ 既重用一个变量,又包含变量不被篡改的一种编程方法。外层函数的作用域对象,因为受到内存函数引用和保护,称为闭包对象

​ 作用:

​ 保护——避免命名冲突

​ 保存——解决循环绑定引发的索引问题

​ 变量不会被销毁——外层函数的变量因为受到内存函数的引用,所以不会被垃圾回收机制回收

​ 应用:

​ 设计模式中的单例模式,for循环中的保留i的操作,防抖,节流,函数柯里化

​ 缺点:

​ 会出现内存泄露

​ 解决:

​ 一旦一个闭包不再使用,应该尽快释放,赋值为null

​ 鄙视题: 闭包形成的原因: 外层函数调用后,因为内层函数引用着外层函数的作用域对象,导致外层函数的作用域对象无法释放,形成了闭包!

垃圾回收和内存泄漏

​ 内存泄露:

​ 指不再使用的内存没有被及时的释放,导致该段内存无法被使用

​ 为什么会导致的内存泄漏

​ 我们无法再通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该内存永远无法释放,积少成多,系统会越来越卡以至于崩溃

​ 垃圾回收机制都有哪些策略?

​ 标记清除法:

​ 垃圾回收机制获取并标记他们,然后访问并标记所有来自他们的引用,然后再访问这些对象并标记他们的引用,如此递进结束后发现没有标记的,就删除,进入执行环境的不能删除

​ 引用计数法:

​ 当声明一个变量并给该变量赋值一个引用类型的值的时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代时,该计数-1,当计数变为0时,说明无法访问该值,垃圾回收机制清除该对象

事件委托

​ 事件委托就是利用事件冒泡,只制定一个时间处理程序,就可以管理某一类型的所有事件。

​ 事件委托就是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务,事件委托的原理是DOM元素的事件冒泡

axios的特点有哪些

​ 1,axios是一个基于promise的HTTP库,支持promise所有的API

​ 2,它可以拦截请求和响应

​ 3,它可以转换请求数据和响应数据,并对响应回来的内容自动转换成JSON类型的数据

​ 4,安全性更高,客户端支持防御xsrf(XSS+CSRF)

typescript
重绘和回流

​ 回流:render树中一部分或全部元素需要改变尺寸、布局、或着需要隐藏而需要重新构建,这个过程叫做回流。回流必将引起重绘

​ 重绘:render树中一部分元素改变,而不影响布局的,只影响外观的,比如颜色。该过程叫做重绘

​ 页面至少经历一次回流和重绘(第一次加载的时候)

箭头函数和普通函数的区别

​ 1,箭头函数是匿名函数,不能作为构造函数,不能使用new

​ 2,箭头函数不能绑定arguments,要有rest参数解决

​ 3,箭头函数没有原型属性

​ 4,箭头函数的this永远指向其上下文的this

​ 5,箭头函数不能绑定this,会捕获其所在上下文的this值,作为自己的this值

如何对网站的文件和资源进行优化

​ 1,文件合并(减少HTTP请求)

​ 2,文件压缩(减少文件下载体积)

​ 3,使用cdn托管资源

​ 4,使用缓存

​ 5,gzip压缩js和css文件

​ 6,mate标签优化,heading标签优化,alt优化

​ 7,反向链接,网站外链接优化

深浅拷贝是什么如何实现

​ 深拷贝:指针赋值,并且拷贝内容

//简易版
function deepClone(obj){
	let obj2={}
	for(var i in obj){
		if(typeof obj[i]==='object'){
			obj2[i]=deepClone(obj[i])
		}else{
			obj2[i]=obj[i]
		}
	}
	return obj2
}

//把一个对象的属性和方法一个一个找出来,在另一个对象中开辟对应的空间,一个个存储到另一个对象中
var obj1={
	age:10,
	sex:'女',
	car:['奔驰','宝马'],
	cat:{
		name:'lulu',
		age:5
	}
}
//声明一个空对象
var obj2={};
//通过函数实现,把对象a中的所有数据深拷贝到对象b中
//使用递归函数
function deepCopy(obj,targetObj){
	for(let i in obj){
		let item = obj[i]
		if(item instanceof Array){
			targetObj[i]=[];
			deepCopy(item,targetObj[i])
		}else if(item instanceof Object){
			targetObj[i]={};
			deepCopy(item,targetObj[i])
		}else{
			targetObj[i]=obj[i]
		}
	}
}

deepCopy(obj1,obj2);
console.dir(obj1);
console.dir(obj2)

​ 浅拷贝:只指针赋值

//第一种
//把一个对象里面的所有属性值和方法都复制给另一个对象
let a ={boss:{name:'lala'}}
let b =Object.assign({},a)
b.boss.name='lala'
console.log(a)   //lala

//第二种
//直接把一个对象赋值给另一个对象,使得两个都指向同一个对象
let a ={age:11}
let b =a;
b.age=22;
console.log(a)   //22

//或者是解构的方法
let c ={...a}

Vue篇

vue数据双向绑定原理

​ 数据劫持:vue.js是采用数据劫持+发布者-订阅者模式的方法,通过object.defineProperty()来劫持各个属性的setter,getters,在数据变动时发布消息给订阅者,触发相应的监听回调

​ vue的绑定原理:访问器属性+虚拟dom树

​ 变量被修改时,访问器属性发出通知,虚拟dom树扫描并仅更新受影响的元素

vue computed

​ computed:计算属性,有时页面上要显示的值,后端并没有直接给出,但是却可以根据给出的其它变量值经过复杂的计算过程动态计算获得。computed的值会有缓存。避免重复计算——效率高

computed和watch的区别

​ computed:计算属性,依赖其他属性,当其他属性改变的时候下一次获取computed值时也会改变,computed的值会有缓存

​ watch:类似于数据改变后的回调,如果想深度监听的话,后面加一个deep:true,如果想监听完立马运行的话,后面加一个immediate:true

生命周期

​ vue的生命周期就是vue实例创建到实例销毁的过程。期间会有8个钩子函数的调用

​ 1,beforeCreate 创建之前,此时还没有data和method

​ 2,Created 创建完成,此时data和method可以使用

​ 在Created之后beforeMount之前,如果没有el选项,生命周期结束

​ 3,beforeMount 在渲染之前

​ 4,mounted 页面已经渲染完成,并且VM实例中已经添加完$el了,已经替换掉那些dom 元素了,这个时候可以操作dom了,但是获取不了元素的高度等属性,如 果想要获得,需要使用nextTick()

​ 5,beforeUpdate data改变后,对应的组件重新渲染之前

​ 6,updated data改变后,对应的组件重新渲染之后

​ 7,beforeDestory 在实例销毁之前,此时实例仍然可以使用

​ 8,destoryed 实例销毁后

vue组件通信

​ 1,父传子

​ 子组件设置props + 父组件设置v-bind: / :

​ 2,子传父

​ 子组件的$emit + 父组件设置v-on / @

​ 3,$bus

​ 任意组件通信,新建一个空的全局Vue对象,利用emit发送,on接收

​ 4,ref

​ ref被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs对象上。

​ 如果在普通的dom元素上使用,引用指向就是dom元素。

​ 如果用在子组件上,引用就组件实例。

​ 父模板:

​ 父访问子:父组件:this.$refs.子名称.子数据/方法名()

​ 子访问父:子组件:this.$parent.子数据/方法名()

​ 5,Vuex

​ state:存储数据的,state里面存放的数据是响应式的,state数据发生改变,对应这个数据的组件也会发生改变 用this.$store.state.xxx调用

​ getters:相当于store的计算属性,主要是对state中数据的过滤,用this.$store.getters.xxx调用

​ mutations:处理数据逻辑的方法全部放在mutations中,当触发事件想改变state数据的时候使用mutations,用this.$store.commit调用,给这个方法添加一个参数,就是mutation的载荷(payload

​ actions:异步操作数据,但是是通过mutation来操作 用this.$store.dispatch来触发,actions也支持载荷

mvvm模式、mvc模式理解

​ MVC:

​ 模型视图控制器,视图是可以直接访问模型,所以,视图里面会包含模型信息,mvc关注的是模型不变,所以,在mvc中,模型不依赖视图,但是视图依赖模型

​ MVVM:

​ 模型 视图 和vm, vm是作为模型和视图的桥梁,当模型层数据改变,vm会检测到并通知视图层进行相应的修改

diff

​ diff算法是指对新旧虚拟节点进行对比,并返回一个patch对象,用来存储两个节点不同的地方,最后利用patch记录的消息局部更新dom

虚拟DOM的优缺点

​ 缺点:首次渲染大量dom时,由于多了一层虚拟dom计算,会比innerHTML插入慢

​ 优点:减少了dom操作,具备局部更新的能力

v-for为什么必须绑定:key

​ 如果不绑定:key,则每删除数组中的一个元素,v-for都会删除所有的副本,重建整个列表,绑定:key是给每个元素副本添加一个唯一标识,在删除数组元素时,根据这个唯一的标识,找到对应的元素对象,删除即可,其他元素不受影响。

vuex

​ Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式

​ 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
使用场景:

​ 1,组件间的状态共享: 登录状态

​ 2,组件间的数据共享: 购物车的数据, 登录的token

​ 5个核心属性

​ 1,state: 数据存放

​ 2,getters: 相当于计算属性

​ 3,mutation: 同步操作, 修改数据

​ 4,action: 异步操作

​ 5,modules: 模块化

原理

1,vuex实现了一个单项数据流,在全局又有一个state存放数据,
2,当组件要更改state中的数据时,必须通过mutation进行,mutation同时提供了订阅者模式供外部插件调用获取state数据的更新
3,而当所有异步操作(常见于调用后台接口异步获取更新数据)或者批量同步操作需要走action
4,但action也是无法直接修改state的,还是需要通过mutation来修改state的数据
5,最后根据state的变化,渲染到视图上

vue-router

​ 通过hashHistory interface两种方式实现前端路由,更新视图但不重新请求页面”是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式

​ hash模式:

​ 利用onhashchange事件实现前端路由,利用URL中的hash来模拟一个hash,以保证URL改变时,页面不会重新加载

​ history模式:

​ 利用pushstate和replacestate来将URL替换但不刷新,但是一旦刷新,可能出现404,因为没有当前真正的路径,需要后端配合,将不存在的路径重定向到入口文件

Vue-router有哪几种钩子函数

​ beforEach(to,from,next)

​ afterEach(to,from,next)

​ beforRouterLeave(to,from,next)

Vue优化方式

​ 图片懒加载,路由懒加载,为减少重新渲染和创建dom节点的时间,采用虚拟dom

Vue中的nextTick

​ nextTick:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

​ 应用:

​ 想要在Vue生命周期函数中的created()操作DOM可以使用Vue.nextTick()回调函数

​ 在数据改变后要执行的操作,而这个操作需要等数据改变后而改变DOM结构的时候才进行操作,需要用到nextTick

为什么虚拟dom会提高性能

​ 虚拟dom相当于在JS和真实的dom中间加了一个缓存,利用dom,diff 算法避免了没有必要的dom操作,从而提高性能

React篇

dom-diff

​ 虚拟dom通过js的object对象模拟dom中的节点,然后再通过特定的渲染方法将其渲染成真实的dom节点

​ 频繁的操作dom,或大量造成页面的重绘和回流

​ diff算法把树形结构按照层级分解,只比较同级元素,给列表结构的每个单元添加唯一的key值,方便比较

react diff原理

​ 1,tree diff web UI 中dom节点跨层级的移动操作特别少,可以忽略不计

​ 2,component diff 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件会生成不同的树形结构

​ 3,element diff 对于同一层级的一组子节点,他们可以通过唯一的id进行区分

列表key属性

key是用来帮助react 识别哪些内容被更改、添加或者删除。如果 key值发生了变更,react 则会触发 UI 的重渲染。key就是元素的唯一标识,相当于身份证一样。

jsx原理
react-router原理

​ 实现URLUI界面的同步。其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成locationcomponents之间的同步问题

redux原理

img

​ 1,找到最上面的state

​ 2,在react中state决定了视图(ui),state的变化就会调用react的render()方法,从而改变视图

​ 3,用户通过一些事件(点击按钮,移动鼠标等)就会向reducer派发一个action

​ 4,reducer接收到action后就会去更新state

​ 5,store是包含了所有state,可以把它看做所有状态的集合

生命周期

​ 1,componentWillMount 组件渲染之前调用

​ 2,componentDidMount 在第一次渲染之后调用

​ 3,componentWillReceiveProps 在组件接收到一个新的props时调用

​ 4,shouldComponentUpdate 判断组件是否更新html

​ 5,componentWillupdate 组价即将更新HTML时调用

​ 6,componentDidupdate 在组件完成更新后立即调用

​ 7,componentWillUNmount 在组件移除之前调用

react setState

​ React在调用setstate后,react会将传入的参数对象和组件当前的状态合并,触发调和过程,

​ 在调和过程中,react会根据新的状态构建react元素树重新渲染整个UI界面,在得到元素树之后,react会自动计算新老节点的差异,根据差异对界面进行最小化重新渲染

react组件通信

​ 1,正向传值用props

​ 2,逆向转值用函数传值,通过事件调用函数传递

​ 3,同级传值用pubsub-js

​ 用pubsub.publush(事件名,数据)抛出数据

​ 用pubsub.subscribe(监听的事件,()=>{}) 接收数据

​ 4,跨组件传递,用context,要使用context进行跨组件传值需要使用createContext()方法,这个方法有两个对象provider(生产者),Comsumer(消费者)

fetch VS ajax VS axios

​ Ajax:是XMLHttpRequset(XHR),最早出现的发送后端请求技术,属于原生js中,核心是使用XHR对象,多个请求之前如果有先后顺序,就会出现回调地狱。

​ axios:是一个基于promise,本质上也是对原生XHR的封装,只不过他是promise的实现版本,符合最新的es规范

​ fetch:不是ajax的进一步封装,而是原生JS,没有使用XHR对象

React事件处理—修改this指向

​ 1,通过bind方法进行原地绑定,从而改变this指向

​ 2,通过创建箭头函数

​ 3,在constructor中提前对事件进行绑定

​ 4,将事件调用的写法改为箭头函数的形式

Vue与react区别

​ 相同点:

​ 1,都支持服务器渲染

​ 2,都有虚拟dom,组件化开发,通过props参数进行父子组件数据的传递,都实现webcomponent规范

​ 3,都是数据驱动视图

​ 4,都有管理状态,react有redux,vue有vuex

​ 5,都有支持native的方案,react有react native vue有weex

​ 不同点:

​ 1,react严格上只针对mvc的view层,vue是mvvm模式

​ 2,虚拟dom不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个dom组件树,而react不同,当应用的状态被改变时,全部组件都会重新渲染,所以react中用shouldcomponentupdate这个生命周期的钩子函数来控制
​ 3,组件写法不一样 ,react是jsx和inline style ,就是把html和css全写进js中,vue则是html,css ,js在同一个文件
​ 4,数据绑定不一样,vue实现了数据双向绑定,react数据流动是单向的
​ 5,在react中,state对象需要用setstate方法更新状态,在vue中,state对象不是必须的,数据由data属性在vue对象中管理

调用super(props)的目的是什么

​ Super()调用父类的构造函数,有super,组件才有自己的this,在组件全局中都可以使用this,如果只是constructor而不执行super,之后的this都是错的,super继承父组件的this

网络篇

http1.0和http1.1,还有http2有什么区别

​ 1,http0.9只能进行get请求

​ 2,http1.0添加了post,head,option,put,delete等

​ 3,http1.1增加了长连接keep-alive,增加了host域,而且节约带宽

​ 4,http2 多路复用,头部压缩,服务器推送

https和http有什么区别,https的实现原理?

http:http是无状态连接,而且是明文传输,不安全

https:https传输的内容是加密,身份验证,保证传输的完整性

https实现原理

​ 1,首先客户端向服务器端发送一个随机值和一个加密算法

​ 2,服务器端接收到以后,返回一个协商好的加密算法和一个随机值

​ 3,服务器端再发送一个公钥CA

​ 4,客户端收到后,先验证公钥CA是否有效,如果无效则报错弹窗,有效则进行下一步

​ 5,客户端使用之前的两个随机值和一个预主密钥组成一个会话密钥,再通过服务器端传过来的公钥加密把会话密钥发送给服务器端

​ 6,服务器端收到后,使用私钥解密,得到两个随机值和一个预主密钥,然后组成会话密钥

​ 7,客户端再向服务器端发送一条信息,这条信息使用会话秘钥加密,来验证服务器端能收到的加密信息

​ 8,服务器端收到信息后返回一个会话私钥加密信息

​ 9,都收到以后ssl层连接建立成功

浏览器从输入网址到回车发生了什么

​ 1,当发送一个URL请求时,不管这个URL是web页面的URL还是web页面上每个资源的URL,浏览器都会开启一个线程来处理这个请求,同时远程DNS服务器上启动一个DNS查询,这能使浏览器获得请求对应的IP地址

​ 2,浏览器与远程web服务器通过TCP3次握手协商来建立一个TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文 和 一个应答报文,这3个报文在浏览器和服务器之间传递,该握手首先由客户端尝试建立通信,然后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文

​ 3,一旦TCP/IP连接建立,浏览器会通过该连接向远程服务器发送http的get请求,远程服务器找到资源,使用http响应返回该资源

​ 4,此时,web服务器提供资源服务,客户端开始下载资源

前端跨域

​ 同源策略:协议+域名+端口 都必须相同。

	1,JSONP:
		jsonp的原理就是利用`<script>`标签没有跨域限制,通过`<script>`标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
	2,CORS:
		通过自定义请求头来让服务器和浏览器进行沟通,有简单请求和非简单请求
		满足以下条件,就是简单请求
			请求方法是HEAD、POST、GET
			请求头只有Accept、AcceptLanguage、ContentType、ContentLanguage、Last-			Event-Id
			简单请求,浏览器自动添加一个Origin字段
			同时后端需要设置的请求头
				Access-Control-Allow-Origin --必须
				Access-Control-Expose-Headers--可选
					XMLHttpRequest只能拿到六个字段,要想拿到其他的需要在这里指定
				Access-Control-Allow-Credentials --可选,是否可传cookie
					要是想传cookie,前端需要设置xhr.withCredentials = true,后端设置					Access-Control-Allow-Credentials
			非简单请求,浏览器判断是否为简单请求,如果是非简单请求,则 浏览器先发送一个header			头为option的请求进行预检
			预检请求格式(请求行 的请求方法为OPTIONS(专门用来询问的))关键字段是Origin
				Access-Control-Request-Method --必选
				Access-Control-Request-Headers --可选
			浏览器检查了Origin、Access-Control-Allow-Method和Access-Control-				Request-Header之后确认允许就可以做出回应了
			通过预检后,浏览器接下来的每次请求就类似于简单请求了
		3,nginx代理跨域:
			nginx模拟一个虚拟服务器,因为服务器与服务器之间是不存在跨域的,
			发送数据时 ,客户端->nginx->服务端
			返回数据时,服务端->nginx->客户端
		4,nodejs中间件代理跨域
		5,document.domain+iframe跨域
		6,location.hash+iframe跨域
		7,window.name+iframe跨域
		8,postMessage跨域
		9,webSocket协议跨域:
			WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时			  允许跨域通讯,是server push技术的一种很好的实现
浏览器缓存

强缓存

​ 在浏览器加载资源时,先看看cache-control里的max-age,判断数据有没有过期,如果没有直接使用该缓存 ,否则浏览器就回去请求服务端,要想避免这样做,可以在cache-control里面加一个immutable.

​ public:允许客户端和虚拟服务器缓存该资源,cache-control中的一个属性

​ privite:只允许客户端缓存该资源

​ no-cache:不允许强缓存,可以协商缓存

​ no-store:不允许缓存

协商缓存

​ 浏览器加载资源时,没有命中强缓存,这时候就去请求服务器,

​ 去请求服务器的时候,会带着两个参数,一个是If-None-Match,也就是响应头中的etag属性,每个文件对应一个etag;另一个参数是If-Modified-Since,也就是响应头中的Last-Modified属性,

​ 检验缓存是否真的过期,如果没有过期,则服务器会给浏览器返回一个304状态码,表示缓存没有过期,可以使用旧缓存。

​ etag的作用:

​ 有时候编辑了文件,但是没有修改,但是last-modified属性的时间就会改变,导致服务器会重新发送资源,但是etag的出现就完美的避免了这个问题,他是文件的唯一标识

缓存位置

​ 内存缓存 Memory-Cache,离线缓存Service-Worker,

​ 磁盘缓存Disk-Cache,推送缓存Push-Cache

cookie, session, token, localstorage, sessionstorage:

cookie

​ 保存在客户端的,一般由后端设置值,可以设置过期时间,储存大小只有4K

​ 一般用来保存用户的信息的,在http下cookie是明文传输的,较不安全

怎么使用cookie保存用户信息

​ document.cookie(“名字 = 数据;expire=时间”)

怎么删除cookie

​ 它的Max-age设置为0,也就是立马失效,也就是删除了

session

​ 保存在服务端的,session的运行依赖sessionId,而sessionId又保存在cookie中,所以如果禁用cookie,session也是不能用,不过硬要用也可以,可以把sessionId保存在URL中

​ session一般用来跟踪用户的状态,一般为使服务端性能更加,会考虑部分信息保存在cookie中

token

​ Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码

localstorage

​ 生命周期:关闭浏览器后数据依然保留,除非手动清除,否则一直在

​ 作用域:相同浏览器的不同标签在同源情况下可以共享localStorage

localstorage存满了怎么办

​ 划分域名,各域名下的存储空间由各业务组统一规划使用

​ 跨页面传数据:考虑单页应用、采用url传输数据

​ 最后兜底方案:调别人的存储

sessionstorage

​ 生命周期:关闭浏览器或者标签后即失效

​ 作用域:只在当前标签可用,当前标签的iframe中且同源可以共享

xss和csrf攻击

xss:脚本注入

​ 不需要你做任何的登录认证,他会通过合法的操作(URL中输入,评论框输入等)向你的页面注入脚本(JS,HTML等)

​ 防御:

​ 编码:对用户输入的数据进行HTML Entity 编码。把字符转换成 转义字符,Encode的作用是将$var等一些字符进行转化,使得浏览器在最终输出结果上是一样的

​ 过滤:移除用户输入的和事件相关的属性

csrf:跨域请求伪造

​ 在未退出A网站的前提下访问B网站,B网站使用A的cookie去访问服务器

​ 防御:

​ token,每次用户提交表单时需要带上token(伪造者访问不到),如果token不合法,则服务器拒绝请求

状态码

​ 100:临时响应是用来通知客户端的,部分的请求服务器已经接受,客户端应继续发送求请求的剩余部分,如果请求已经完成,就忽略这个响应,而且服务器会在请求完成后向客户发送一个最终的结果

​ 2开头的表示成功
​ 一般见到的就是200

​ 3开头的表示重定向
​ 301永久重定向
​ 302临时重定向
​ 304表示可以在缓存中取数据(协商缓存)

​ 4开头表示客户端错误
​ 403跨域
​ 404请求资源不存在

​ 5开头表示服务端错误
503服务器由于临时的服务器过载或者是维护,无法解决当前的请求

TCP连接(三次握手, 四次挥手)

​ 客户端和服务端之间通过三次握手建立连接,四次挥手释放连接

三次握手

​ 客户端先向服务端发起一个SYN包,进入SYN_SENT状态,服务端收到SYN后,给客户端返回 一个ACK+SYN包,表示已收到SYN,并进入SYN_RECEIVE状态,最后客户端再向服务端发送 一个ACK包表示确认,双方进入establish状态。

​ 之所以是三次握手而不是两次,是因为如果只有两次,在服务端收到SYN后,向客户端返回 一个ACK确认就进入establish状态,万一这个请求中间遇到网络情况而没有传给客户端,客 户端一直是等待状态,后面服务端发送的信息客户端也接收不到了。

四次挥手

​ 首先客户端向服务端发送一个FIN包,进入FIN_WAIT1状态,服务端收到后,向客户端发送 ACK确认包,进入CLOSE_WAIT状态,然后客户端收到ACK包后进入FIN_WAIT2状态,然后服 务端再把自己剩余没传完的数据发送给客户端,发送完毕后再发送一个FIN+ACK包,进入 LAST_ACK(最后确认)状态,客户端收到FIN+ACK包后,再向服务端发送ACK包,在等待两 个周期后在关闭连接

​ 之所以等待两个周期是因为最后服务端发送的ACK包可能会丢失,如果不等待2个周期的话, 服务端在没收到ACK包之前,会不停的重复发送FIN包而不关闭,所以得等待两个周期

工程化

webpack loader & plugin

​ loaders:

​ loaders是文件加载器,能够加载资源文件,并对这些文件进行处理,例如:编译,压缩等,最终一起打包到指定的文件中

​ plugin:

​ 在webpack运行的生命周期会有许多的事件,plugin可以监听这些事件

​ 区别:

​ 加载器是用来加载文件的,webpack本身只能加载JS文件(内置label-loader),加载其他文件就需要安装其他的loader,比如:css-loader,file-loader

​ plugin是扩展webpack功能的,通过plugin,webpack可以实现loader不能完成的复杂功能

性能优化

图片

​ 度量标准和设定目标

​ 编码优化:读取数据的速度,dom,流程控制

​ 静态资源优化:使用brotli或zopfli进行纯文本压缩,图片优化,

​ 交付优化:异步无阻塞加载JS,懒加载,优先加载关键的css,资源提示,preload(预获取资源),快速响应的用户界面

​ 构建优化:使用预编译,使用 Tree-shaking、Scope hoisting、Code-splitting,服务端渲染,使用import函数动态导入模块,使用http缓存头,

​ 其他

​ 性能监控 图片

webpack的打包原理

​ webpack是把项目当做是一个整体,通过给定一个主文件,webpack将从这个主文件开始查找到项目中所有的依赖文件,用loaders类处理,最后打包成一个或者多个浏览器可识别的 JS 文件

git常用命令

​ commit之后撤回:git reset soft HEAD^

​ 分支:git branch xx 创建分支

​ git checkout xx 切换分支

​ 添加: git add .

​ git push

​ git commit-m

算法篇

排序

​ 冒泡算法排序:

//冒泡排序
//1,比较相邻的两个元素,如果前一个比后一个大,则交换位置
//2,第一轮的时候,最后一个元素应该是最大的
//3,按照步骤一的方法,进行相邻两个元素的比较,这个时候由于最后一个元素已经是最大了,所以不用比较

function bubbleSort(arr){
	for(var i=0;i<arr.lenght;i++){
		for(var j=0;j<arr.lenght;j++){
			if(arr[j]>arr[j+1]){
				var temp=arr[j]
				arr[j]=arr[j+1]
				arr[j+1]=temp
			}
		}
	}
}

​ 快速排序:

//快速排序是对冒泡排序的一种改进,第一趟排序是将数据分为两部分,一部分比另一部分的所有内容都小,然后递归调用,在两边实行快速排序

function quickSort(arr){
	if(arr.lenght<=1){
		return arr
	}
	var middle=Math.floor(arr.lenght/2)
	var middleData=arr.splice(middle,1)[0]
	
	var left=[]
	var right=[]
	
	for(var i=0;i<arr.lebght;i++){
		if(arr[i]<middleData){
			left.push(arr[i])
		}else{
			right.push(arr[i])
		}
	}
	return quickSort(left).concat([middleData].quickSort(right))
}

​ 插入排序:

function insertSort(arr){
	//默认第一个排好序了
	for(var i=0;i<arr.lenght;i++){
		//如果后面的小于前面的,直接把后面的插入到前面的位置
		if(arr[i]<arr[i-1]){
			var el=arr[i]
			arr[i]=arr[i-1]
			var j=i-1
			while(j>=0&&arr[j]>el){
				arr[j+1]=arr[j]
				j--
			}
			arr[j+1]=el
		}
	}
}
正则表达式,千分符
function thousand(num){
	return (num+"").replace(/\d(?=(\d{3})+$)/g,"$&,")
}

console.log(thousand(123456789234))
斐波那契数列
//num1前一项,num2 当前项
function fb(n,num1=1,num2=1){
	if(n==0)return 0
	if(n<=2){
		return num2
	}else{
		return fb(n-1,num2,num1+num2)
	}
}
数组去重的方法
//第一种
var arr1=[1,2,3,4,4,3,3,2,2,1,5]
let newArr1=[]
for(let i=0;i<arr1.lenght;i++){
	if(!newArr1.includes(arr1[i])){
		newArr1.push(arr1[i])
	}
}
console.log(newArr1)

//第二种
let arr2=[1,2,2,3,3,5,6,7,7,5]
for(let i=0;i<arr2.lenght;i++){
	var item=arr2[i]
	for(let j=i+1;j<arr2.lenght;j++){
		var compare=arr2[j]
		if(compare===item){
			arr2.splice(j,1)
			j--
		}
	}
}
console.log(arr2)

//第三种
//利用对象的属性名不能重复的特点
function unique3(arr){
	var obj={}
	for(var n of arr){
		obj[n]=1
	}
	var newArr3=[]
	for(var key in obj){
		newArr3.push(parseInt(key))
	}
	return newArr3;
}

//第四种
function unique4(arr){
	return [...set=new Set(arr)]
}
instanceof
function isInstanceOf(instance,klass){
	let proto=instance.__proto__;
	let prototype=klass.prototype;
	while(true){
		if(proto===null) return false
		if(proto===prototype) return true
		proto=proto.__proto__
	}
}
函数柯里化

​ 柯里化:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

function curry(func){
	return function curried(...args){
		//关键知识点:function.lenght用来获取函数的形参个数
		//补充:arguments.lenght获取的是实参个数
		if(args.lenght >= func.lenght){
			return func.apply(this,args)
		}
		return function(...args2){
			return curried.apply(this,args.concat(args2))
		}
	}
}

// 测试
function sum (a, b, c) {
  return a + b + c
}
const curriedSum = curry(sum)
console.log(curriedSum(1, 2, 3))
console.log(curriedSum(1)(2,3))
console.log(curriedSum(1)(2)(3))
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值