HTML语义化
- 是什么
前端语义化是指在构建网页时多使用html语义化标签布局,多使用带有语义的标签如header,aside,footer等标签 - 为什么
结构清晰利于开发者开发与维护 有利于seo搜索引擎优化 有利于在网络卡顿时,正常显示页面结构(虽然没有样式),提高用户体验
盒模型
- 是什么:
页面布局的元素可以看做一个个盒子,盒子包括content,padding,border,margin
一般分为两种盒子模型 :
标准盒子模型与怪异盒子模型
两者区别是,标准盒子设置盒子宽高时默认是content的宽高,而怪异盒子设置时是除了margin都包括 - 怎么办:
为了布局方便,采取css属性,box-sizing:border-box设置为怪异盒子
浮动
- 是什么
一种网页布局方式,顾名思义,布局的元素会浮在标准布局之上 - 为什么
最初是为了实现图文环绕效果,目前一般用于将块级元素排列在一行 - 怎么做
元素设置flot:left/right 存在问题:由于脱标,若父元素未设置高度会造成父盒子塌陷现象,然后影响之后其他元素布局 解决方案:常用的给父元素设置overflow:hidden 添加伪元素,或直接设置高度等
样式优先级的规则
CSS样式的优先级应该分成四大类
- 第一类
!important
,无论引入方式是什么,选择器是什么,它的优先级都是最高的。 - 第二类引入方式,行内样式的优先级要高于嵌入和外链,嵌入和外链如果使用的选择器相同就看他们在页面中插入的顺序,在后面插入的会覆盖前面的。
- 第三类选择器,选择器优先级:id选择器>(类选择器 | 伪类选择器 | 属性选择器 )> (后代选择器 | 伪元素选择器 )> (子选择器 | 相邻选择器) > 通配符选择器 。
- 第四类继承样式,是所有样式中优先级比较低的。
- -第五类 浏览器默认样式优先级最低。
css尺寸设置的单位
- px 绝对长度,由屏幕分辨率决定
- em 相对长度,相对自身font大小,自身fontsize未设置继承父元素
- rem 相对长度,相对页面根元素大小
- vw/vh 相对长度,相对视窗的宽/高 1/100
BFC
- 是什么
块级格式化上下文,使自己成为一个独立环境不影响上下文其他元素的布局 - 为什么
主要用于清除浮动影响,与避免外边距合并等问题 - 怎么做
overflow:hidden display:flow-root等
未知宽高元素水平垂直居中
1.flex
父元素设置display:flex,justify-content:center,align-items:center
2.position
子绝父相,
子元素注意left:50℅,top:50%,transform:translate(-50%,-50%)
三栏布局实现方案
三栏布局,要求左右两边盒子宽度固定,中间盒子宽度自适应,盒子的高度都是随内容撑高的,
js数据类型
JS数据类型分为两类:
- 基本数据类型(简单数据类型)
分别是Number 、String、Boolean、BigInt、Symbol、Null、Undefined。 - 引用数据类型(复杂数据类型)
通常用Object代表,普通对象,数组,正则,日期,Math数学函数都属于Object。
数据分成两大类的本质区别:
==基本数据类型和引用数据类型它们在内存中的存储方式不同。 ==
基本数据类型是直接存储在栈中的简单数据段,占据空间小,属于被频繁使用的数据。
引用数据类型是存储在堆内存中,占据空间大。
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。
null 和 undefined 的区别,如何让一个属性变为null
- undefind
是全局对象的一个属性,当一个变量没有被赋值或者一个函数没有返回值或者某个对象不存在某个属性却去访问或者函数定义了形参但没有传递实参,这时候都是undefined。
undefined通过typeof
判断类型是’undefined’。undefined == undefined undefined === undefined 。 - null
代表对象的值未设置,相当于一个对象没有设置指针地址就是null。null通过typeof判断类型是’object’。null === null null == null null == undefined null !== undefined
undefined 表示一个变量初始状态值,而 null 则表示一个变量被人为的设置为空对象
,而不是原始状态。
在实际使用过程中,不需要对一个变量显式的赋值 undefined,当需要释放一个对象时,直接赋值为 null
即可。
加分回答
null 其实属于自己的类型 Null,而不属于Object类型,typeof 之所以会判定为 Object 类型,是因为JavaScript 数据类型在底层都是以二进制的形式表示的,二进制的前三位为 0 会被 typeof 判断为对象类型,而 null 的二进制位恰好都是 0 ,因此,null 被误判断为 Object 类型。 对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
JavaScript有几种方法判断变量的类型?
4种方法判断变量的类型,分别是typeof、instanceof、Object.prototype.toString.call()(对象原型链判断方法)、 constructor (用于引用数据类型)
- typeof:常用于判断基本数据类型,对于引用数据类型除了function返回’function‘,其余全部返回’object’。
- instanceof:主要用于区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,
- constructor:用于检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
- Object.prototype.toString.call():适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。 这四种判断数据类型的方法中,各种数据类型都能检测且检测精准的就是Object.prototype.toString.call()这种方法。 加分回答 instanceof的实现原理:验证当前类的原型prototype是否会出现在实例的原型链__proto__上,只要在它的原型链上,则结果都为true。因此,
instanceof
在查找的过程中会遍历左边变量的原型链,直到找到右边变量的prototype
,找到返回true,未找到返回false。 Object.prototype.toString.call()原理:Object.prototype.toString 表示一个返回对象类型的字符串,call()方法可以改变this的指向,那么把Object.prototype.toString()方法指向不同的数据类型上面,返回不同的结果
数组去重都有哪些方法?
第一种方法:
利用对象属性key
排除重复项:遍历数组,每次判断对象中是否存在该属性,不存在就存储在新数组中,并且把数组元素作为key,设置一个值,存储在对象中,最后返回新数组。这个方法的优点是效率较高,缺点是占用了较多空间,使用的额外空间有一个查询对象和一个新的数组
第二种方法:
利用Set
类型数据无重复项:new 一个 Set,参数为需要去重的数组,Set 会自动删除重复的元素,再将 Set 转为数组返回。这个方法的优点是效率更高,代码简单,思路清晰,缺点是可能会有兼容性问题
第三种方法:
filter+indexof
去重:这个方法和第一种方法类似,利用 Array 自带的 filter 方法,返回 arr.indexOf(num) 等于 index 的num。原理就是 indexOf 会返回最先找到的数字的索引,假设数组是 [1, 1],在对第二个1使用 indexOf 方法时,返回的是第一个1的索引0。这个方法的优点是可以在去重的时候插入对元素的操作,可拓展性强。
伪数组和数组的区别?
伪数组
它的类型不是Array,而是Object,
而数组类型是Array。
可以使用的length属性查看长度,也可以使用[index]获取某个元素,但是不能使用数组的其他方法
,也不能改变长度,遍历使用for in方法。
伪数组的常见场景:
- 函数的参数arguments
- 原生js获取DOM:document.querySelector(‘div’) 等
- jquery获取DOM:$(“div”)等
加分回答 伪数组转换成真数组方法 -Array.prototype.slice.call(伪数组) - [].slice.call(伪数组)
- Array.from(伪数组) 转换后的数组长度由
length
属性决定。索引不连续时转换结果是连续的,会自动补位。
map 和 forEach 的区别?
map 和 forEach 的区别:
map
有返回值,可以开辟新空间,return出来一个length和原数组一致的数组,即便数组元素是undefined或者是null。forEach
默认无返回值,返回结果为undefined,可以通过在函数体内部使用索引修改数组元素。
加分回答 map的处理速度比forEach快,而且返回一个新的数组,方便链式调用其他数组新方法,比如filter、reduce let arr = [1, 2, 3, 4, 5]; let arr2 = arr.map(value => value * value).filter(value => value > 10); // arr2 = [16, 25]
es6中箭头函数
what
箭头函数相当于匿名函数,简化了函数定义。箭头函数有两种写法,当函数体是单条语句的时候可以省略{}和return。另一种是包含多条语句,不可以省略{}和return。 箭头函数最大的特点就是没有this,所以this是从外部获取,就是继承外部的执行上下文中的this,由于没有this关键字所以箭头函数也不能作为构造函数,
同时通过 call()
或 apply()
方法调用一个函数时,只能传递参数(不能绑定this),第一个参数会被忽略。箭头函数也没有原型和super。不能使用yield关键字,因此箭头函数不能用作 Generator 函数。不能返回直接对象字面量。
加分回答 箭头函数的不适用场景:
- 定义对象上的方法 当调用
dog.jumps
时,lives
并没有递减。因为this
没有绑定值,而继承父级作用域。 var dog = { lives: 20, jumps: () => { this.lives–; } } - 不适合做事件处理程序 此时触发点击事件,this不是button,无法进行class切换 var button = document.querySelector(‘button’); button.addEventListener(‘click’, () => { this.classList.toggle(‘on’); }); 箭头函数函数适用场景:
- 简单的函数表达式,内部没有this引用,没有递归、事件绑定、解绑定,适用于map、filter等方法中,写法简洁 var arr = [1,2,3]; var newArr = arr.map((num)=>num*num) -内层函数表达式,需要调用this,且this应与外层函数一致时 let group = { title: “Our Group”, students: [“John”, “Pete”, “Alice”], showList() { this.students.forEach( student => alert(this.title + ': ’ + student) ); } }; group.showList();
事件扩展符用过吗(…),什么场景下?
展开语法(Spread syntax),
可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;
还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。常见的场景:等价于apply的方式、将数组展开为构造函数的参数、字面量数组或字符串连接不需要使用concat等方法了、构造字面量对象时,进行浅克隆或者属性拷贝
加分回答 只能用于可迭代对象 在数组或函数参数中使用展开语法时, 该语法只能用于 可迭代对象: var obj = {‘key1’: ‘value1’}; var array = […obj]; // TypeError: obj is not iterable 剩余语法(剩余参数) 剩余语法(Rest syntax) 看起来和展开语法完全相同,不同点在于, 剩余参数用于解构数组和对象。从某种意义上说,剩余语法与展开语法是相反的:展开语法将数组展开为其中的各个元素,而剩余语法则是将多个元素收集起来并“凝聚”为单个元素。 function f(…[a, b, c]) { return a + b + c; } f(1) // NaN (b and c are undefined) f(1, 2, 3) // 6 f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
闭包理解
闭包
一个函数和词法环境的引用捆绑在一起,这样的组合就是闭包(closure)。一般就是一个函数A,return其内部的函数B,被return出去的B函数能够在外部访问A函数内部的变量,这时候就形成了一个B函数的变量背包,A函数执行结束后这个变量背包也不会被销毁,并且这个变量背包在A函数外部只能通过B函数访问。
闭包形成的原理:作用域链,当前作用域可以访问上级作用域中的变量 闭包解决的问题:能够让函数作用域中的变量在函数执行结束之后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。
闭包带来的问题:由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出。 加分回答 闭包的应用,能够模仿块级作用域,能够实现柯里化,在构造函数中定义特权方法、Vue中数据响应式Observer中使用闭包等。
JS变量提升
变量提升是指JS的变量和函数声明会在代码编译期,提升到代码的最前面。 变量提升成立的前提是使用Var关键字进行声明的变量,并且变量提升的时候只有声明被提升
,赋值并不会被提升,同时函数的声明提升会比变量的提升优先。 变量提升的结果,可以在变量初始化之前
访问该变量,返回的是undefined。在函数声明前可以调用该函数。 加分回答 使用let和const声明的变量是创建提升,形成暂时性死区,在初始化之前访问let和const创建的变量会报错。
this指向(普通函数、箭头函数)
this关键字由来:
在对象内部的方法中使用对象内部的属性是一个非常普遍的需求。但是 JavaScript 的作用域机制并不支持这一点,基于这个需求,JavaScript 又搞出来另外一套 this 机制。 this存在的场景有三种全局执行上下文和函数执行上下文和eval执行上下文,eval这种不讨论。
在全局执行环境中无论是否在严格模式下,(在任何函数体外部)this
都指向全局对象。
在函数执行上下文中访问this,函数的调用方式决定了 this
的值。在全局环境中调用一个函数,函数内部的 this 指向的是全局变量 window,通过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象本身。
普通函数this指向:
当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;通过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象本身。new 关键字构建好了一个新对象,并且构造函数中的 this 其实就是新对象本身。嵌套函数中的 this 不会继承外层函数的 this 值。
箭头函数this指向:
箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数。
加分回答 箭头函数因为没有this,所以也不能作为构造函数,但是需要继承函数外部this的时候,使用箭头函数比较方便 var myObj = { name : “闷倒驴”, showThis:function(){ console.log(this); // myObj var bar = ()=>{ this.name = “王美丽”; console.log(this) // myObj } bar(); } }; myObj.showThis(); console.log(myObj.name); // “王美丽” console.log(window.name); // ‘’
call apply bind的作用和区别?
what
call、apply、bind的作用都是改变函数运行时的this指向。 bind和call、apply在使用上有所不同,
bind在改变this指向的时候,返回一个改变执行上下文的函数,不会立即执行函数,而是需要调用该函数的时候再调用即可,但是call和apply在改变this指向的同时执行了该函数。 bind只接收一个参数,就是this指向的执行上文。 call、apply接收多个参数,第一个参数都是this指向的执行上文,后面的参数都是作为改变this指向的函数的参数。但是call和apply参数的格式不同,call是一个参数对应一个原函数的参数,但是apply第二个参数是数组,数组中每个元素代表函数接收的参数,数组有几个元素函数就接收几个元素。
加分回答 call的应用场景: 对象的继承,在子构造函数这种调用父构造函数,但是改变this指向,就可以继承父的属性 function superClass () { this.a = 1; this.print = function () { console.log(this.a); } } function subClass () { superClass.call(this); // 执行superClass,并将superClass方法中的this指向subClass this.print(); } subClass(); 借用Array原型链上的slice方法,把伪数组转换成真数组 let domNodes = Array.prototype.slice.call(document.getElementsByTagName(“div”)); apply的应用场景: Math.max,获取数组中最大、最小的一项 let max = Math.max.apply(null, array); let min = Math.min.apply(null, array); 实现两个数组合并 let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; Array.prototype.push.apply(arr1, arr2); console.log(arr1); // [1, 2, 3, 4, 5, 6] bind的应用场景 在vue或者react框架中,使用bind将定义的方法中的this指向当前类
js继承的方法和优缺点?
原型链继承:
让一个构造函数的原型是另一个类型的实例,那么这个构造函数new出来的实例就具有该实例的属性,原型链继承的。
优点:写法方便简洁,容易理解。
缺点:在父类型构造函数中定义的引用类型值的实例属性,会在子类型原型上变成原型属性被所有子类型实例所共享。同时在创建子类型的实例时,不能向超类型的构造函数中传递参数。
借用构造函数继承:在子类型构造函数的内部调用父类型构造函数;使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上。
优点:解决了原型链实现继承的不能传参的问题和父类的原型共享的问题。
缺点:借用构造函数的缺点是方法都在构造函数中定义,因此无法实现函数复用。在父类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。
组合继承:将原型链和借用构造函数的组合到一块。使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有自己的属性。优点就是解决了原型链继承和借用构造函数继承造成的影响。缺点是无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部 原型式继承:在一个函数A内部创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。本质上,函数A是对传入的对象执行了一次浅复制。ECMAScript 5通过增加Object.create()方法将原型式继承的概念规范化了。这个方法接收两个参数:作为新对象原型的对象,以及给新对象定义额外属性的对象(第二个可选)。在只有一个参数时,Object.create()与这里的函数A方法效果相同。优点是:不需要单独创建构造函数。缺点是:属性中包含的引用值始终会在相关对象间共享 寄生式继承:寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种方式增强对象,然后返回这个对象。优点:写法简单,不需要单独创建构造函数。缺点:通过寄生式继承给对象添加函数会导致函数难以重用。 寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。优点是:高效率只调用一次父构造函数,并且因此避免了在子原型上面创建不必要,多余的属性。与此同时,原型链还能保持不变;缺点是:代码复杂 加分回答 ES6 Class实现继承。原理:原理ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。 ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。需要注意的是,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。 优点:语法简单易懂,操作更方便。缺点:并不是所有的浏览器都支持class关键字 lass Person { //调用类的构造方法 constructor(name, age) { this.name = name this.age = age } //定义一般的方法 showName() { console.log(“调用父类的方法”) console.log(this.name, this.age); } } let p1 = new Person(‘kobe’, 39) console.log(p1) //定义一个子类 class Student extends Person { constructor(name, age, salary) { super(name, age)//通过super调用父类的构造方法 this.salary = salary } showName() {//在子类自身定义方法 console.log(“调用子类的方法”) console.log(this.name, this.age, this.salary); } } let s1 = new Student(‘wade’, 38, 1000000000) console.log(s1) s1.showName()
new会发生什么?
new
关键字会进行如下的操作: 1. 创建一个空的简单JavaScript对象(即{}
); 2. 为步骤1新创建的对象添加属性__proto__
,将该属性链接至构造函数的原型对象 ; 3. 将步骤1新创建的对象作为this
的上下文 ; 4. 如果该函数没有返回对象,则返回this
。 加分回答 new
关键字后面的构造函数不能是箭头函数。
defer和async区别?
浏览器会立即加载JS文件并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行 加上async属性,加载JS文档和渲染文档可以同时进行(异步),当JS加载完成,JS代码立即执行,会阻塞HTML渲染。
加上defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),当HTML渲染完成,才会执行JS代码。 加分回答 渲染阻塞的原因: 由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。当浏览器在执行 JavaScript 程序的时候,GUI 渲染线程会被保存在一个队列中,直到 JS 程序执行完成,才会接着执行。如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉
promise是什么与使用方法?
Promise的作用:Promise是异步微任务,解决了异步多层嵌套回调的问题,让代码的可读性更高,更容易维护 Promise使用:Promise是ES6提供的一个构造函数,可以使用Promise构造函数new一个实例,Promise构造函数接收一个函数作为参数,这个函数有两个参数,分别是两个函数 resolve
和reject
,resolve
将Promise的状态由等待变为成功,将异步操作的结果作为参数传递过去;reject
则将状态由等待转变为失败,在异步操作失败时调用,将异步操作报出的错误作为参数传递过去。实例创建完成后,可以使用then
方法分别指定成功或失败的回调函数,也可以使用catch捕获失败,then和catch最终返回的也是一个Promise,所以可以链式调用。 Promise的特点: 1. 对象的状态不受外界影响(Promise对象代表一个异步操作,有三种状态)。 - pending(执行中) - Resolved(成功,又称Fulfilled) - rejected(拒绝) 其中pending为初始状态,fulfilled和rejected为结束状态(结束状态表示promise的生命周期已结束)。 2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 Promise对象的状态改变,只有两种可能(状态凝固了,就不会再变了,会一直保持这个结果): - 从Pending变为Resolved - 从Pending变为Rejected 3. resolve 方法的参数是then中回调函数的参数,reject 方法中的参数是catch中的参数 4. then 方法和 catch方法 只要不报错,返回的都是一个fullfilled状态的promise 加分回答 Promise的其他方法: Promise.resolve() :返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。 Promise.reject():返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法。 Promise.all():返回一个新的promise对象,该promise对象在参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。 Promise.any():接收一个Promise对象的集合,当其中的一个 promise 成功,就返回那个成功的promise的值。 Promise.race():当参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。
JS实现异步的方法
所有异步任务都是在同步任务执行结束之后,从任务队列中依次取出执行。 回调函数是异步操作最基本的方法,比如AJAX回调,回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数。此外它不能使用 try catch 捕获错误,不能直接 return Promise包装了一个异步调用并生成一个Promise实例,当异步调用返回的时候根据调用的结果分别调用实例化时传入的resolve 和 reject方法,then接收到对应的数据,做出相应的处理。Promise不仅能够捕获错误,而且也很好地解决了回调地狱的问题,缺点是无法取消 Promise,错误需要通过回调函数捕获。 Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数是一个状态机,封装了多个内部状态,可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。优点是异步语义清晰,缺点是手动迭代Generator
函数很麻烦,实现逻辑有点绕 async/awt是基于Promise实现的,async/awt使得异步代码看起来像同步代码,所以优点是,使用方法清晰明了,缺点是awt 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 awt 会导致性能上的降低,代码没有依赖性的话,完全可以使用 Promise.all 的方式。 加分回答 JS 异步编程进化史:callback -> promise -> generator/yield -> async/awt。 async/awt函数对 Generator 函数的改进,体现在以下三点: - 内置执行器。 Generator 函数的执行必须靠执行器,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。 - 更广的适用性。 yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 awt 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。 - 更好的语义。 async 和 awt,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,awt 表示紧跟在后面的表达式需要等待结果。 目前使用很广泛的就是promise和async/awt
cookie sessionStorage localStorage区别
Cookie、SessionStorage、 LocalStorage都是浏览器的本地存储。 它们的共同点:都是存储在浏览器本地的 它们的区别:cookie是由服务器端写入的,而SessionStorage、 LocalStorage都是由前端写入的,cookie的生命周期是由服务器端在写入的时候就设置好的,LocalStorage是写入就一直存在,除非手动清除,SessionStorage是页面关闭的时候就会自动清除。cookie的存储空间比较小大概4KB,SessionStorage、 LocalStorage存储空间比较大,大概5M。Cookie、SessionStorage、 LocalStorage数据共享都遵循同源原则,SessionStorage还限制必须是同一个页面。在前端给后端发送请求的时候会自动携带Cookie中的数据,但是SessionStorage、 LocalStorage不会 加分回答 由于它们的以上区别,所以它们的应用场景也不同,Cookie一般用于存储登录验证信息SessionID或者token,LocalStorage常用于存储不易变动的数据,减轻服务器的压力,SessionStorage可以用来检测用户是否是刷新进入页面,如音乐播放器恢复播放进度条的功能。
说一说如何实现可过期的localstorage数据?
localStorage只能用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。所以要实现可过期的localStorage缓存的中重点就是:如何清理过期的缓存。 目前有两种方法,一种是惰性删除,另一种是定时删除。 惰性删除是指某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。实现方法是,存储的数据类型是个对象,该对象有两个key,一个是要存储的value值,另一个是当前时间。获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。 定时删除是指,每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。另一方面定时删除也有效的减少了因惰性删除带来的对localStorage空间的浪费。实现过程,获取所有设置过期时间的key判断是否过期,过期就存储到数组中,遍历数组,每隔1S(固定时间)删除5个(固定个数),直到把数组中的key从localstorage中全部删除。 加分回答 LocalStorage清空应用场景:token存储在LocalStorage中,要清空
说一下token 能放在cookie中吗?
能。 token一般是用来判断用户是否登录的,它内部包含的信息有:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串) token
可以存放在Cookie
中,token
是否过期,应该由后端来判断,不该前端来判断,所以token
存储在cookie
中只要不设置cookie
的过期时间就ok了,如果 token
失效,就让后端在接口中返回固定的状态表示token
失效,需要重新登录,再重新登录的时候,重新设置 cookie
中的 token
就行。 加分回答 token认证流程 1. 客户端使用用户名跟密码请求登录 2. 服务端收到请求,去验证用户名与密码 3. 验证成功后,服务端签发一个 token ,并把它发送给客户端 4. 客户端接收 token 以后会把它存储起来,比如放在 cookie 里或者 localStorage 里 5. 客户端每次发送请求时都需要带着服务端签发的 token(把 token 放到 HTTP 的 Header 里) 6. 服务端收到请求后,需要验证请求里带有的 token ,如验证成功则返回对应的数据
axios的拦截器原理及应用?
axios的拦截器的应用场景: 请求拦截器用于在接口请求之前做的处理,比如为每个请求带上相应的参数(token,时间戳等)。 返回拦截器用于在接口返回之后做的处理,比如对返回的状态进行判断(token是否过期)。 xios为开发者提供了这样一个API:拦截器。拦截器分为 请求(request)拦截器和 响应(response)拦截器。 拦截器原理:创建一个chn数组,数组中保存了拦截器相应方法以及dispatchRequest(dispatchRequest这个函数调用才会真正的开始下发请求),把请求拦截器的方法放到chn数组中dispatchRequest的前面,把响应拦截器的方法放到chn数组中dispatchRequest的后面,把请求拦截器和相应拦截器forEach将它们分unshift,push到chn数组中,为了保证它们的执行顺序,需要使用promise,以出队列的方式对chn数组中的方法挨个执行。 加分回答 Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。从浏览器中创建 XMLHttpRequests,从 node.js 创建 http 请求,支持 Promise API,可拦截请求和响应,可转换请求数据和响应数据,可取消请求,可自动转换 JSON 数据,客户端支持防御 XSRF
创建ajax过程?
创建ajax过程: 1. 创建XHR对象:new XMLHttpRequest() 2. 设置请求参数:request.open(Method, 服务器接口地址); 3. 发送请求: request.send(),如果是get请求不需要参数,post请求需要参数request.send(data) 4. 监听请求成功后的状态变化:根据状态码进行相应的处理。 XHR.onreadystatechange = function () { if (XHR.readyState == 4 && XHR.status == 200) { console.log(XHR.responseText); // 主动释放,JS本身也会回收的 XHR = null; } }; 加分回答 POST请求需要设置请求头 readyState值说明 0:初始化,XHR对象已经创建,还未执行open 1:载入,已经调用open方法,但是还没发送请求 2:载入完成,请求已经发送完成 3:交互,可以接收到部分数据 4:数据全部返回 status值说明 200:成功 404:没有发现文件、查询或URl 500:服务器产生内部错误