javascriptJS原生面经从初级到高级近1.5W字前端小而全的知识归纳
在ES5中函数内容的this指向和调用方法有关
包括函数名()和匿名函数调用,this指向window
functiongetSum(){console.log(this)//window}getSum()(function(){console.log(this)//window})()vargetSum=function(){console.log(this)//window}getSum()1.2.2方法调用对象.方法名(),this指向对象
varobjList={name:'methods',getSum:function(){console.log(this)//objList对象}}objList.getSum()1.2.3构造器调用new构造函数名(),this指向构造函数
functionPerson(){console.log(this);//指向实例}varpersonOne=newPerson();1.2.4间接调用利用call和apply来实现,this就是call和apply对应的第一个参数,如果不传值或者第一个值为null,undefined时this指向window
functionfoo(){console.log(this);}foo.apply('我是apply改变的this值');//我是apply改变的this值foo.call('我是call改变的this值');//我是call改变的this值1.3ES6中函数的调用箭头函数不可以当作构造函数使用,也就是不能用new命令实例化一个对象,否则会抛出一个错误箭头函数的this是和定义时有关和调用无关调用就是函数调用模式
(()=>{console.log(this)//window})()letarrowFun=()=>{console.log(this)//window}arrowFun()letarrowObj={arrFun:function(){(()=>{console.log(this)//指向对象arrowObj})()}}arrowObj.arrFun();1.4.call,apply和bind1.IE5之前不支持call和apply,bind是ES5出来的;2.call和apply可以调用函数,改变this,实现继承和借用别的对象的方法;
调用方法,用一个对象替换掉另一个对象(this)对象.call(新this对象,实参1,实参2,实参3.....)对象.apply(新this对象,[实参1,实参2,实参3.....])
1.间接调用函数,改变作用域的this值2.劫持其他对象的方法
varfoo={name:"张三",logName:function(){console.log(this.name);}}varbar={name:"李四"};foo.logName.call(bar);//李四实质是call改变了foo的this指向为bar,并调用该函数3.两个函数实现继承
functionAnimal(name){this.name=name;this.showName=function(){console.log(this.name);}}functionCat(name){Animal.call(this,name);}varcat=newCat("BlackCat");cat.showName();//BlackCat4.为类数组(arguments和nodeList)添加数组方法push,pop
(function(){Array.prototype.push.call(arguments,'王五');console.log(arguments);//['张三','李四','王五']})('张三','李四')5.合并数组
letarr1=[1,2,3];letarr2=[4,5,6];Array.prototype.push.apply(arr1,arr2);//将arr2合并到了arr1中6.求数组最大值
Math.max.apply(null,arr)7.判断字符类型
Object.prototype.toString.call({})1.4.3bindbind是function的一个函数扩展方法,bind以后代码重新绑定了func内部的this指向,不会调用方法,不兼容IE8
varname='李四'varfoo={name:"张三",logName:function(age){console.log(this.name,age);}}varfooNew=foo.logName;varfooNewBind=foo.logName.bind(foo);fooNew(10)//李四,10fooNewBind(11)//张三,11因为bind改变了fooNewBind里面的this指向1.4.4call,apply和bind原生实现call实现:
Function.prototype.newCall=function(context,...parameter){if(typeofcontext==='object'||typeofcontext==='function'){context=context||window}else{context=Object.create(null)}context[fn]=thisconstres=context[fn](...parameter)deletecontext.fn;returnres}letperson={name:'Abiel'}functionsayHi(age,sex){console.log(this.name,age,sex);}sayHi.newCall(person,25,'男');//Abiel25男apply实现:
Function.prototype.newApply=function(context,parameter){if(typeofcontext==='object'||typeofcontext==='function'){context=context||window}else{context=Object.create(null)}letfn=Symbol()context[fn]=thisreturnres=context[fn](..parameter);deletecontext[fn]returnres}sayHi.newApply(person,[25,'男'])//Abiel25男bind实现:
Function.prototype.bind=function(context,...innerArgs){varme=thisreturnfunction(...finnalyArgs){returnme.call(context,...innerArgs,...finnalyArgs)}}letperson={name:'Abiel'}functionsayHi(age,sex){console.log(this.name,age,sex);}letpersonSayHi=sayHi.bind(person,25)personSayHi('男')1.4.5三者异同同:都是改变this指向,都可接收参数异:bind和call是接收单个参数,apply是接收数组
节流:
//html部分
//js部分letthrottle=function(func,delay){lettimer=null;returnfunction(){if(!timer){timer=setTimeout(()=>{func.apply(this,arguments);//或者直接func()timer=null;},delay);}};};//处理函数functionhandle(){console.log(arguments)console.log(Math.random());}//测试用例document.getElementsByClassName('scroll-box')[0].addEventListener("scroll",debounce(handle,3000));1.5.2防抖//html部分同上//js部分letdebounce=function(fn,wait){lettimeout=null;returnfunction(){if(timeout!==null)clearTimeout(timeout);//如果多次触发将上次记录延迟清除掉timeout=setTimeout(()=>{fn.apply(this,arguments);//或者直接fn()timeout=null;},wait);};}//处理函数functionhandle(){console.log(arguments)console.log(Math.random());}//测试用例document.getElementsByClassName('scroll-box')[0].addEventListener("scroll",debounce(handle,3000));
对象继承属性的一个链条
varPerson=function(name){this.name=name;}//person是构造函数varo3personTwo=newPerson('personTwo')//personTwo是实例
原型对象都有一个默认的constructor属性指向构造函数
1.字面量
letobj={'name':'张三'}2.Object构造函数创建
letObj=newObject()Obj.name='张三'3.使用工厂模式创建对象
functioncreatePerson(name){varo=newObject();o.name=name;returno;}varperson1=createPerson('张三');4.使用构造函数创建对象
functionPerson(name){this.name=name;}varperson1=newPerson('张三');1.6.4new运算符1.创了一个新对象;2.this指向构造函数;3.构造函数有返回,会替换new出来的对象,如果没有就是new出来的对象4.手动封装一个new运算符
varnew2=function(func){varo=Object.create(func.prototype);//创建对象vark=func.call(o);//改变this指向,把结果付给kif(k&&typeofk==='object'){//判断k的类型是不是对象returnk;//是,返回k}else{returno;//不是返回返回构造函数的执行结果}}1.6.5对象的原型链
JS是一门弱类型动态语言,封装和继承是他的两大特性
将父类的实例作为子类的原型1.代码实现定义父类:
//定义一个动物类functionAnimal(name){//属性this.name=name||'Animal';//实例方法this.sleep=function(){console.log(this.name+'正在睡觉!');}}//原型方法Animal.prototype.eat=function(food){console.log(this.name+'正在吃:'+food);};子类:
functionCat(){}Cat.prototype=newAnimal();Cat.prototype.name='cat';//TestCodevarcat=newCat();console.log(cat.name);//catconsole.log(cat.eat('fish'));//cat正在吃:fishundefinedconsole.log(cat.sleep());//cat正在睡觉!undefinedconsole.log(catinstanceofAnimal);//trueconsole.log(catinstanceofCat);//true2.优缺点简单易于实现,但是要想为子类新增属性和方法,必须要在newAnimal()这样的语句之后执行,无法实现多继承
实质是利用call来改变Cat中的this指向1.代码实现子类:
functionCat(name){Animal.call(this);this.name=name||'Tom';}2.优缺点可以实现多继承,不能继承原型属性/方法
为父类实例添加新特性,作为子类实例返回1.代码实现子类
functionCat(name){varinstance=newAnimal();instance.name=name||'Tom';returninstance;}2.优缺点不限制调用方式,但不能实现多继承
将父类的属性和方法拷贝一份到子类中1.子类:
functionCat(name){varanimal=newAnimal();for(varpinanimal){Cat.prototype[p]=animal[p];}Cat.prototype.name=name||'Tom';}2.优缺点支持多继承,但是效率低占用内存
通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用1.子类:
//父类classPerson{//constructor是构造方法constructor(skin,language){this.skin=skin;this.language=language;}say(){console.log('我是父类')}}//子类classChineseextendsPerson{constructor(skin,language,positon){//console.log(this);//报错super(skin,language);//super();相当于父类的构造函数//console.log(this);调用super后得到了this,不报错,this指向子类,相当于调用了父类.prototype.constructor.call(this)this.positon=positon;}aboutMe(){console.log(`${this.skin}${this.language}${this.positon}`);}}//调用只能通过new的方法得到实例,再调用里面的方法letobj=newChinese('红色','中文','香港');obj.aboutMe();obj.say();1.8.高阶函数1.8.1定义函数的参数是函数或返回函数
map,reduce,filter,sort
1.定义:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
fn(a,b,c,d)=>fn(a)(b)(c)(d)2.代码实现:
constcurrying=fn=>{constlen=fn.lengthreturnfunctioncurr(...args1){if(args1.length>=len){returnfn(...args1)}return(...args2)=>curr(...args1,...args2)}}
1.定义:
obj.func(arg1,arg2)=>func(obj,arg1,arg2)2.代码实现:
Function.prototype.uncurrying=function(){varthat=this;returnfunction(){returnFunction.prototype.call.apply(that,arguments);}};functionsayHi(){return"Hello"+this.value+""+[].slice.call(arguments);}letsayHiuncurrying=sayHi.uncurrying();console.log(sayHiuncurrying({value:'world'},"hahaha"));1.8.5偏函数1.定义:指定部分参数来返回一个新的定制函数的形式2.例子:
Obejct.create(obj,descriptor),obj是对象,describe描述符属性(可选)
1.数据属性4个特性:configurable(可配置),enumerable(可枚举),writable(可修改),value(属性值)
2.访问器属性2个特性:get(获取),set(设置)
3.内部属性由JavaScript引擎内部使用的属性;不能直接访问,但是可以通过对象内置方法间接访问,如:[[Prototype]]可以通过Object.getPrototypeOf()访问;内部属性用[[]]包围表示,是一个抽象操作,没有对应字符串类型的属性名,如[[Prototype]].
1.定义:将一个属性的所有特性编码成一个对象返回2.描述符的属性有:数据属性和访问器属性3.使用范围:作为方法Object.defineProperty,Object.getOwnPropertyDescriptor,Object.create的第二个参数,
1.访问对象存在的属性
get,set与wriable,value是互斥的,如果有交集设置会报错
1.定义属性的函数有两个:Object.defineProperty和Object.defineProperties.例如:Object.defineProperty(obj,propName,desc)
2.在引擎内部,会转换成这样的方法调用:obj.[[DefineOwnProperty]](propName,desc,true)
1.赋值运算符(=)就是在调用[[Put]].比如:obj.prop=v;
2.在引擎内部,会转换成这样的方法调用:obj.[[Put]]("prop",v,isStrictModeOn)
是一种数据类型;不能new,因为Symbol是一个原始类型的值,不是对象。
Symbol(),可以传参
vars1=Symbol();vars2=Symbol();s1===s2//false//有参数的情况vars1=Symbol("foo");vars2=Symbol("foo");s1===s2//false2.3.3用法1.不能与其他类型的值进行运算;2.作为属性名
letmySymbol=Symbol();//第一种写法vara={};a[mySymbol]='Hello!';//第二种写法vara={[mySymbol]:'Hello!'};//第三种写法vara={};Object.defineProperty(a,mySymbol,{value:'Hello!'});//以上写法都得到同样结果a[mySymbol]//"Hello!"3.作为对象属性名时,不能用点运算符,可以用[]
leta={};letname=Symbol();a.name='lili';a[name]='lucy';console.log(a.name,a[name]);4.遍历不会被for...in、for...of和Object.keys()、Object.getOwnPropertyNames()取到该属性
1.定义:在全局中搜索有没有以该参数作为名称的Symbol值,如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值2.举例:
vars1=Symbol.for('foo');vars2=Symbol.for('foo');s1===s2//true2.3.5Symbol.keyFor1.定义:返回一个已登记的Symbol类型值的key2.举例:
vars1=Symbol.for("foo");Symbol.keyFor(s1)//"foo"vars2=Symbol("foo");Symbol.keyFor(s2)//undefined2.4.遍历2.4.1一级对象遍历方法方法特性for...in遍历对象自身的和继承的可枚举属性(不含Symbol属性)Object.keys(obj)返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)Object.getOwnPropertyNames(obj)返回一个数组,包括对象自身的所有可枚举属性(不含Symbol属性)Object.getOwnPropertySymbols(obj)返回一个数组,包含对象自身的所有Symbol属性Reflect.ownKeys(obj)返回一个数组,包含对象自身的所有(不枚举、可枚举和Symbol)属性Reflect.enumerate(obj)返回一个Iterator对象,遍历对象自身的和继承的所有可枚举属性(不含Symbol属性)总结:1.只有Object.getOwnPropertySymbols(obj)和Reflect.ownKeys(obj)可以拿到Symbol属性2.只有Reflect.ownKeys(obj)可以拿到不可枚举属性
数据模型:
vartreeNodes=[{id:1,name:'1',children:[{id:11,name:'11',children:[{id:111,name:'111',children:[]},{id:112,name:'112'}]},{id:12,name:'12',children:[]}],users:[]},];递归:
varparseTreeJson=function(treeNodes){if(!treeNodes||!treeNodes.length)return;for(vari=0,len=treeNodes.length;i0){parseTreeJson(childs);}}};console.log('-------------递归实现------------------');parseTreeJson(treeNodes);2.5.深度拷贝2.5.1Object.assign1.定义:将源对象(source)的所有可枚举属性,复制到目标对象(target)2.用法:
合并多个对象vartarget={a:1,b:1};varsource1={b:2,c:2};varsource2={c:3};Object.assign(target,source1,source2);3.注意:这个是伪深度拷贝,只能拷贝第一层
1.原理:是将对象转化为字符串,而字符串是简单数据类型
functiondeepClone(source){consttargetObj=source.constructor===Array[]:{};//判断复制的目标是数组还是对象for(letkeysinsource){//遍历目标if(source.hasOwnProperty(keys)){if(source[keys]&&typeofsource[keys]==='object'){//如果值是对象,就递归一下targetObj[keys]=source[keys].constructor===Array[]:{};targetObj[keys]=deepClone(source[keys]);}else{//如果不是,就直接赋值targetObj[keys]=source[keys];}}}returntargetObj;}2.6.数据拦截定义:利用对象内置方法,设置属性,进而改变对象的属性值
1.ES5出来的方法;2.三个参数:对象(必填),属性值(必填),描述符(可选);3.defineProterty的描述符属性
数据属性:value,writable,configurable,enumerable访问器属性:get,set注:不能同时设置value和writable,这两对属性是互斥的4.拦截对象的两种情况:
letobj={name:'',age:'',sex:''},defaultName=["这是姓名默认值1","这是年龄默认值1","这是性别默认值1"];Object.keys(obj).forEach(key=>{Object.defineProperty(obj,key,{get(){returndefaultName;},set(value){defaultName=value;}});});console.log(obj.name);console.log(obj.age);console.log(obj.sex);obj.name="这是改变值1";console.log(obj.name);console.log(obj.age);console.log(obj.sex);letobjOne={},defaultNameOne="这是默认值2";Object.defineProperty(obj,'name',{get(){returndefaultNameOne;},set(value){defaultNameOne=value;}});console.log(objOne.name);objOne.name="这是改变值2";console.log(objOne.name);5.拦截数组变化的情况
leta={};bValue=1;Object.defineProperty(a,"b",{set:function(value){bValue=value;console.log("setted");},get:function(){returnbValue;}});a.b;//1a.b=[];//setteda.b=[1,2,3];//setteda.b[1]=10;//无输出a.b.push(4);//无输出a.b.length=5;//无输出a.b;//[1,10,3,4,undefined];结论:defineProperty无法检测数组索引赋值,改变数组长度的变化;但是通过数组方法来操作可以检测到多级对象监听
letinfo={};functionobserve(obj){if(!obj||typeofobj!=="object"){return;}for(variinobj){definePro(obj,i,obj[i]);}}functiondefinePro(obj,key,value){observe(value);Object.defineProperty(obj,key,{get:function(){returnvalue;},set:function(newval){console.log("检测变化",newval);value=newval;}});}definePro(info,"friends",{name:"张三"});info.friends.name="李四";6.存在的问题
2.两个参数:对象和行为函数
lethandler={get(target,key,receiver){console.log("get",key);returnReflect.get(target,key,receiver);},set(target,key,value,receiver){console.log("set",key,value);returnReflect.set(target,key,value,receiver);}};letproxy=newProxy(obj,handler);proxy.name="李四";proxy.age=24;涉及到多级对象或者多级数组监听
1.defineProterty是es5的标准,proxy是es6的标准;
2.proxy可以监听到数组索引赋值,改变数组长度的变化;
3.proxy是监听对象,不用深层遍历,defineProterty是监听属性;
数组基本上考察数组方法多一点,所以这里就单纯介绍常见的场景数组的方法,还有很多场景后续补充;本文主要从应用来讲数组api的一些骚操作;如一行代码扁平化n维数组、数组去重、求数组最大值、数组求和、排序、对象和数组的转化等;上面这些应用场景你可以用一行代码实现?
1.终极篇
[1,[2,3]].flat(2)//[1,2,3][1,[2,3,[4,5]].flat(3)//[1,2,3,4,5][1,[2,3,[4,5]]].toString()//'1,2,3,4,5'[1[2,3,[4,5[...]].flat(Infinity)//[1,2,3,4...n]Array.flat(n)是ES10扁平数组的api,n表示维度,n值为Infinity时维度为无限大
2.开始篇
functionflatten(arr){while(arr.some(item=>Array.isArray(item))){arr=[].concat(...arr);}returnarr;}flatten([1,[2,3]])//[1,2,3]flatten([1,[2,3,[4,5]])//[1,2,3,4,5]实质是利用递归和数组合并方法concat实现扁平
Array.from(newSet([1,2,3,3,4,4]))//[1,2,3,4][...newSet([1,2,3,3,4,4])]//[1,2,3,4]set是ES6新出来的一种一种定义不重复数组的数据类型Array.from是将类数组转化为数组...是扩展运算符,将set里面的值转化为字符串2.开始篇
Array.prototype.distinct=nums=>{constmap={}constresult=[]for(constnofnums){if(!(ninmap)){map[n]=1result.push(n)}}returnresult}[1,2,3,3,4,4].distinct();//[1,2,3,4]取新数组存值,循环两个数组值相比较
[1,2,3,4].sort((a,b)=>a-b);//[1,2,3,4],默认是升序[1,2,3,4].sort((a,b)=>b-a);//[4,3,2,1]降序sort是js内置的排序方法,参数为一个函数2.开始篇冒泡排序:
Array.prototype.bubleSort=function(){letarr=this,len=arr.length;for(letouter=len;outer>=2;outer--){for(letinner=0;inner<=outer-1;inner++){if(arr[inner]>arr[inner+1]){//升序[arr[inner],arr[inner+1]]=[arr[inner+1],arr[inner]];console.log([arr[inner],arr[inner+1]]);}}}returnarr;}[1,2,3,4].bubleSort()//[1,2,3,4]选择排序
Array.prototype.selectSort=function(){letarr=this,len=arr.length;for(leti=0,len=arr.length;iarr[j]){[arr[i],arr[j]]=[arr[j],arr[i]];}}}returnarr;}[1,2,3,4].selectSort()//[1,2,3,4]3.4最大值1.终极篇
Math.max(...[1,2,3,4])//4Math.max.apply(this,[1,2,3,4])//4[1,2,3,4].reduce((prev,cur,curIndex,arr)=>{returnMath.max(prev,cur);},0)//4Math.max()是Math对象内置的方法,参数是字符串;reduce是ES5的数组api,参数有函数和默认初始值;函数有四个参数,pre(上一次的返回值),cur(当前值),curIndex(当前值索引),arr(当前数组)
2.开始篇先排序再取值
[1,2,3,4].reduce(function(prev,cur){returnprev+cur;},0)//102.开始篇
functionsum(arr){varlen=arr.length;if(len==0){return0;}elseif(len==1){returnarr[0];}else{returnarr[0]+sum(arr.slice(1));}}sum([1,2,3,4])//10利用slice截取改变数组,再利用递归求和
[1,2,3,4].concat([5,6])//[1,2,3,4,5,6][...[1,2,3,4],...[4,5]]//[1,2,3,4,5,6]letarrA=[1,2],arrB=[3,4]Array.prototype.push.apply(arrA,arrB))//arrA值为[1,2,3,4]2.开始篇
letarr=[1,2,3,4];[5,6].map(item=>{arr.push(item)})//arr值为[1,2,3,4,5,6],注意不能直接return出来,return后只会返回[5,6]3.7判断是否包含值1.终极篇
[1,2,3].includes(4)//false[1,2,3].indexOf(4)//-1如果存在换回索引[1,2,3].find((item)=>item===3))//3如果数组中无值返回undefined[1,2,3].findIndex((item)=>item===3))//2如果数组中无值返回-1includes(),find(),findIndex()是ES6的api
[1,2,3].some(item=>{returnitem===3})//true如果不包含返回false3.8类数组转化1.终极篇
Array.prototype.slice.call(arguments)//arguments是类数组(伪数组)Array.prototype.slice.apply(arguments)Array.from(arguments)[...arguments]类数组:表示有length属性,但是不具备数组的方法call,apply:是改变slice里面的this指向arguments,所以arguments也可调用数组的方法Array.from是将类似数组或可迭代对象创建为数组...是将类数组扩展为字符串,再定义为数组
Array.prototype.slice=function(start,end){varresult=newArray();start=start||0;end=end||this.length;//this指向调用的对象,当用了call后,能够改变this的指向,也就是指向传进来的对象,这是关键for(vari=start;i[1,2,3].fill(false)//[false,false,false]fill是ES6的方法2.开始篇
[1,2,3].map(()=>0)3.10每一项是否满足[1,2,3].every(item=>{returnitem>2})//falseevery是ES5的api,每一项满足返回true
[1,2,3].some(item=>{returnitem>2})//truesome是ES5的api,有一项满足返回true
[1,2,3].filter(item=>{returnitem>2})//[3]filter是ES5的api,返回满足添加的项的数组
Object.keys({name:'张三',age:14})//['name','age']Object.values({name:'张三',age:14})//['张三',14]Object.entries({name:'张三',age:14})//[[name,'张三'],[age,14]]Object.fromEntries([name,'张三'],[age,14])//ES10的api,Chrome不支持,firebox输出{name:'张三',age:14}3.14对象数组[{count:1},{count:2},{count:3}].reduce((p,e)=>p+(e.count),0)4.数据结构篇数据结构是计算机存储、组织数据的方式,算法是系统描述解决问题的策略。了解基本的数据结构和算法可以提高代码的性能和质量。也是程序猿进阶的一个重要技能。手撸代码实现栈,队列,链表,字典,二叉树,动态规划和贪心算法4.1栈栈的特点:先进后出
classStack{constructor(){this.items=[];}//入栈push(element){this.items.push(element);}//出栈pop(){returnthis.items.pop();}//末位getpeek(){returnthis.items[this.items.length-1];}//是否为空栈getisEmpty(){return!this.items.length;}//长度getsize(){returnthis.items.length;}//清空栈clear(){this.items=[];}}//实例化一个栈conststack=newStack();console.log(stack.isEmpty);//true//添加元素stack.push(5);stack.push(8);//读取属性再添加console.log(stack.peek);//8stack.push(11);console.log(stack.size);//3console.log(stack.isEmpty);//false4.2队列队列:先进先出
classQueue{constructor(items){this.items=items||[];}enqueue(element){this.items.push(element);}dequeue(){returnthis.items.shift();}front(){returnthis.items[0];}clear(){this.items=[];}getsize(){returnthis.items.length;}getisEmpty(){return!this.items.length;}print(){console.log(this.items.toString());}}constqueue=newQueue();console.log(queue.isEmpty);//truequeue.enqueue("John");queue.enqueue("Jack");queue.enqueue("Camila");console.log(queue.size);//3console.log(queue.isEmpty);//falsequeue.dequeue();queue.dequeue();4.3链表链表:存贮有序元素的集合,但是不同于数组,每个元素是一个存贮元素本身的节点和指向下一个元素引用组成要想访问链表中间的元素,需要从起点开始遍历找到所需元素
classDictionary{constructor(){this.items={};}set(key,value){this.items[key]=value;}get(key){returnthis.items[key];}remove(key){deletethis.items[key];}getkeys(){returnObject.keys(this.items);}getvalues(){/*也可以使用ES7中的values方法returnObject.values(this.items)*///在这里我们通过循环生成一个数组并输出returnObject.keys(this.items).reduce((r,c,i)=>{r.push(this.items[c]);returnr;},[]);}}constdictionary=newDictionary();dictionary.set("Gandalf","gandalf@email.com");dictionary.set("John","johnsnow@email.com");dictionary.set("Tyrion","tyrion@email.com");console.log(dictionary);console.log(dictionary.keys);console.log(dictionary.values);console.log(dictionary.items);4.5二叉树特点:每个节点最多有两个子树的树结构
特点:第三项等于前面两项之和
functionfibonacci(num){if(num===1||num===2){return1}returnfibonacci(num-1)+fibonacci(num-2)}5.3动态规划特点:通过全局规划,将大问题分割成小问题来取最优解案例:最少硬币找零美国有以下面额(硬币):d1=1,d2=5,d3=10,d4=25如果要找36美分的零钱,我们可以用1个25美分、1个10美分和1个便士(1美分)
classMinCoinChange{constructor(coins){this.coins=coinsthis.cache={}}makeChange(amount){if(!amount)return[]if(this.cache[amount])returnthis.cache[amount]letmin=[],newMin,newAmountthis.coins.forEach(coin=>{newAmount=amount-coinif(newAmount>=0){newMin=this.makeChange(newAmount)}if(newAmount>=0&&(newMin.lengthfunctionMinCoinChange(coins){varcoins=coins;varcache={};this.makeChange=function(amount){varchange=[],total=0;for(vari=coins.length;i>=0;i--){varcoin=coins[i];while(total+coin<=amount){change.push(coin);total+=coin;}}returnchange;};}varminCoinChange=newMinCoinChange([1,5,10,25]);console.log(minCoinChange.makeChange(36));console.log(minCoinChange.makeChange(34));console.log(minCoinChange.makeChange(6));6设计模式设计模式如果应用到项目中,可以实现代码的复用和解耦,提高代码质量。本文主要介绍14种设计模式写UI组件,封装框架必备6.1简单工厂模式1.定义:又叫静态工厂方法,就是创建对象,并赋予属性和方法2.应用:抽取类相同的属性和方法封装到对象上3.代码:
letUserFactory=function(role){functionUser(opt){this.name=opt.name;this.viewPage=opt.viewPage;}switch(role){case'superAdmin':returnnewUser(superAdmin);break;case'admin':returnnewUser(admin);break;case'user':returnnewUser(user);break;default:thrownewError('参数错误,可选参数:superAdmin、admin、user')}}//调用letsuperAdmin=UserFactory('superAdmin');letadmin=UserFactory('admin')letnormalUser=UserFactory('user')//最后得到角色,可以调用6.2工厂方法模式1.定义:对产品类的抽象使其创建业务主要负责用于创建多类产品的实例2.应用:创建实例3.代码:
varFactory=function(type,content){if(thisinstanceofFactory){vars=newthis[type](content);returns;}else{returnnewFactory(type,content);}}//工厂原型中设置创建类型数据对象的属性Factory.prototype={Java:function(content){console.log('Java值为',content);},PHP:function(content){console.log('PHP值为',content);},Python:function(content){console.log('Python值为',content);},}//测试用例Factory('Python','我是Python');6.3原型模式1.定义:设置函数的原型属性2.应用:实现继承3.代码:
functionAnimal(name){//属性this.name=name||'Animal';//实例方法this.sleep=function(){console.log(this.name+'正在睡觉!');}}//原型方法Animal.prototype.eat=function(food){console.log(this.name+'正在吃:'+food);};functionCat(){}Cat.prototype=newAnimal();Cat.prototype.name='cat';//TestCodevarcat=newCat();console.log(cat.name);//catconsole.log(cat.eat('fish'));//cat正在吃:fishundefinedconsole.log(cat.sleep());//cat正在睡觉!undefinedconsole.log(catinstanceofAnimal);//trueconsole.log(catinstanceofCat);//true6.4单例模式1.定义:只允许被实例化依次的类2.应用:提供一个命名空间3.代码:
1.定义:不改变原对象的基础上,给对象添加属性或方法2.代码
1.作用:解决类与对象,对象与对象之间的耦合2.代码:
HTTP是一个连接客户端,网关和服务器的一个协议。
支持客户/服务器模式:可以连接客户端和服务端;简单快速:请求只需传送请求方法,路径和请求主体;灵活:传输数据类型灵活;无连接:请求结束立即断开;无状态:无法记住上一次请求。
无连接:可以通过自身属性Keep-Alive。
HTTP(S)请求地址→DNS解析→三次握手→发送请求→四次挥手
在这里插入图片描述
只允许客户端发送GET这一种请求;且不支持请求头,协议只支持纯文本;无状态性,每个访问独立处理,完成断开;无状态码。
有身份认证,三次握手;请求与响应支持头域;请求头内容;
响应头内容;
注意
请求头增加Cache-Control
采用二进制格式传输多路复用,其实就是将请求数据分成帧乱序发送到TCP中。TCP只能有一个steam,所以还是会阻塞报头压缩服务器推送主动向B端发送静态资源,避免往返延迟。
1.是基于QUIC协议,基于UDP2.特点:自定义连接机制:TCP以IP/端口标识,变化重新连接握手,UDP是一64位ID标识,是无连接;自定义重传机制:TCP使用序号和应答传输,QUIC是使用递增序号传输;无阻塞的多路复用:同一条QUIC可以创建多个steam。
3.协商缓存对比:etag优先级高于last-modified;4.etag精度高,last-modified精度是s,1s内etag修改多少次都会被记录;last-modified性能好,etag要得到hash值。
5.浏览器读取缓存流程:会先判断强缓存;再判断协商缓存etag(last-modified)是否存在;存在利用属性If-None-match(If-Modified-since)携带值;请求服务器,服务器对比etag(last-modified),生效返回304。