共计 10461 个字符,预计需要花费 27 分钟才能阅读完成。
对 Promise 的了解
Promise 是异步编程的一种解决方案,它是一个对象,能够获取异步操作的音讯,他的呈现大大改善了异步编程的窘境,防止了天堂回调,它比传统的解决方案回调函数和事件更正当和更弱小。
所谓 Promise,简略说就是一个容器,外面保留着某个将来才会完结的事件(通常是一个异步操作)的后果。从语法上说,Promise 是一个对象,从它能够获取异步操作的音讯。Promise 提供对立的 API,各种异步操作都能够用同样的办法进行解决。
(1)Promise 的实例有 三个状态:
- Pending(进行中)
- Resolved(已实现)
- Rejected(已回绝)
当把一件事件交给 promise 时,它的状态就是 Pending,工作实现了状态就变成了 Resolved、没有实现失败了就变成了 Rejected。
(2)Promise 的实例有 两个过程:
- pending -> fulfilled : Resolved(已实现)
- pending -> rejected:Rejected(已回绝)
留神:一旦从进行状态变成为其余状态就永远不能更改状态了。
Promise 的特点:
- 对象的状态不受外界影响。promise 对象代表一个异步操作,有三种状态,
pending
(进行中)、fulfilled
(已胜利)、rejected
(已失败)。只有异步操作的后果,能够决定以后是哪一种状态,任何其余操作都无奈扭转这个状态,这也是 promise 这个名字的由来——“承诺”; - 一旦状态扭转就不会再变,任何时候都能够失去这个后果。promise 对象的状态扭转,只有两种可能:从
pending
变为fulfilled
,从pending
变为rejected
。这时就称为resolved
(已定型)。如果扭转曾经产生了,你再对 promise 对象增加回调函数,也会立刻失去这个后果。这与事件(event)齐全不同,事件的特点是:如果你错过了它,再去监听是得不到后果的。
Promise 的毛病:
- 无奈勾销 Promise,一旦新建它就会立刻执行,无奈中途勾销。
- 如果不设置回调函数,Promise 外部抛出的谬误,不会反馈到内部。
- 当处于 pending 状态时,无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。
总结: Promise 对象是异步编程的一种解决方案,最早由社区提出。Promise 是一个构造函数,接管一个函数作为参数,返回一个 Promise 实例。一个 Promise 实例有三种状态,别离是 pending、resolved 和 rejected,别离代表了进行中、已胜利和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经扭转,就凝固了,无奈再被扭转了。
状态的扭转是通过 resolve() 和 reject() 函数来实现的,能够在异步操作完结后调用这两个函数扭转 Promise 实例的状态,它的原型上定义了一个 then 办法,应用这个 then 办法能够为两个状态的扭转注册回调函数。这个回调函数属于微工作,会在本轮事件循环的开端执行。
留神: 在结构 Promise
的时候,构造函数外部的代码是立刻执行的
常见的浏览器内核比拟
- Trident: 这种浏览器内核是 IE 浏览器用的内核,因为在晚期 IE 占有大量的市场份额,所以这种内核比拟风行,以前有很多网页也是依据这个内核的规范来编写的,然而实际上这个内核对真正的网页规范反对不是很好。然而因为 IE 的高市场占有率,微软也很长时间没有更新 Trident 内核,就导致了 Trident 内核和 W3C 规范脱节。还有就是 Trident 内核的大量 Bug 等平安问题没有失去解决,加上一些专家学者公开本人认为 IE 浏览器不平安的观点,使很多用户开始转向其余浏览器。
- Gecko: 这是 Firefox 和 Flock 所采纳的内核,这个内核的长处就是功能强大、丰盛,能够反对很多简单网页成果和浏览器扩大接口,然而代价是也不言而喻就是要耗费很多的资源,比方内存。
- Presto: Opera 已经采纳的就是 Presto 内核,Presto 内核被称为公认的浏览网页速度最快的内核,这得益于它在开发时的天生劣势,在解决 JS 脚本等脚本语言时,会比其余的内核快 3 倍左右,毛病就是为了达到很快的速度而丢掉了一部分网页兼容性。
- Webkit: Webkit 是 Safari 采纳的内核,它的长处就是网页浏览速度较快,尽管不迭 Presto 然而也胜于 Gecko 和 Trident,毛病是对于网页代码的容错性不高,也就是说对网页代码的兼容性较低,会使一些编写不规范的网页无奈正确显示。WebKit 前身是 KDE 小组的 KHTML 引擎,能够说 WebKit 是 KHTML 的一个开源的分支。
- Blink: 谷歌在 Chromium Blog 上发表博客,称将与苹果的开源浏览器外围 Webkit 各奔前程,在 Chromium 我的项目中研发 Blink 渲染引擎(即浏览器外围),内置于 Chrome 浏览器之中。其实 Blink 引擎就是 Webkit 的一个分支,就像 webkit 是 KHTML 的分支一样。Blink 引擎当初是谷歌公司与 Opera Software 独特研发,下面提到过的,Opera 弃用了本人的 Presto 内核,退出 Google 营垒,追随谷歌一起研发 Blink。
V8 的垃圾回收机制是怎么的
V8 实现了精确式 GC,GC 算法采纳了分代式垃圾回收机制。因而,V8 将内存(堆)分为新生代和老生代两局部。
(1)新生代算法
新生代中的对象个别存活工夫较短,应用 Scavenge GC 算法。
在新生代空间中,内存空间分为两局部,别离为 From 空间和 To 空间。在这两个空间中,必然有一个空间是应用的,另一个空间是闲暇的。新调配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了。算法会查看 From 空间中存活的对象并复制到 To 空间中,如果有失活的对象就会销毁。当复制实现后将 From 空间和 To 空间调换,这样 GC 就完结了。
(2)老生代算法
老生代中的对象个别存活工夫较长且数量也多,应用了两个算法,别离是标记革除算法和标记压缩算法。
先来说下什么状况下对象会呈现在老生代空间中:
- 新生代中的对象是否曾经经验过一次 Scavenge 算法,如果经验过的话,会将对象从新生代空间移到老生代空间中。
- To 空间的对象占比大小超过 25 %。在这种状况下,为了不影响到内存调配,会将对象从新生代空间移到老生代空间中。
老生代中的空间很简单,有如下几个空间
enum AllocationSpace {// TODO(v8:7464): Actually map this space's memory as read-only.
RO_SPACE, // 不变的对象空间
NEW_SPACE, // 新生代用于 GC 复制算法的空间
OLD_SPACE, // 老生代常驻对象空间
CODE_SPACE, // 老生代代码对象空间
MAP_SPACE, // 老生代 map 对象
LO_SPACE, // 老生代大空间对象
NEW_LO_SPACE, // 新生代大空间对象
FIRST_SPACE = RO_SPACE,
LAST_SPACE = NEW_LO_SPACE,
FIRST_GROWABLE_PAGED_SPACE = OLD_SPACE,
LAST_GROWABLE_PAGED_SPACE = MAP_SPACE
};
在老生代中,以下状况会先启动标记革除算法:
- 某一个空间没有分块的时候
- 空间中被对象超过肯定限度
- 空间不能保障新生代中的对象挪动到老生代中
在这个阶段中,会遍历堆中所有的对象,而后标记活的对象,在标记实现后,销毁所有没有被标记的对象。在标记大型对内存时,可能须要几百毫秒能力实现一次标记。这就会导致一些性能上的问题。为了解决这个问题,2011 年,V8 从 stop-the-world 标记切换到增量标记。在增量标记期间,GC 将标记工作合成为更小的模块,能够让 JS 应用逻辑在模块间隙执行一会,从而不至于让利用呈现进展状况。但在 2018 年,GC 技术又有了一个重大突破,这项技术名为并发标记。该技术能够让 GC 扫描和标记对象时,同时容许 JS 运行。
革除对象后会造成堆内存呈现碎片的状况,当碎片超过肯定限度后会启动压缩算法。在压缩过程中,将活的对象向一端挪动,直到所有对象都挪动实现而后清理掉不须要的内存。
网络劫持有哪几种,如何防备?
⽹络劫持分为两种:
(1)DNS 劫持: (输⼊京东被强制跳转到淘宝这就属于 dns 劫持)
- DNS 强制解析: 通过批改运营商的本地 DNS 记录,来疏导⽤户流量到缓存服务器
- 302 跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是能够进⾏劫持解决的, 再对劫持的内存发动 302 跳转的回复,疏导⽤户获取内容
(2)HTTP 劫持: (拜访⾕歌然而⼀直有贪玩蓝⽉的⼴告), 因为 http 明⽂传输, 运营商会批改你的 http 响应内容(即加⼴告)
DNS 劫持因为涉嫌守法,曾经被监管起来,当初很少会有 DNS 劫持,⽽ http 劫持仍然⾮常盛⾏,最无效的方法就是全站 HTTPS,将 HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
你在工作终于到那些问题,解决办法是什么
常常遇到的问题就是 Cannot read property‘prototype’of undefined
解决办法通过浏览器报错提醒代码定位问题,解决问题
Vue 我的项目中遇到视图不更新,办法不执行,埋点不触发等问题
个别解决方案查看浏览器报错,查看代码运行到那个阶段未之行完结,浏览源码以及相干文档等
而后举进去最近开发的我的项目中遇到的算是两个比拟大的问题。
代码输入问题
function Parent() {
this.a = 1;
this.b = [1, 2, this.a];
this.c = {demo: 5};
this.show = function () {console.log(this.a , this.b , this.c.demo);
}
}
function Child() {
this.a = 2;
this.change = function () {this.b.push(this.a);
this.a = this.b.length;
this.c.demo = this.a++;
}
}
Child.prototype = new Parent();
var parent = new Parent();
var child1 = new Child();
var child2 = new Child();
child1.a = 11;
child2.a = 12;
parent.show();
child1.show();
child2.show();
child1.change();
child2.change();
parent.show();
child1.show();
child2.show();
输入后果:
parent.show(); // 1 [1,2,1] 5
child1.show(); // 11 [1,2,1] 5
child2.show(); // 12 [1,2,1] 5
parent.show(); // 1 [1,2,1] 5
child1.show(); // 5 [1,2,1,11,12] 5
child2.show(); // 6 [1,2,1,11,12] 5
这道题目值得神帝,他波及到的知识点很多,例如 this 的指向、原型、原型链、类的继承、数据类型 等。
解析:
- parent.show(),能够间接取得所需的值,没啥好说的;
- child1.show(),
Child
的构造函数本来是指向Child
的,题目显式将Child
类的原型对象指向了Parent
类的一个实例,须要留神Child.prototype
指向的是Parent
的实例parent
,而不是指向Parent
这个类。 - child2.show(),这个也没啥好说的;
- parent.show(),
parent
是一个Parent
类的实例,Child.prorotype
指向的是Parent
类的另一个实例,两者在堆内存中互不影响,所以上述操作不影响parent
实例,所以输入后果不变; - child1.show(),
child1
执行了change()
办法后,产生了怎么的变动呢? - this.b.push(this.a),因为 this 的动静指向个性,this.b 会指向
Child.prototype
上的 b 数组,this.a 会指向child1
的a属性, 所以Child.prototype.b
变成了[1,2,1,11]; - this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句统一,故后果为child1.a
变为4; - this.c.demo = this.a++,因为
child1
本身属性并没有 c 这个属性,所以此处的this.c
会指向Child.prototype.c
,this.a
值为 4,为原始类型,故赋值操作时会间接赋值,Child.prototype.c.demo
的后果为 4,而this.a
随后自增为5(4 + 1 = 5)。 child2
执行了change()
办法, 而child2
和child1
均是Child
类的实例,所以他们的原型链指向同一个原型对象Child.prototype
, 也就是同一个parent
实例,所以child2.change()
中所有影响到原型对象的语句都会影响child1
的最终输入后果。- this.b.push(this.a),因为 this 的动静指向个性,this.b 会指向
Child.prototype
上的 b 数组,this.a 会指向child2
的a属性, 所以Child.prototype.b
变成了[1,2,1,11,12]; - this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句统一,故后果为child2.a
变为5; - this.c.demo = this.a++,因为
child2
本身属性并没有 c 这个属性,所以此处的this.c
会指向Child.prototype.c
,故执行后果为Child.prototype.c.demo
的值变为child2.a
的值 5,而child2.a
最终自增为6(5 + 1 = 6)。
参考 前端进阶面试题具体解答
僵尸过程和孤儿过程是什么?
- 孤儿过程 :父过程退出了,而它的一个或多个过程还在运行,那这些子过程都会成为孤儿过程。孤儿过程将被 init 过程(过程号为 1) 所收养,并由 init 过程对它们实现状态收集工作。
- 僵尸过程:子过程比父过程先完结,而父过程又没有开释子过程占用的资源,那么子过程的过程描述符依然保留在零碎中,这种过程称之为僵死过程。
代码输入问题
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); // 15
console.log(window.number); // 40
这道题目看清起来有点乱,然而实际上是考查 this 指向的:
- 执行 db1()时,this 指向全局作用域,所以 window.number 4 = 8,而后执行匿名函数,所以 window.number 5 = 40;
- 执行 obj.db1(); 时,this 指向 obj 对象,执行匿名函数,所以 obj.numer * 5 = 15。
说一下数组如何去重, 你有几种办法?
let arr = [1,1,"1","1",true,true,"true",{},{},"{}",null,null,undefined,undefined]
// 办法 1
let uniqueOne = Array.from(new Set(arr)) console.log(uniqueOne)
// 办法 2
let uniqueTwo = arr => {let map = new Map(); // 或者用空对象 let obj = {} 利用对象属性不能反复得个性
let brr = []
arr.forEach( item => {if(!map.has(item)) {// 如果是对象得话就判断 !obj[item]
map.set(item,true) // 如果是对象得话就 obj[item] =true 其余一样
brr.push(item)
}
})
return brr
}
console.log(uniqueTwo(arr))
// 办法 3
let uniqueThree = arr => {let brr = []
arr.forEach(item => {
// 应用 indexOf 返回数组是否蕴含某个值 没有就返回 -1 有就返回下标
if(brr.indexOf(item) === -1) brr.push(item)
// 或者应用 includes 返回数组是否蕴含某个值 没有就返回 false 有就返回 true
if(!brr.includes(item)) brr.push(item)
})
return brr
}
console.log(uniqueThree(arr))
// 办法 4
let uniqueFour = arr => {
// 应用 filter 返回符合条件的汇合
let brr = arr.filter((item,index) => {return arr.indexOf(item) === index
})
return brr
}
console.log(uniqueFour(arr))
Number() 的存储空间是多大?如果后盾发送了一个超过最大本人的数字怎么办
Math.pow(2, 53),53 为有效数字,会产生截断,等于 JS 能反对的最大数字。
代码输入后果
function a() {console.log(this);
}
a.call(null);
打印后果:window 对象
依据 ECMAScript262 标准规定:如果第一个参数传入的对象调用者是 null 或者 undefined,call 办法将把全局对象(浏览器上是 window 对象)作为 this 的值。所以,不论传入 null 还是 undefined,其 this 都是全局对象 window。所以,在浏览器上答案是输入 window 对象。
要留神的是,在严格模式中,null 就是 null,undefined 就是 undefined:
'use strict';
function a() {console.log(this);
}
a.call(null); // null
a.call(undefined); // undefined
js 脚本加载问题,async、defer 问题
- 如果依赖其余脚本和 DOM 后果,应用 defer
- 如果与 DOM 和其余脚本依赖不强时,应用 async
浏览器的次要组成部分
- ⽤户界⾯ 包含地址栏、后退 / 后退按钮、书签菜单等。除了浏览器主窗⼝显示的您申请的⻚⾯外,其余显示的各个局部都属于⽤户界⾯。
- 浏览器引擎 在⽤户界⾯和出现引擎之间传送指令。
- 出现引擎 负责显示申请的内容。如果申请的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
- ⽹络 ⽤于⽹络调⽤,⽐如 HTTP 申请。其接⼝与平台⽆关,并为所有平台提供底层实现。
- ⽤户界⾯后端 ⽤于绘制根本的窗⼝⼩部件,⽐如组合框和窗⼝。其公开了与平台⽆关的通⽤接⼝,⽽在底层使⽤操作系统的⽤户界⾯⽅法。
- JavaScript 解释器。⽤于解析和执⾏ JavaScript 代码。
- 数据存储 这是长久层。浏览器须要在硬盘上保留各种数据,例如 Cookie。新的 HTML 标准 (HTML5) 定义了“⽹络数据库”,这是⼀个残缺(然而轻便)的浏览器内数据库。
值得注意的是,和⼤少数浏览器不同,Chrome 浏览器的每个标签⻚都别离对应⼀个出现引擎实例。每个标签⻚都是⼀个独⽴的过程。
陈说输出 URL 回车后的过程
1. 读取缓存:搜寻本身的 DNS 缓存。(如果 DNS 缓存中找到 IP 地址就跳过了接下来查找 IP 地址步骤,间接拜访该 IP 地址。)
2.DNS 解析: 将域名解析成 IP 地址
3.TCP 连贯:TCP 三次握手,繁难形容三次握手
客户端:服务端你在么?服务端:客户端我在,你要连贯我么?客户端:是的服务端,我要链接。连贯买通,能够开始申请来
4. 发送 HTTP 申请
5. 服务器解决申请并返回 HTTP 报文
6. 浏览器解析渲染页面
7. 断开连接:TCP 四次挥手
对于第六步浏览器解析渲染页面又能够聊聊如果返回的是 html 页面
依据 HTML 解析出 DOM 树
依据 CSS 解析生成 CSS 规定树
联合 DOM 树和 CSS 规定树,生成渲染树
依据渲染树计算每一个节点的信息
依据计算好的信息绘制页面
说一说你用过的 css 布局
gird 布局,layout 布局,flex 布局,双飞翼,圣杯布局等
代码输入问题
function A(){}
function B(a){this.a = a;}
function C(a){if(a){this.a = a;}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a);
console.log(new B().a);
console.log(new C(2).a);
输入后果:1 undefined 2
解析:
- console.log(new A().a),new A()为构造函数创立的对象,自身没有 a 属性,所以向它的原型去找,发现原型的 a 属性的属性值为 1,故该输入值为 1;
- console.log(new B().a),ew B()为构造函数创立的对象,该构造函数有参数 a,但该对象没有传参,故该输入值为 undefined;
- console.log(new C(2).a),new C()为构造函数创立的对象,该构造函数有参数 a,且传的实参为 2,执行函数外部,发现 if 为真,执行 this.a = 2, 故属性 a 的值为 2。
代码输入后果
var length = 10;
function fn() {console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {fn();
arguments[0]();}
};
obj.method(fn, 1);
输入后果:10 2
解析:
- 第一次执行 fn(),this 指向 window 对象,输入 10。
- 第二次执行 arguments[0],相当于 arguments 调用办法,this 指向 arguments,而这里传了两个参数,故输入 arguments 长度为 2。
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
箭头函数和一般函数有啥区别?箭头函数能当构造函数吗?
- 一般函数通过 function 关键字定义,this 无奈联合词法作用域应用,在运行时绑定,只取决于函数的调用形式,在哪里被调用,调用地位。(取决于调用者,和是否独立运行)
-
箭头函数应用被称为“胖箭头”的操作
=>
定义,箭头函数不利用一般函数 this 绑定的四种规定,而是依据外层(函数或全局)的作用域来决定 this,且箭头函数的绑定无奈被批改(new 也不行)。- 箭头函数罕用于回调函数中,包含事件处理器或定时器
- 箭头函数和 var self = this,都试图取代传统的 this 运行机制,将 this 的绑定拉回到词法作用域
- 没有原型、没有 this、没有 super,没有 arguments,没有 new.target
-
不能通过 new 关键字调用
- 一个函数外部有两个办法:[[Call]] 和 [[Construct]],在通过 new 进行函数调用时,会执行 [[construct]] 办法,创立一个实例对象,而后再执行这个函数体,将函数的 this 绑定在这个实例对象上
- 当间接调用时,执行 [[Call]] 办法,间接执行函数体
- 箭头函数没有 [[Construct]] 办法,不能被用作结构函数调用,当应用 new 进行函数调用时会报错。
function foo() {return (a) => {console.log(this.a);
}
}
var obj1 = {a: 2}
var obj2 = {a: 3}
var bar = foo.call(obj1);
bar.call(obj2);
如何进攻 XSS 攻打?
能够看到 XSS 危害如此之大,那么在开发网站时就要做好进攻措施,具体措施如下:
- 能够从浏览器的执行来进行预防,一种是应用纯前端的形式,不必服务器端拼接后返回(不应用服务端渲染)。另一种是对须要插入到 HTML 中的代码做好充沛的本义。对于 DOM 型的攻打,次要是前端脚本的不牢靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能呈现的恶意代码状况进行判断。
- 应用 CSP,CSP 的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行,从而避免恶意代码的注入攻打。
- CSP 指的是内容安全策略,它的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡由浏览器本人来实现。
- 通常有两种形式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的形式
- 对一些敏感信息进行爱护,比方 cookie 应用 http-only,使得脚本无奈获取。也能够应用验证码,防止脚本伪装成用户执行一些操作。