说一下类组件和函数组件的区别?
1. 语法上的区别:函数式组件是一个纯函数,它是须要承受props参数并且返回一个React元素就能够了。类组件是须要继承React.Component的,而且class组件须要创立render并且返回React元素,语法上来讲更简单。2. 调用形式函数式组件能够间接调用,返回一个新的React元素;类组件在调用时是须要创立一个实例的,而后通过调用实例里的render办法来返回一个React元素。3. 状态治理函数式组件没有状态治理,类组件有状态治理。4. 应用场景类组件没有具体的要求。函数式组件个别是用在大型项目中来宰割大组件(函数式组件不必创立实例,所有更高效),个别状况下能用函数式组件就不必类组件,晋升效率。
常见的位运算符有哪些?其计算规定是什么?
古代计算机中数据都是以二进制的模式存储的,即0、1两种状态,计算机对二进制数据进行的运算加减乘除等都是叫位运算,行将符号位独特参加运算的运算。
常见的位运算有以下几种:
运算符 | 形容 | 运算规定 | |
---|---|---|---|
& | 与 | 两个位都为1时,后果才为1 | |
` | ` | 或 | 两个位都为0时,后果才为0 |
^ | 异或 | 两个位雷同为0,相异为1 | |
~ | 取反 | 0变1,1变0 | |
<< | 左移 | 各二进制位全副左移若干位,高位抛弃,低位补0 | |
>> | 右移 | 各二进制位全副右移若干位,负数左补0,正数左补1,左边抛弃 |
1. 按位与运算符(&)
定义: 加入运算的两个数据按二进制位进行“与”运算。 运算规定:
0 & 0 = 0 0 & 1 = 0 1 & 0 = 0 1 & 1 = 1
总结:两位同时为1,后果才为1,否则后果为0。
例如:3&5 即:
0000 0011 0000 0101 = 0000 0001
因而 3&5 的值为1。
留神:正数按补码模式加入按位与运算。
用处:
(1)判断奇偶
只有依据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因而能够用if ((i & 1) == 0)
代替if (i % 2 == 0)
来判断a是不是偶数。
(2)清零
如果想将一个单元清零,即便其全副二进制位为0,只有与一个各位都为零的数值相与,后果为零。
2. 按位或运算符(|)
定义: 加入运算的两个对象按二进制位进行“或”运算。
运算规定:
0 | 0 = 00 | 1 = 1 1 | 0 = 1 1 | 1 = 1
总结:加入运算的两个对象只有有一个为1,其值为1。
例如:3|5即:
0000 0011 0000 0101 = 0000 0111
因而,3|5的值为7。
留神:正数按补码模式加入按位或运算。
3. 异或运算符(^)
定义: 加入运算的两个数据按二进制位进行“异或”运算。
运算规定:
0 ^ 0 = 0 0 ^ 1 = 1 1 ^ 0 = 1 1 ^ 1 = 0
总结:加入运算的两个对象,如果两个相应位雷同为0,相异为1。
例如:3|5即:
0000 0011 0000 0101 = 0000 0110
因而,3^5的值为6。
异或运算的性质:
- 交换律:
(a^b)^c == a^(b^c)
- 结合律:
(a + b)^c == a^b + b^c
- 对于任何数x,都有
x^x=0,x^0=x
- 自反性:
a^b^b=a^0=a
;
4. 取反运算符 (~)
定义: 加入运算的一个数据按二进制进行“取反”运算。
运算规定:
~ 1 = 0~ 0 = 1
总结:对一个二进制数按位取反,行将0变1,1变0。
例如:~6 即:
0000 0110= 1111 1001
在计算机中,负数用原码示意,正数应用补码存储,首先看最高位,最高位1示意正数,0示意负数。此计算机二进制码为正数,最高位为符号位。
当发现按位取反为正数时,就间接取其补码,变为十进制:
0000 0110 = 1111 1001反码:1000 0110补码:1000 0111
因而,~6的值为-7。
5. 左移运算符(<<)
定义: 将一个运算对象的各二进制位全副左移若干位,右边的二进制位抛弃,左边补0。
设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。
若左移时舍弃的高位不蕴含1,则每左移一位,相当于该数乘以2。
6. 右移运算符(>>)
定义: 将一个数的各二进制位全副右移若干位,负数左补0,正数左补1,左边抛弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。
操作数每右移一位,相当于该数除以2。
7. 原码、补码、反码
下面提到了补码、反码等常识,这里就补充一下。
计算机中的有符号数有三种示意办法,即原码、反码和补码。三种示意办法均有符号位和数值位两局部,符号位都是用0示意“正”,用1示意“负”,而数值位,三种示意办法各不相同。
(1)原码
原码就是一个数的二进制数。例如:10的原码为0000 1010
(2)反码
- 负数的反码与原码雷同,如:10 反码为 0000 1010
- 正数的反码为除符号位,按位取反,即0变1,1变0。
例如:-10
原码:1000 1010反码:1111 0101
(3)补码
- 负数的补码与原码雷同,如:10 补码为 0000 1010
- 正数的补码是原码除符号位外的所有位取反即0变1,1变0,而后加1,也就是反码加1。
例如:-10
原码:1000 1010反码:1111 0101补码:1111 0110
对对象与数组的解构的了解
解构是 ES6 提供的一种新的提取数据的模式,这种模式可能从对象或数组里有针对性地拿到想要的数值。 1)数组的解构 在解构数组时,以元素的地位为匹配条件来提取想要的数据的:
const [a, b, c] = [1, 2, 3]
最终,a、b、c别离被赋予了数组第0、1、2个索引位的值:
数组里的0、1、2索引位的元素值,精准地被映射到了左侧的第0、1、2个变量里去,这就是数组解构的工作模式。还能够通过给左侧变量数组设置空占位的形式,实现对数组中某几个元素的精准提取:
const [a,,c] = [1,2,3]
通过把两头位留空,能够顺利地把数组第一位和最初一位的值赋给 a、c 两个变量:
2)对象的解构 对象解构比数组构造略微简单一些,也更显弱小。在解构对象时,是以属性的名称为匹配条件,来提取想要的数据的。当初定义一个对象:
const stu = { name: 'Bob', age: 24}
如果想要解构它的两个自有属性,能够这样:
const { name, age } = stu
这样就失去了 name 和 age 两个和 stu 平级的变量:
留神,对象解构严格以属性名作为定位根据,所以就算调换了 name 和 age 的地位,后果也是一样的:
const { age, name } = stu
Promise 以及相干办法的实现
题目形容:手写 Promise 以及 Promise.all Promise.race 的实现
实现代码如下:
class Mypromise { constructor(fn) { // 示意状态 this.state = "pending"; // 示意then注册的胜利函数 this.successFun = []; // 示意then注册的失败函数 this.failFun = []; let resolve = (val) => { // 放弃状态扭转不可变(resolve和reject只准触发一种) if (this.state !== "pending") return; // 胜利触发机会 扭转状态 同时执行在then注册的回调事件 this.state = "success"; // 为了保障then事件先注册(次要是思考在promise外面写同步代码) promise标准 这里为模仿异步 setTimeout(() => { // 执行以后事件外面所有的注册函数 this.successFun.forEach((item) => item.call(this, val)); }); }; let reject = (err) => { if (this.state !== "pending") return; // 失败触发机会 扭转状态 同时执行在then注册的回调事件 this.state = "fail"; // 为了保障then事件先注册(次要是思考在promise外面写同步代码) promise标准 这里模仿异步 setTimeout(() => { this.failFun.forEach((item) => item.call(this, err)); }); }; // 调用函数 try { fn(resolve, reject); } catch (error) { reject(error); } } // 实例办法 then then(resolveCallback, rejectCallback) { // 判断回调是否是函数 resolveCallback = typeof resolveCallback !== "function" ? (v) => v : resolveCallback; rejectCallback = typeof rejectCallback !== "function" ? (err) => { throw err; } : rejectCallback; // 为了放弃链式调用 持续返回promise return new Mypromise((resolve, reject) => { // 将回调注册到successFun事件汇合外面去 this.successFun.push((val) => { try { // 执行回调函数 let x = resolveCallback(val); //(最难的一点) // 如果回调函数后果是一般值 那么就resolve进来给下一个then链式调用 如果是一个promise对象(代表又是一个异步) 那么调用x的then办法 将resolve和reject传进去 等到x外部的异步 执行结束的时候(状态实现)就会主动执行传入的resolve 这样就管制了链式调用的程序 x instanceof Mypromise ? x.then(resolve, reject) : resolve(x); } catch (error) { reject(error); } }); this.failFun.push((val) => { try { // 执行回调函数 let x = rejectCallback(val); x instanceof Mypromise ? x.then(resolve, reject) : reject(x); } catch (error) { reject(error); } }); }); } //静态方法 static all(promiseArr) { let result = []; //申明一个计数器 每一个promise返回就加一 let count = 0; return new Mypromise((resolve, reject) => { for (let i = 0; i < promiseArr.length; i++) { //这里用 Promise.resolve包装一下 避免不是Promise类型传进来 Promise.resolve(promiseArr[i]).then( (res) => { //这里不能间接push数组 因为要管制程序一一对应(感激评论区斧正) result[i] = res; count++; //只有全副的promise执行胜利之后才resolve进来 if (count === promiseArr.length) { resolve(result); } }, (err) => { reject(err); } ); } }); } //静态方法 static race(promiseArr) { return new Mypromise((resolve, reject) => { for (let i = 0; i < promiseArr.length; i++) { Promise.resolve(promiseArr[i]).then( (res) => { //promise数组只有有任何一个promise 状态变更 就能够返回 resolve(res); }, (err) => { reject(err); } ); } }); }}// 应用// let promise1 = new Mypromise((resolve, reject) => {// setTimeout(() => {// resolve(123);// }, 2000);// });// let promise2 = new Mypromise((resolve, reject) => {// setTimeout(() => {// resolve(1234);// }, 1000);// });// Mypromise.all([promise1,promise2]).then(res=>{// console.log(res);// })// Mypromise.race([promise1, promise2]).then(res => {// console.log(res);// });// promise1// .then(// res => {// console.log(res); //过两秒输入123// return new Mypromise((resolve, reject) => {// setTimeout(() => {// resolve("success");// }, 1000);// });// },// err => {// console.log(err);// }// )// .then(// res => {// console.log(res); //再过一秒输入success// },// err => {// console.log(err);// }// );
扩大思考:如何勾销 promise
Promise.race()办法能够用来竞争 Promise
能够借助这个个性 本人包装一个 空的 Promise 与要发动的 Promise 来实现
function wrap(pro) { let obj = {}; // 结构一个新的promise用来竞争 let p1 = new Promise((resolve, reject) => { obj.resolve = resolve; obj.reject = reject; }); obj.promise = Promise.race([p1, pro]); return obj;}let testPro = new Promise((resolve, reject) => { setTimeout(() => { resolve(123); }, 1000);});let wrapPro = wrap(testPro);wrapPro.promise.then((res) => { console.log(res);});wrapPro.resolve("被拦挡了");
代码输入问题
window.number = 2;var obj = { number: 3, db1: (function(){ console.log(this); this.number *= 4; return function(){ console.log(this); this.number *= 5; } })()}var db1 = obj.db1;db1();obj.db1();console.log(obj.number); // 15console.log(window.number); // 40
这道题目看清起来有点乱,然而实际上是考查this指向的:
- 执行db1()时,this指向全局作用域,所以window.number 4 = 8,而后执行匿名函数, 所以window.number 5 = 40;
- 执行obj.db1();时,this指向obj对象,执行匿名函数,所以obj.numer * 5 = 15。
对 CSS 工程化的了解
CSS 工程化是为了解决以下问题:
- 宏观设计:CSS 代码如何组织、如何拆分、模块构造怎么设计?
- 编码优化:怎么写出更好的 CSS?
- 构建:如何解决我的 CSS,能力让它的打包后果最优?
- 可维护性:代码写完了,如何最小化它后续的变更老本?如何确保任何一个共事都能轻松接手?
以下三个方向都是时下比拟风行的、普适性十分好的 CSS 工程化实际:
- 预处理器:Less、 Sass 等;
- 重要的工程化插件: PostCss;
- Webpack loader 等 。
基于这三个方向,能够衍生出一些具备典型意义的子问题,这里咱们一一来看:
(1)预处理器:为什么要用预处理器?它的呈现是为了解决什么问题?
预处理器,其实就是 CSS 世界的“轮子”。预处理器反对咱们写一种相似 CSS、但理论并不是 CSS 的语言,而后把它编译成 CSS 代码: 那为什么写 CSS 代码写得好好的,偏偏要转去写“类 CSS”呢?这就和原本用 JS 也能够实现所有性能,但最初却写 React 的 jsx 或者 Vue 的模板语法一样——为了爽!要想晓得有了预处理器有多爽,首先要晓得的是传统 CSS 有多不爽。随着前端业务复杂度的进步,前端工程中对 CSS 提出了以下的诉求:
- 宏观设计上:咱们心愿能优化 CSS 文件的目录构造,对现有的 CSS 文件实现复用;
- 编码优化上:咱们心愿能写出构造清晰、扼要易懂的 CSS,须要它具备高深莫测的嵌套层级关系,而不是无差别的一铺到底写法;咱们心愿它具备变量特色、计算能力、循环能力等等更强的可编程性,这样咱们能够少写一些无用的代码;
- 可维护性上:更强的可编程性意味着更优质的代码构造,实现复用意味着更简略的目录构造和更强的拓展能力,这两点如果能做到,天然会带来更强的可维护性。
这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器广泛会具备这样的个性:
- 嵌套代码的能力,通过嵌套来反映不同 css 属性之间的层级关系 ;
- 反对定义 css 变量;
- 提供计算函数;
- 容许对代码片段进行 extend 和 mixin;
- 反对循环语句的应用;
- 反对将 CSS 文件模块化,实现复用。
(2)PostCss:PostCss 是如何工作的?咱们在什么场景下会应用 PostCss?
它和预处理器的不同就在于,预处理器解决的是 类CSS,而 PostCss 解决的就是 CSS 自身。Babel 能够将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是相似的事件:它能够编译尚未被浏览器广泛支持的先进的 CSS 语法,还能够主动为一些须要额定兼容的语法减少前缀。更强的是,因为 PostCss 有着弱小的插件机制,反对各种各样的扩大,极大地强化了 CSS 的能力。
PostCss 在业务中的应用场景十分多:
- 进步 CSS 代码的可读性:PostCss 其实能够做相似预处理器能做的工作;
- 当咱们的 CSS 代码须要适配低版本浏览器时,PostCss 的 Autoprefixer 插件能够帮忙咱们主动减少浏览器前缀;
- 容许咱们编写面向未来的 CSS:PostCss 可能帮忙咱们编译 CSS next 代码;
(3)Webpack 能解决 CSS 吗?如何实现? Webpack 能解决 CSS 吗:
- Webpack 在裸奔的状态下,是不能解决 CSS 的,Webpack 自身是一个面向 JavaScript 且只能解决 JavaScript 代码的模块化打包工具;
- Webpack 在 loader 的辅助下,是能够解决 CSS 的。
如何用 Webpack 实现对 CSS 的解决:
- Webpack 中操作 CSS 须要应用的两个要害的 loader:css-loader 和 style-loader
留神,答出“用什么”有时候可能还不够,面试官会狐疑你是不是在背答案,所以你还须要理解每个 loader 都做了什么事件:
- css-loader:导入 CSS 模块,对 CSS 代码进行编译解决;
- style-loader:创立style标签,把 CSS 内容写入标签。
在理论应用中,css-loader 的执行程序肯定要安顿在 style-loader 的后面。因为只有实现了编译过程,才能够对 css 代码进行插入;若提前插入了未编译的代码,那么 webpack 是无奈了解这坨货色的,它会无情报错。
对AJAX的了解,实现一个AJAX申请
AJAX是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新以后网页的对应局部,而不必刷新整个网页。
创立AJAX申请的步骤:
- 创立一个 XMLHttpRequest 对象。
- 在这个对象上应用 open 办法创立一个 HTTP 申请,open 办法所须要的参数是申请的办法、申请的地址、是否异步和用户的认证信息。
- 在发动申请前,能够为这个对象增加一些信息和监听函数。比如说能够通过 setRequestHeader 办法来为申请增加头信息。还能够为这个对象增加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变动时会触发onreadystatechange 事件,能够通过设置监听函数,来解决申请胜利后的后果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接管实现,这个时候能够通过判断申请的状态,如果状态是 2xx 或者 304 的话则代表返回失常。这个时候就能够通过 response 中的数据来对页面进行更新了。
- 当对象的属性和监听函数设置实现后,最初调用 sent 办法来向服务器发动申请,能够传入参数作为发送的数据体。
const SERVER_URL = "/server";let xhr = new XMLHttpRequest();// 创立 Http 申请xhr.open("GET", url, true);// 设置状态监听函数xhr.onreadystatechange = function() { if (this.readyState !== 4) return; // 当申请胜利时 if (this.status === 200) { handle(this.response); } else { console.error(this.statusText); }};// 设置申请失败时的监听函数xhr.onerror = function() { console.error(this.statusText);};// 设置申请头信息xhr.responseType = "json";xhr.setRequestHeader("Accept", "application/json");// 发送 Http 申请xhr.send(null);
应用Promise封装AJAX:
// promise 封装实现:function getJSON(url) { // 创立一个 promise 对象 let promise = new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest(); // 新建一个 http 申请 xhr.open("GET", url, true); // 设置状态的监听函数 xhr.onreadystatechange = function() { if (this.readyState !== 4) return; // 当申请胜利或失败时,扭转 promise 的状态 if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; // 设置谬误监听函数 xhr.onerror = function() { reject(new Error(this.statusText)); }; // 设置响应的数据类型 xhr.responseType = "json"; // 设置申请头信息 xhr.setRequestHeader("Accept", "application/json"); // 发送 http 申请 xhr.send(null); }); return promise;}
对于原型的继承咱们借助寄生组合继承
function Person(obj) { this.name = obj.name this.age = obj.age}Person.prototype.add = function(value){ console.log(value)}var p1 = new Person({name:"番茄", age: 18})function Person1(obj) { Person.call(this, obj) this.sex = obj.sex}// 这一步是继承的要害Person1.prototype = Object.create(Person.prototype)Person1.prototype.play = function(value){ console.log(value)}var p2 = new Person1({name:"鸡蛋", age: 118, sex: "男"})
z-index属性在什么状况下会生效
通常 z-index 的应用是在有两个重叠的标签,在肯定的状况下管制其中一个在另一个的上方或者下方呈现。z-index值越大就越是在下层。z-index元素的position属性须要是relative,absolute或是fixed。
z-index属性在下列状况下会生效:
- 父元素position为relative时,子元素的z-index生效。解决:父元素position改为absolute或static;
- 元素没有设置position属性为非static属性。解决:设置该元素的position属性为relative,absolute或是fixed中的一种;
- 元素在设置z-index的同时还设置了float浮动。解决:float去除,改为display:inline-block;
实现一个扇形
用CSS实现扇形的思路和三角形基本一致,就是多了一个圆角的款式,实现一个90°的扇形:
div{ border: 100px solid transparent; width: 0; heigt: 0; border-radius: 100px; border-top-color: red;}
10 个 Ajax 同时发动申请,全副返回展现后果,并且至少容许三次失败,说出设计思路
这个问题置信很多人会第一工夫想到 Promise.all
,然而这个函数有一个局限在于如果失败一次就返回了,间接这样实现会有点问题,须要变通下。以下是两种实现思路
// 以下是不残缺代码,着重于思路 非 Promise 写法let successCount = 0let errorCount = 0let datas = []ajax(url, (res) => { if (success) { success++ if (success + errorCount === 10) { console.log(datas) } else { datas.push(res.data) } } else { errorCount++ if (errorCount > 3) { // 失败次数大于3次就应该报错了 throw Error('失败三次') } }})// Promise 写法let errorCount = 0let p = new Promise((resolve, reject) => { if (success) { resolve(res.data) } else { errorCount++ if (errorCount > 3) { // 失败次数大于3次就应该报错了 reject(error) } else { resolve(error) } }})Promise.all([p]).then(v => { console.log(v);});
首屏和白屏工夫如何计算
首屏工夫的计算,能够由 Native WebView 提供的相似 onload 的办法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是onPageFinished事件。
白屏的定义有多种。能够认为“没有任何内容”是白屏,能够认为“网络或服务异样”是白屏,能够认为“数据加载中”是白屏,能够认为“图片加载不进去”是白屏。场景不同,白屏的计算形式就不雷同。
办法1:当页面的元素数小于x时,则认为页面白屏。比方“没有任何内容”,能够获取页面的DOM节点数,判断DOM节点数少于某个阈值X,则认为白屏。 办法2:当页面呈现业务定义的错误码时,则认为是白屏。比方“网络或服务异样”。 办法3:当页面呈现业务定义的特征值时,则认为是白屏。比方“数据加载中”。
行内元素有哪些?块级元素有哪些? 空(void)元素有那些?
- 行内元素有:
a b span img input select strong
; - 块级元素有:
div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p
;
空元素,即没有内容的HTML元素。空元素是在开始标签中敞开的,也就是空元素没有闭合标签:
- 常见的有:
<br>
、<hr>
、<img>
、<input>
、<link>
、<meta>
; - 鲜见的有:
<area>
、<base>
、<col>
、<colgroup>
、<command>
、<embed>
、<keygen>
、<param>
、<source>
、<track>
、<wbr>
。
URL有哪些组成部分
一个残缺的URL包含以下几局部:
- 协定局部:该URL的协定局部为“http:”,这代表网页应用的是HTTP协定。在Internet中能够应用多种协定,如HTTP,FTP等等本例中应用的是HTTP协定。在"HTTP"前面的“//”为分隔符;
- 域名局部
- 端口局部:跟在域名前面的是端口,域名和端口之间应用“:”作为分隔符。端口不是一个URL必须的局部,如果省略端口局部,将采纳默认端口(HTTP协定默认端口是80,HTTPS协定默认端口是443);
- 虚拟目录局部:从域名后的第一个“/”开始到最初一个“/”为止,是虚拟目录局部。虚拟目录也不是一个URL必须的局部。本例中的虚拟目录是“/news/”;
- 文件名局部:从域名后的最初一个“/”开始到“?”为止,是文件名局部,如果没有“?”,则是从域名后的最初一个“/”开始到“#”为止,是文件局部,如果没有“?”和“#”,那么从域名后的最初一个“/”开始到完结,都是文件名局部。本例中的文件名是“index.asp”。文件名局部也不是一个URL必须的局部,如果省略该局部,则应用默认的文件名;
- 锚局部:从“#”开始到最初,都是锚局部。本例中的锚局部是“name”。锚局部也不是一个URL必须的局部;
- 参数局部:从“?”开始到“#”为止之间的局部为参数局部,又称搜寻局部、查问局部。本例中的参数局部为“boardID=5&ID=24618&page=1”。参数能够容许有多个参数,参数与参数之间用“&”作为分隔符。
实现节流函数和防抖函数
函数防抖的实现:
function debounce(fn, wait) { var timer = null; return function() { var context = this, args = [...arguments]; // 如果此时存在定时器的话,则勾销之前的定时器从新记时 if (timer) { clearTimeout(timer); timer = null; } // 设置定时器,使事件间隔指定事件后执行 timer = setTimeout(() => { fn.apply(context, args); }, wait); };}
函数节流的实现:
// 工夫戳版function throttle(fn, delay) { var preTime = Date.now(); return function() { var context = this, args = [...arguments], nowTime = Date.now(); // 如果两次工夫距离超过了指定工夫,则执行函数。 if (nowTime - preTime >= delay) { preTime = Date.now(); return fn.apply(context, args); } };}// 定时器版function throttle (fun, wait){ let timeout = null return function(){ let context = this let args = [...arguments] if(!timeout){ timeout = setTimeout(() => { fun.apply(context, args) timeout = null }, wait) } }}
怎么解决白屏问题
1、加loading2、骨架屏
AJAX
题目形容:利用 XMLHttpRequest 手写 AJAX 实现
实现代码如下:
const getJSON = function (url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState !== 4) return; if (xhr.status === 200 || xhr.status === 304) { resolve(xhr.responseText); } else { reject(new Error(xhr.responseText)); } }; xhr.send(); });};
如何判断一个对象是不是空对象?
Object.keys(obj).length === 0
手写题:在线编程,getUrlParams(url,key); 就是很简略的获取url的某个参数的问题,但要思考边界状况,多个返回值等等
setTimeout 模仿 setInterval
形容:应用setTimeout
模仿实现setInterval
的性能。
实现:
const mySetInterval(fn, time) { let timer = null; const interval = () => { timer = setTimeout(() => { fn(); // time 工夫之后会执行真正的函数fn interval(); // 同时再次调用interval自身 }, time) } interval(); // 开始执行 // 返回用于敞开定时器的函数 return () => clearTimeout(timer);}// 测试const cancel = mySetInterval(() => console.log(1), 400);setTimeout(() => { cancel();}, 1000); // 打印两次1
介绍下 promise 的个性、优缺点,外部是如何实现的,入手实现 Promise
1)Promise根本个性
- 1、Promise有三种状态:pending(进行中)、fulfilled(已胜利)、rejected(已失败)
- 2、Promise对象承受一个回调函数作为参数, 该回调函数承受两个参数,别离是胜利时的回调resolve和失败时的回调reject;另外resolve的参数除了正常值以外, 还可能是一个Promise对象的实例;reject的参数通常是一个Error对象的实例。
- 3、then办法返回一个新的Promise实例,并接管两个参数onResolved(fulfilled状态的回调);onRejected(rejected状态的回调,该参数可选)
- 4、catch办法返回一个新的Promise实例
- 5、finally办法不论Promise状态如何都会执行,该办法的回调函数不承受任何参数
- 6、Promise.all()办法将多个多个Promise实例,包装成一个新的Promise实例,该办法承受一个由Promise对象组成的数组作为参数(Promise.all()办法的参数能够不是数组,但必须具备Iterator接口,且返回的每个成员都是Promise实例),留神参数中只有有一个实例触发catch办法,都会触发Promise.all()办法返回的新的实例的catch办法,如果参数中的某个实例自身调用了catch办法,将不会触发Promise.all()办法返回的新实例的catch办法
- 7、Promise.race()办法的参数与Promise.all办法一样,参数中的实例只有有一个率先扭转状态就会将该实例的状态传给Promise.race()办法,并将返回值作为Promise.race()办法产生的Promise实例的返回值
- 8、Promise.resolve()将现有对象转为Promise对象,如果该办法的参数为一个Promise对象,Promise.resolve()将不做任何解决;如果参数thenable对象(即具备then办法),Promise.resolve()将该对象转为Promise对象并立刻执行then办法;如果参数是一个原始值,或者是一个不具备then办法的对象,则Promise.resolve办法返回一个新的Promise对象,状态为fulfilled,其参数将会作为then办法中onResolved回调函数的参数,如果Promise.resolve办法不带参数,会间接返回一个fulfilled状态的 Promise 对象。须要留神的是,立刻resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的完结时执行,而不是在下一轮“事件循环”的开始时。
- 9、Promise.reject()同样返回一个新的Promise对象,状态为rejected,无论传入任何参数都将作为reject()的参数
2)Promise长处
①对立异步 API
- Promise 的一个重要长处是它将逐步被用作浏览器的异步 API ,对立当初各种各样的 API ,以及不兼容的模式和手法。
②Promise 与事件比照
- 和事件相比拟, Promise 更适宜解决一次性的后果。在后果计算出来之前或之后注册回调函数都是能够的,都能够拿到正确的值。 Promise 的这个长处很天然。然而,不能应用 Promise 解决屡次触发的事件。链式解决是 Promise 的又一长处,然而事件却不能这样链式解决。
③Promise 与回调比照
- 解决了回调天堂的问题,将异步操作以同步操作的流程表达出来。
- ④Promise 带来的额定益处是蕴含了更好的错误处理形式(蕴含了异样解决),并且写起来很轻松(因为能够重用一些同步的工具,比方 Array.prototype.map() )。
3)Promise毛病
- 1、无奈勾销Promise,一旦新建它就会立刻执行,无奈中途勾销。
- 2、如果不设置回调函数,Promise外部抛出的谬误,不会反馈到内部。
- 3、当处于Pending状态时,无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。
- 4、Promise 真正执行回调的时候,定义 Promise 那局部实际上曾经走完了,所以 Promise 的报错堆栈上下文不太敌对。
4)简略代码实现
最简略的Promise实现有7个次要属性, state(状态), value(胜利返回值), reason(错误信息), resolve办法, reject办法, then办法
class Promise{ constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; let resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; } }; let reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; } }; try { // 立刻执行函数 executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.state === 'fulfilled') { let x = onFulfilled(this.value); }; if (this.state === 'rejected') { let x = onRejected(this.reason); }; }}
5)面试够用版
function myPromise(constructor){ let self=this; self.status="pending" //定义状态扭转前的初始状态 self.value=undefined;//定义状态为resolved的时候的状态 self.reason=undefined;//定义状态为rejected的时候的状态 function resolve(value){ //两个==="pending",保障了了状态的扭转是不不可逆的 if(self.status==="pending"){ self.value=value; self.status="resolved"; } } function reject(reason){ //两个==="pending",保障了了状态的扭转是不不可逆的 if(self.status==="pending"){ self.reason=reason; self.status="rejected"; } } //捕捉结构异样 try{ constructor(resolve,reject); }catch(e){ reject(e); } }myPromise.prototype.then=function(onFullfilled,onRejected){ let self=this; switch(self.status){ case "resolved": onFullfilled(self.value); break; case "rejected": onRejected(self.reason); break; default: }}// 测试var p=new myPromise(function(resolve,reject){resolve(1)}); p.then(function(x){console.log(x)})//输入1
6)大厂专供版
const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected";const resolvePromise = (promise, x, resolve, reject) => { if (x === promise) { // If promise and x refer to the same object, reject promise with a TypeError as the reason. reject(new TypeError('循环援用')) } // if x is an object or function, if (x !== null && typeof x === 'object' || typeof x === 'function') { // If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored. let called try { // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason. let then = x.then // Let then be x.then // If then is a function, call it with x as this if (typeof then === 'function') { // If/when resolvePromise is called with a value y, run [[Resolve]](promise, y) // If/when rejectPromise is called with a reason r, reject promise with r. then.call(x, y => { if (called) return called = true resolvePromise(promise, y, resolve, reject) }, r => { if (called) return called = true reject(r) }) } else { // If then is not a function, fulfill promise with x. resolve(x) } } catch (e) { if (called) return called = true reject(e) } } else { // If x is not an object or function, fulfill promise with x resolve(x) }}function Promise(excutor) { let that = this; // 缓存以后promise实例例对象 that.status = PENDING; // 初始状态 that.value = undefined; // fulfilled状态时 返回的信息 that.reason = undefined; // rejected状态时 回绝的起因 that.onFulfilledCallbacks = []; // 存储fulfilled状态对应的onFulfilled函数 that.onRejectedCallbacks = []; // 存储rejected状态对应的onRejected函数 function resolve(value) { // value胜利态时接管的终值 if(value instanceof Promise) { return value.then(resolve, reject); } // 实际中要确保 onFulfilled 和 onRejected ⽅办法异步执⾏行行,且应该在 then ⽅办法被调⽤用的那⼀一轮事件循环之后的新执⾏行行栈中执⾏行行。 setTimeout(() => { // 调⽤用resolve 回调对应onFulfilled函数 if (that.status === PENDING) { // 只能由pending状态 => fulfilled状态 (防止调⽤用屡次resolve reject) that.status = FULFILLED; that.value = value; that.onFulfilledCallbacks.forEach(cb => cb(that.value)); } }); } function reject(reason) { // reason失败态时接管的拒因 setTimeout(() => { // 调⽤用reject 回调对应onRejected函数 if (that.status === PENDING) { // 只能由pending状态 => rejected状态 (防止调⽤用屡次resolve reject) that.status = REJECTED; that.reason = reason; that.onRejectedCallbacks.forEach(cb => cb(that.reason)); } }); } // 捕捉在excutor执⾏行行器器中抛出的异样 // new Promise((resolve, reject) => { // throw new Error('error in excutor') // }) try { excutor(resolve, reject); } catch (e) { reject(e); }}Promise.prototype.then = function(onFulfilled, onRejected) { const that = this; let newPromise; // 解决理参数默认值 保障参数后续可能持续执⾏行行 onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value; onRejected = typeof onRejected === "function" ? onRejected : reason => { throw reason; }; if (that.status === FULFILLED) { // 胜利态 return newPromise = new Promise((resolve, reject) => { setTimeout(() => { try{ let x = onFulfilled(that.value); resolvePromise(newPromise, x, resolve, reject); //新的promise resolve 上⼀一个onFulfilled的返回值 } catch(e) { reject(e); // 捕捉前⾯面onFulfilled中抛出的异样then(onFulfilled, onRejected); } }); }) } if (that.status === REJECTED) { // 失败态 return newPromise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(that.reason); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); } }); }); } if (that.status === PENDING) { // 期待态// 当异步调⽤用resolve/rejected时 将onFulfilled/onRejected收集暂存到汇合中 return newPromise = new Promise((resolve, reject) => { that.onFulfilledCallbacks.push((value) => { try { let x = onFulfilled(value); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); } }); that.onRejectedCallbacks.push((reason) => { try { let x = onRejected(reason); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); } }); }); }};