nextTick
nextTick
能够让咱们在下次DOM
更新循环完结之后执行提早回调,用于取得更新后的DOM
nextTick
次要应用了宏工作和微工作。依据执行环境别离尝试采纳
Promise
MutationObserver
setImmediate
- 如果以上都不行则采纳
setTimeout
定义了一个异步办法,屡次调用nextTick
会将办法存入队列中,通过这个异步办法清空以后队列
Promise.allSettled
形容:等到所有promise
都返回后果,就返回一个promise
实例。
实现:
Promise.allSettled = function(promises) { return new Promise((resolve, reject) => { if(Array.isArray(promises)) { if(promises.length === 0) return resolve(promises); let result = []; let count = 0; promises.forEach((item, index) => { Promise.resolve(item).then( value => { count++; result[index] = { status: 'fulfilled', value: value }; if(count === promises.length) resolve(result); }, reason => { count++; result[index] = { status: 'rejected'. reason: reason }; if(count === promises.length) resolve(result); } ); }); } else return reject(new TypeError("Argument is not iterable")); });}
实现 JSONP 跨域
JSONP 外围原理:script
标签不受同源策略束缚,所以能够用来进行跨域申请,长处是兼容性好,然而只能用于 GET 申请;
实现:
const jsonp = (url, params, callbackName) => { const generateUrl = () => { let dataSrc = ""; for(let key in params) { if(params.hasOwnProperty(key)) { dataSrc += `${key}=${params[key]}&` } } dataSrc += `callback=${callbackName}`; return `${url}?${dataSrc}`; } return new Promise((resolve, reject) => { const scriptEle = document.createElement('script'); scriptEle.src = generateUrl(); document.body.appendChild(scriptEle); window[callbackName] = data => { resolve(data); document.removeChild(scriptEle); } });}
数据类型判断
typeof 能够正确辨认:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,然而对于其余的都会认为是 object,比方 Null、Date 等,所以通过 typeof 来判断数据类型会不精确。然而能够应用 Object.prototype.toString 实现。
function typeOf(obj) {- let res = Object.prototype.toString.call(obj).split(' ')[1]- res = res.substring(0, res.length - 1).toLowerCase()- return res// 更好的写法+ return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()}typeOf([]) // 'array'typeOf({}) // 'object'typeOf(new Date) // 'date'
事件循环机制 (Event Loop)
事件循环机制从整体上通知了咱们 JavaScript 代码的执行程序 Event Loop
即事件循环,是指浏览器或Node
的一种解决javaScript
单线程运行时不会阻塞的一种机制,也就是咱们常常应用异步的原理。
先执行 Script 脚本,而后清空微工作队列,而后开始下一轮事件循环,持续先执行宏工作,再清空微工作队列,如此往返。
- 宏工作:Script/setTimeout/setInterval/setImmediate/ I/O / UI Rendering
- 微工作:process.nextTick()/Promise
上诉的 setTimeout 和 setInterval 等都是工作源,真正进入工作队列的是他们散发的工作。
优先级
- setTimeout = setInterval 一个队列
- setTimeout > setImmediate
- process.nextTick > Promise
for (const macroTask of macroTaskQueue) { handleMacroTask(); for (const microTask of microTaskQueue) { handleMicroTask(microTask); }}
说一下HTTP和HTTPS协定的区别?
1、HTTPS协定须要CA证书,费用较高;而HTTP协定不须要2、HTTP协定是超文本传输协定,信息是明文传输的,HTTPS则是具备安全性的SSL加密传输协定;3、应用不同的连贯形式,端口也不同,HTTP协定端口是80,HTTPS协定端口是443;4、HTTP协定连贯很简略,是无状态的;HTTPS协定是具备SSL和HTTP协定构建的可进行加密传输、身份认证的网络协议,比HTTP更加平安
参考 前端进阶面试题具体解答
什么是文档的预解析?
Webkit 和 Firefox 都做了这个优化,当执行 JavaScript 脚本时,另一个线程解析剩下的文档,并加载前面须要通过网络加载的资源。这种形式能够使资源并行加载从而使整体速度更快。须要留神的是,预解析并不扭转 DOM 树,它将这个工作留给主解析过程,本人只解析内部资源的援用,比方内部脚本、样式表及图片。
闭包
首先阐明什么是闭包,闭包简略来说就是函数嵌套函数,外部函数援用来内部函数的变量,从而导致垃圾回收机制没有把以后变量回收掉,这样的操作带来了内存透露的影响,当内存透露到肯定水平会影响你的我的项目运行变得卡顿等等问题。因而在我的项目中咱们要尽量避免内存透露。
手写 bind、apply、call
// callFunction.prototype.call = function (context, ...args) { context = context || window; const fnSymbol = Symbol("fn"); context[fnSymbol] = this; context[fnSymbol](...args); delete context[fnSymbol];}
// applyFunction.prototype.apply = function (context, argsArr) { context = context || window; const fnSymbol = Symbol("fn"); context[fnSymbol] = this; context[fnSymbol](...argsArr); delete context[fnSymbol];}
// bindFunction.prototype.bind = function (context, ...args) { context = context || window; const fnSymbol = Symbol("fn"); context[fnSymbol] = this; return function (..._args) { args = args.concat(_args); context[fnSymbol](...args); delete context[fnSymbol]; }}
JS 整数是怎么示意的?
- 通过 Number 类型来示意,遵循 IEEE754 规范,通过 64 位来示意一个数字,(1 + 11 + 52),最大平安数字是 Math.pow(2, 53) - 1,对于 16 位十进制。(符号位 + 指数位 + 小数局部无效位)
代码输入后果
Promise.resolve('1') .then(res => { console.log(res) }) .finally(() => { console.log('finally') })Promise.resolve('2') .finally(() => { console.log('finally2') return '我是finally2返回的值' }) .then(res => { console.log('finally2前面的then函数', res) })
输入后果如下:
1finally2finallyfinally2前面的then函数 2
.finally()
个别用的很少,只有记住以下几点就能够了:
.finally()
办法不论Promise对象最初的状态如何都会执行.finally()
办法的回调函数不承受任何的参数,也就是说你在.finally()
函数中是无奈晓得Promise最终的状态是resolved
还是rejected
的- 它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异样则返回异样的Promise对象。
- finally实质上是then办法的特例
.finally()
的谬误捕捉:
Promise.resolve('1') .finally(() => { console.log('finally1') throw new Error('我是finally中抛出的异样') }) .then(res => { console.log('finally前面的then函数', res) }) .catch(err => { console.log('捕捉谬误', err) })
输入后果为:
'finally1''捕捉谬误' Error: 我是finally中抛出的异样
Vuex有哪些根本属性?为什么 Vuex 的 mutation 中不能做异步操作?
有五种,别离是 State、 Getter、Mutation 、Action、 Module1、state => 根本数据(数据源寄存地)2、getters => 从根本数据派生进去的数据3、mutations => 提交更改数据的办法,同步4、actions => 像一个装璜器,包裹mutations,使之能够异步。5、modules => 模块化Vuex1、Vuex中所有的状态更新的惟一路径都是mutation,异步操作通过 Action 来提交 mutation实现,这样能够不便地跟踪每一个状态的变动,从而可能实现一些工具帮忙更好地理解咱们的利用。2、每个mutation执行实现后都会对应到一个新的状态变更,这样devtools就能够打个快照存下来,而后就能够实现 time-travel 了。如果mutation反对异步操作,就没有方法晓得状态是何时更新的,无奈很好的进行状态的追踪,给调试带来艰难。
IE 兼容
- attchEvent('on' + type, handler)
- detachEvent('on' + type, handler)
实现 LazyMan
题目形容:
实现一个LazyMan,能够依照以下形式调用:LazyMan(“Hank”)输入:Hi! This is Hank!LazyMan(“Hank”).sleep(10).eat(“dinner”)输入Hi! This is Hank!//期待10秒..Wake up after 10Eat dinner~LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输入Hi This is Hank!Eat dinner~Eat supper~LazyMan(“Hank”).eat(“supper”).sleepFirst(5)输入//期待5秒Wake up after 5Hi This is Hank!Eat supper
实现代码如下:
class _LazyMan { constructor(name) { this.tasks = []; const task = () => { console.log(`Hi! This is ${name}`); this.next(); }; this.tasks.push(task); setTimeout(() => { // 把 this.next() 放到调用栈清空之后执行 this.next(); }, 0); } next() { const task = this.tasks.shift(); // 取第一个工作执行 task && task(); } sleep(time) { this._sleepWrapper(time, false); return this; // 链式调用 } sleepFirst(time) { this._sleepWrapper(time, true); return this; } _sleepWrapper(time, first) { const task = () => { setTimeout(() => { console.log(`Wake up after ${time}`); this.next(); }, time * 1000); }; if (first) { this.tasks.unshift(task); // 放到工作队列顶部 } else { this.tasks.push(task); // 放到工作队列尾部 } } eat(name) { const task = () => { console.log(`Eat ${name}`); this.next(); }; this.tasks.push(task); return this; }}function LazyMan(name) { return new _LazyMan(name);}
深拷贝(思考到复制 Symbol 类型)
题目形容:手写 new 操作符实现
实现代码如下:
function isObject(val) { return typeof val === "object" && val !== null;}function deepClone(obj, hash = new WeakMap()) { if (!isObject(obj)) return obj; if (hash.has(obj)) { return hash.get(obj); } let target = Array.isArray(obj) ? [] : {}; hash.set(obj, target); Reflect.ownKeys(obj).forEach((item) => { if (isObject(obj[item])) { target[item] = deepClone(obj[item], hash); } else { target[item] = obj[item]; } }); return target;}// var obj1 = {// a:1,// b:{a:2}// };// var obj2 = deepClone(obj1);// console.log(obj1);
说一下购物车的逻辑?
//vue中购物车逻辑的实现1. 购物车信息用一个数组来存储,数组中保留对象,对象中有id和count属性2. 在vuex中state中增加一个数据 cartList 用来保留这个数组3. 因为商品详情页须要用到退出购物车性能,所以咱们须要提供一个mutation, 用来将购物车信息退出 cartList中4. 退出购物车信息的时候,遵循如下规定: 如果购物车中曾经有了该商品信息,则数量累加,如果没有该商品信息,则新增一个对象5. 在商品详情页,点击退出购物车按钮的时候,调用vuex提供的addToCart这个mutation将以后的商品信息 (id count)传给addTocart this.$store.commit("addToCart", {id: , count:})// js中购物车逻辑的实现1.商品页点击“退出购物车”按钮,触发事件2.事件调用购物车“减少商品”的Js程序(函数、对象办法)3.向Js程序传递传递“商品id”、“商品数量”等数据4.存储“商品id”、“商品数量”到浏览器的localStorage中**展现购物车中的商品******1.关上购物车页面2.从localStorage中取出“商品Id”、“商品数量”等信息。3.调用服务器端“取得商品详情”的接口失去购物车中的商品信息(参数为商品Id)4.将取得的商品信息显示在购物车页面。**实现购物车中商品的购买******1.用户对购物车中的商品实现购买流程,产生购物订单2.革除localStorage中存储的曾经购买的商品信息备注1:购物车中商品存储的数据除了“商品id”、“商品数量”之外,依据产品要求还能够有其余的信息,例如残缺的商品详情(这样就不必掉服务器接口取得详情了)、购物车商品的过期工夫,超过工夫的购物车商品在下次关上网站或者购物车页面时被革除。备注2:购物车商品除了存储在localStorage中,依据产品的需要不同,也能够存储在sessionStorage、cookie、session中,或者间接向服务器接口发动申请存储在服务器上。何种状况应用哪种形式存储、有啥区别请本人剖析。
代码输入后果
setTimeout(function () { console.log(1);}, 100);new Promise(function (resolve) { console.log(2); resolve(); console.log(3);}).then(function () { console.log(4); new Promise((resove, reject) => { console.log(5); setTimeout(() => { console.log(6); }, 10); })});console.log(7);console.log(8);
输入后果为:
23784561
代码执行过程如下:
- 首先遇到定时器,将其退出到宏工作队列;
- 遇到Promise,首先执行外面的同步代码,打印出2,遇到resolve,将其退出到微工作队列,执行前面同步代码,打印出3;
- 继续执行script中的代码,打印出7和8,至此第一轮代码执行实现;
- 执行微工作队列中的代码,首先打印出4,如遇到Promise,执行其中的同步代码,打印出5,遇到定时器,将其退出到宏工作队列中,此时宏工作队列中有两个定时器;
- 执行宏工作队列中的代码,这里咱们须要留神是的第一个定时器的工夫为100ms,第二个定时器的工夫为10ms,所以先执行第二个定时器,打印出6;
- 此时微工作队列为空,继续执行宏工作队列,打印出1。
做完这道题目,咱们就须要分外留神,每个定时器的工夫,并不是所有定时器的工夫都为0哦。
如何进攻 XSS 攻打?
能够看到XSS危害如此之大, 那么在开发网站时就要做好进攻措施,具体措施如下:
- 能够从浏览器的执行来进行预防,一种是应用纯前端的形式,不必服务器端拼接后返回(不应用服务端渲染)。另一种是对须要插入到 HTML 中的代码做好充沛的本义。对于 DOM 型的攻打,次要是前端脚本的不牢靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能呈现的恶意代码状况进行判断。
- 应用 CSP ,CSP 的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行,从而避免恶意代码的注入攻打。
- CSP 指的是内容安全策略,它的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡由浏览器本人来实现。
- 通常有两种形式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的形式
- 对一些敏感信息进行爱护,比方 cookie 应用 http-only,使得脚本无奈获取。也能够应用验证码,防止脚本伪装成用户执行一些操作。
说一下JSON.stringify有什么毛病?
1.如果obj外面有工夫对象,则JSON.stringify后再JSON.parse的后果,工夫将只是字符串的模式,而不是对象的模式2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的后果将只失去空对象;3、如果obj里有函数,undefined,则序列化的后果会把函数或 undefined失落;4、如果obj里有NaN、Infinity和-Infinity,则序列化的后果会变成null5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果obj中的对象是有构造函数生成的, 则应用JSON.parse(JSON.stringify(obj))深拷贝后,会抛弃对象的constructor;6、如果对象中存在循环援用的状况也无奈正确实现深拷贝;
说一下常见的检测数据类型的几种形式?
typeof 其中数组、对象、null都会被判断为Object,其余判断都正确instanceof 只能判断援用数据类型,不能判断根本数据类型constructor 它有2个作用 一是判断数据的类型,二是对象实例通过constructor对象拜访它的构造函数。须要留神的事件是如果创立一个对象来扭转它的原型,constructor就不能来判断数据类型了Object.prototype.toString.call()