对 HTML 语义化的了解
语义化是指依据内容的结构化(内容语义化),抉择适合的标签(代码语义化)。艰深来讲就是用正确的标签做正确的事件。
语义化的长处如下:
- 对机器敌对,带有语义的文字表现力丰盛,更适宜搜索引擎的爬虫爬取无效信息,有利于 SEO。除此之外,语义类还反对读屏软件,依据文章能够主动生成目录;
- 对开发者敌对,应用语义类标签加强了可读性,构造更加清晰,开发者能清晰的看出网页的构造,便于团队的开发与保护。
常见的语义化标签:
<header></header> 头部
<nav></nav> 导航栏
<section></section> 区块(有语义化的 div)<main></main> 次要区域
<article></article> 次要内容
<aside></aside> 侧边栏
<footer></footer> 底部
代码输入后果
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。
浏览器
浏览器架构
单过程浏览器时代
单过程浏览器是指浏览器的所有功能模块都是运行在同一个过程里,这些模块蕴含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。其实早在 2007 年之前,市面上浏览器都是单过程的
-
毛病
- 不稳固:一个插件的意外解体会引起整个浏览器的解体
- 不晦涩:所有页面的渲染模块、JavaScript 执行环境以及插件都是运行在同一个线程中的,这就意味着同一时刻只能有一个模块能够执行
- 不平安:能够通过浏览器的破绽来获取零碎权限,这些脚本获取零碎权限之后也能够对你的电脑做一些歹意的事件,同样也会引发平安问题
- 以上这些就是过后浏览器的特点,
不稳固,不晦涩,而且不平安
多过程浏览器时代
- 因为
过程是互相隔离的
,所以当一个页面或者插件解体时,影响到的仅仅是以后的页面过程或者插件过程,并不会影响到浏览器和其余页面,这就完满地解决了页面或者插件的解体会导致整个浏览器解体,也就是不稳固的问题 - JavaScript 也是运行在渲染过程中的,所以即便 JavaScript 阻塞了渲染过程,影响到的也只是以后的渲染页面,而并不会影响浏览器和其余页面,因为其余页面的脚本是运行在它们本人的渲染过程中的
- Chrome 把插件过程和渲染过程锁在沙箱外面,这样即便在渲染过程或者插件过程外面执行了恶意程序,恶意程序也无奈冲破沙箱去获取零碎权限。
最新的 Chrome 浏览器包含:
1 个浏览器(Browser)主过程
、1 个 GPU 过程
、1 个网络(NetWork)过程
、多个渲染过程
和多个插件过程
- 浏览器过程。次要负责界面显示、用户交互、子过程治理,同时提供存储等性能。
- 渲染过程。外围工作是将
HTML、CSS
和 JavaScript 转换为用户能够与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该过程中,默认状况下,Chrome 会为每个 Tab 标签创立一个渲染过程。出于平安思考,渲染过程都是运行在沙箱模式下。 - GPU 过程。其实,Chrome 刚开始公布的时候是没有 GPU 过程的。而 GPU 的应用初衷是为了实现 3D CSS 的成果,只是随后网页、Chrome 的 UI 界面都抉择采纳 GPU 来绘制,这使得 GPU 成为浏览器广泛的需要。最初,Chrome 在其多过程架构上也引入了 GPU 过程。
- 网络过程。次要负责页面的网络资源加载,之前是作为一个模块运行在浏览器过程外面的,直至最近才独立进去,成为一个独自的过程。
- 插件过程。次要是负责插件的运行,因插件易解体,所以须要通过插件过程来隔离,以保障插件过程解体不会对浏览器和页面造成影响
代码输入后果
var a=3;
function c(){alert(a);
}
(function(){
var a=4;
c();})();
js 中变量的作用域链与定义时的环境无关,与执行时无关。执行环境只会扭转 this、传递的参数、全局变量等
如何优化动画?
对于如何优化动画,咱们晓得,个别状况下,动画须要频繁的操作 DOM,就就会导致页面的性能问题,咱们能够将动画的 position
属性设置为 absolute
或者fixed
,将动画脱离文档流,这样他的回流就不会影响到页面了。
new 一个函数产生了什么
结构调用:
- 发明一个全新的对象
- 这个对象会被执行 [[Prototype]] 连贯,将这个新对象的 [[Prototype]] 链接到这个构造函数.prototype 所指向的对象
- 这个新对象会绑定到函数调用的 this
- 如果函数没有返回其余对象,那么 new 表达式中的函数调用会主动返回这个新对象
JS 数据类型
根本类型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020)
援用类型:Object,对象子类型(Array,Function)
参考:前端进阶面试题具体解答
协商缓存和强缓存的区别
(1)强缓存
应用强缓存策略时,如果缓存资源无效,则间接应用缓存资源,不用再向服务器发动申请。
强缓存策略能够通过两种形式来设置,别离是 http 头信息中的 Expires 属性和 Cache-Control 属性。
(1)服务器通过在响应头中增加 Expires 属性,来指定资源的过期工夫。在过期工夫以内,该资源能够被缓存应用,不用再向服务器发送申请。这个工夫是一个相对工夫,它是服务器的工夫,因而可能存在这样的问题,就是客户端的工夫和服务器端的工夫不统一,或者用户能够对客户端工夫进行批改的状况,这样就可能会影响缓存命中的后果。
(2)Expires 是 http1.0 中的形式,因为它的一些毛病,在 HTTP 1.1 中提出了一个新的头部属性就是 Cache-Control 属性,它提供了对资源的缓存的更准确的管制。它有很多不同的值,
Cache-Control
可设置的字段:
public
:设置了该字段值的资源示意能够被任何对象(包含:发送申请的客户端、代理服务器等等)缓存。这个字段值不罕用,个别还是应用 max-age= 来准确管制;private
:设置了该字段值的资源只能被用户浏览器缓存,不容许任何代理服务器缓存。在理论开发当中,对于一些含有用户信息的 HTML,通常都要设置这个字段值,防止代理服务器 (CDN) 缓存;no-cache
:设置了该字段须要先和服务端确认返回的资源是否产生了变动,如果资源未发生变化,则间接应用缓存好的资源;no-store
:设置了该字段示意禁止任何缓存,每次都会向服务端发动新的申请,拉取最新的资源;max-age=
:设置缓存的最大有效期,单位为秒;s-maxage=
:优先级高于 max-age=,仅实用于共享缓存(CDN),优先级高于 max-age 或者 Expires 头;max-stale[=]
:设置了该字段表明客户端违心接管曾经过期的资源,然而不能超过给定的工夫限度。
一般来说只须要设置其中一种形式就能够实现强缓存策略,当两种形式一起应用时,Cache-Control 的优先级要高于 Expires。
no-cache 和 no-store 很容易混同:
- no-cache 是指先要和服务器确认是否有资源更新,在进行判断。也就是说没有强缓存,然而会有协商缓存;
- no-store 是指不应用任何缓存,每次申请都间接从服务器获取资源。
(2)协商缓存
如果命中强制缓存,咱们无需发动新的申请,间接应用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用了。
下面曾经说到了,命中协商缓存的条件有两个:
max-age=xxx
过期了- 值为
no-store
应用协商缓存策略时,会先向服务器发送一个申请,如果资源没有产生批改,则返回一个 304 状态,让浏览器应用本地的缓存正本。如果资源产生了批改,则返回批改后的资源。
协商缓存也能够通过两种形式来设置,别离是 http 头信息中的 Etag 和Last-Modified 属性。
(1)服务器通过在响应头中增加 Last-Modified 属性来指出资源最初一次批改的工夫,当浏览器下一次发动申请时,会在申请头中增加一个 If-Modified-Since 的属性,属性值为上一次资源返回时的 Last-Modified 的值。当申请发送到服务器后服务器会通过这个属性来和资源的最初一次的批改工夫来进行比拟,以此来判断资源是否做了批改。如果资源没有批改,那么返回 304 状态,让客户端应用本地的缓存。如果资源曾经被批改了,则返回批改后的资源。应用这种办法有一个毛病,就是 Last-Modified 标注的最初批改工夫只能准确到秒级,如果某些文件在 1 秒钟以内,被批改屡次的话,那么文件已将扭转了然而 Last-Modified 却没有扭转,这样会造成缓存命中的不精确。
(2)因为 Last-Modified 的这种可能产生的不准确性,http 中提供了另外一种形式,那就是 Etag 属性。服务器在返回资源的时候,在头信息中增加了 Etag 属性,这个属性是资源生成的惟一标识符,当资源产生扭转的时候,这个值也会产生扭转。在下一次资源申请时,浏览器会在申请头中增加一个 If-None-Match 属性,这个属性的值就是上次返回的资源的 Etag 的值。服务接管到申请后会依据这个值来和资源以后的 Etag 的值来进行比拟,以此来判断资源是否产生扭转,是否须要返回资源。通过这种形式,比 Last-Modified 的形式更加准确。
当 Last-Modified 和 Etag 属性同时呈现的时候,Etag 的优先级更高。应用协商缓存的时候,服务器须要思考负载平衡的问题,因而多个服务器上资源的 Last-Modified 应该保持一致,因为每个服务器上 Etag 的值都不一样,因而在思考负载平衡时,最好不要设置 Etag 属性。
总结:
强缓存策略和协商缓存策略在缓存命中时都会间接应用本地的缓存正本,区别只在于协商缓存会向服务器发送一次申请。它们缓存不命中时,都会向服务器发送申请来获取资源。在理论的缓存机制中,强缓存策略和协商缓存策略是一起单干应用的。浏览器首先会依据申请的信息判断,强缓存是否命中,如果命中则间接应用资源。如果不命中则依据头信息向服务器发动申请,应用协商缓存,如果协商缓存命中的话,则服务器不返回资源,浏览器间接应用本地资源的正本,如果协商缓存不命中,则浏览器返回最新的资源给浏览器。
浏览器的次要组成部分
- ⽤户界⾯ 包含地址栏、后退 / 后退按钮、书签菜单等。除了浏览器主窗⼝显示的您申请的⻚⾯外,其余显示的各个局部都属于⽤户界⾯。
- 浏览器引擎 在⽤户界⾯和出现引擎之间传送指令。
- 出现引擎 负责显示申请的内容。如果申请的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
- ⽹络 ⽤于⽹络调⽤,⽐如 HTTP 申请。其接⼝与平台⽆关,并为所有平台提供底层实现。
- ⽤户界⾯后端 ⽤于绘制根本的窗⼝⼩部件,⽐如组合框和窗⼝。其公开了与平台⽆关的通⽤接⼝,⽽在底层使⽤操作系统的⽤户界⾯⽅法。
- JavaScript 解释器。⽤于解析和执⾏ JavaScript 代码。
- 数据存储 这是长久层。浏览器须要在硬盘上保留各种数据,例如 Cookie。新的 HTML 标准 (HTML5) 定义了“⽹络数据库”,这是⼀个残缺(然而轻便)的浏览器内数据库。
值得注意的是,和⼤少数浏览器不同,Chrome 浏览器的每个标签⻚都别离对应⼀个出现引擎实例。每个标签⻚都是⼀个独⽴的过程。
Cookie 有哪些字段,作用别离是什么
Cookie 由以下字段组成:
- Name:cookie 的名称
- Value:cookie 的值,对于认证 cookie,value 值包含 web 服务器所提供的拜访令牌;
- Size:cookie 的大小
- Path:能够拜访此 cookie 的页面门路。比方 domain 是 abc.com,path 是
/test
,那么只有/test
门路下的页面能够读取此 cookie。 - Secure:指定是否应用 HTTPS 平安协定发送 Cookie。应用 HTTPS 平安协定,能够爱护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。该办法也可用于 Web 站点的身份甄别,即在 HTTPS 的连贯建设阶段,浏览器会查看 Web 网站的 SSL 证书的有效性。然而基于兼容性的起因(比方有些网站应用自签订的证书)在检测到 SSL 证书有效时,浏览器并不会立刻终止用户的连贯申请,而是显示平安危险信息,用户仍能够抉择持续拜访该站点。
- Domain:能够拜访该 cookie 的域名,Cookie 机制并未遵循严格的同源策略,容许一个子域能够设置或获取其父域的 Cookie。当须要实现单点登录计划时,Cookie 的上述个性十分有用,然而也减少了 Cookie 受攻打的危险,比方攻击者能够借此动员会话定置攻打。因此,浏览器禁止在 Domain 属性中设置.org、.com 等通用顶级域名、以及在国家及地区顶级域下注册的二级域名,以减小攻打产生的范畴。
- HTTP:该字段蕴含
HTTPOnly
属性,该属性用来设置 cookie 是否通过脚本来拜访,默认为空,即能够通过脚本拜访。在客户端是不能通过 js 代码去设置一个 httpOnly 类型的 cookie 的,这种类型的 cookie 只能通过服务端来设置。该属性用于避免客户端脚本通过document.cookie
属性拜访 Cookie,有助于爱护 Cookie 不被跨站脚本攻打窃取或篡改。然而,HTTPOnly 的利用仍存在局限性,一些浏览器能够阻止客户端脚本对 Cookie 的读操作,但容许写操作;此外大多数浏览器仍容许通过 XMLHTTP 对象读取 HTTP 响应中的 Set-Cookie 头。 - Expires/Max-size:此 cookie 的超时工夫。若设置其值为一个工夫,那么当达到此工夫后,此 cookie 生效。不设置的话默认值是 Session,意思是 cookie 会和 session 一起生效。当浏览器敞开(不是浏览器标签页,而是整个浏览器) 后,此 cookie 生效。
总结: 服务器端能够应用 Set-Cookie 的响应头部来配置 cookie 信息。一条 cookie 包含了 5 个属性值 expires、domain、path、secure、HttpOnly。其中 expires 指定了 cookie 生效的工夫,domain 是域名、path 是门路,domain 和 path 一起限度了 cookie 可能被哪些 url 拜访。secure 规定了 cookie 只能在确保安全的状况下传输,HttpOnly 规定了这个 cookie 只能被服务器拜访,不能应用 js 脚本拜访。
如果一个构造函数,bind 了一个对象,用这个构造函数创立出的实例会继承这个对象的属性吗?为什么?
不会继承,因为依据 this 绑定四大规定,new 绑定的优先级高于 bind 显示绑定,通过 new 进行结构函数调用时,会创立一个新对象,这个新对象会代替 bind 的对象绑定,作为此函数的 this,并且在此函数没有返回对象的状况下,返回这个新建的对象
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"));
});
}
常见浏览器所用内核
(1)IE 浏览器内核:Trident 内核,也是俗称的 IE 内核;
(2)Chrome 浏览器内核:统称为 Chromium 内核或 Chrome 内核,以前是 Webkit 内核,当初是 Blink 内核;
(3)Firefox 浏览器内核:Gecko 内核,俗称 Firefox 内核;
(4)Safari 浏览器内核:Webkit 内核;
(5)Opera 浏览器内核:最后是本人的 Presto 内核,起初退出谷歌大军,从 Webkit 又到了 Blink 内核;
(6)360 浏览器、猎豹浏览器内核:IE + Chrome 双内核;
(7)搜狗、漫游、QQ 浏览器内核:Trident(兼容模式)+ Webkit(高速模式);
(8)百度浏览器、世界之窗内核:IE 内核;
(9)2345 浏览器内核:如同以前是 IE 内核,当初也是 IE + Chrome 双内核了;
(10)UC 浏览器内核:这个众口不一,UC 说是他们本人研发的 U3 内核,但如同还是基于 Webkit 和 Trident,还有说是基于火狐内核。
网络劫持有哪几种,如何防备?
⽹络劫持分为两种:
(1)DNS 劫持: (输⼊京东被强制跳转到淘宝这就属于 dns 劫持)
- DNS 强制解析: 通过批改运营商的本地 DNS 记录,来疏导⽤户流量到缓存服务器
- 302 跳转的⽅式: 通过监控⽹络出⼝的流量,分析判断哪些内容是能够进⾏劫持解决的, 再对劫持的内存发动 302 跳转的回复,疏导⽤户获取内容
(2)HTTP 劫持: (拜访⾕歌然而⼀直有贪玩蓝⽉的⼴告), 因为 http 明⽂传输, 运营商会批改你的 http 响应内容(即加⼴告)
DNS 劫持因为涉嫌守法,曾经被监管起来,当初很少会有 DNS 劫持,⽽ http 劫持仍然⾮常盛⾏,最无效的方法就是全站 HTTPS,将 HTTP 加密,这使得运营商⽆法获取明⽂,就⽆法劫持你的响应内容。
代码输入问题
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)。
什么是 XSS 攻打?
(1)概念
XSS 攻打指的是跨站脚本攻打,是一种代码注入攻打。攻击者通过在网站注入歹意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。
XSS 的实质是因为网站没有对恶意代码进行过滤,与失常的代码混合在一起了,浏览器没有方法分辨哪些脚本是可信的,从而导致了恶意代码的执行。
攻击者能够通过这种攻击方式能够进行以下操作:
- 获取页面的数据,如 DOM、cookie、localStorage;
- DOS 攻打,发送正当申请,占用服务器资源,从而使用户无法访问服务器;
- 毁坏页面构造;
- 流量劫持(将链接指向某网站);
(2)攻打类型
XSS 能够分为存储型、反射型和 DOM 型:
- 存储型指的是歹意脚本会存储在指标服务器上,当浏览器申请数据时,脚本从服务器传回并执行。
- 反射型指的是攻击者诱导用户拜访一个带有恶意代码的 URL 后,服务器端接收数据后处理,而后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终实现 XSS 攻打。
- DOM 型指的通过批改页面的 DOM 节点造成的 XSS。
1)存储型 XSS 的攻打步骤:
- 攻击者将恶意代码提交到⽬标⽹站的数据库中。
- ⽤户关上⽬标⽹站时,⽹站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
这种攻打常⻅于带有⽤户保留数据的⽹站性能,如论坛发帖、商品评论、⽤户私信等。
2)反射型 XSS 的攻打步骤:
- 攻击者结构出非凡的 URL,其中蕴含恶意代码。
- ⽤户关上带有恶意代码的 URL 时,⽹站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- ⽤户浏览器接管到响应后解析执⾏,混在其中的恶意代码也被执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库⾥,反射型 XSS 的恶意代码存在 URL ⾥。
反射型 XSS 破绽常⻅于通过 URL 传递参数的性能,如⽹站搜寻、跳转等。因为须要⽤户被动关上歹意的 URL 能力⽣效,攻击者往往会联合多种⼿段诱导⽤户点击。
3)DOM 型 XSS 的攻打步骤:
- 攻击者结构出非凡的 URL,其中蕴含恶意代码。
- ⽤户关上带有恶意代码的 URL。
- ⽤户浏览器接管到响应后解析执⾏,前端 JavaScript 取出 URL 中的恶意代码并执⾏。
- 恶意代码窃取⽤户数据并发送到攻击者的⽹站,或者假冒⽤户的⾏为,调⽤⽬标⽹站接⼝执⾏攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻打中,取出和执⾏恶意代码由浏览器端实现,属于前端 JavaScript ⾃身的安全漏洞,⽽其余两种 XSS 都属于服务端的安全漏洞。
渲染过程中遇到 JS 文件如何解决?
JavaScript 的加载、解析与执行会阻塞文档的解析,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JavaScript,那么它会暂停文档的解析,将控制权移交给 JavaScript 引擎,等 JavaScript 引擎运行结束,浏览器再从中断的中央复原持续解析文档。也就是说,如果想要首屏渲染的越快,就越不应该在首屏就加载 JS 文件,这也是都倡议将 script 标签放在 body 标签底部的起因。当然在当下,并不是说 script 标签必须放在底部,因为你能够给 script 标签增加 defer 或者 async 属性。
数组去重
实现代码如下:
function uniqueArr(arr) {return [...new Set(arr)];
}
symbol
有什么用途
能够用来示意一个举世无双的变量避免命名抵触。然而面试官问还有吗?我没想出其余的用途就间接答我不晓得了,还能够利用 symbol
不会被惯例的办法(除了 Object.getOwnPropertySymbols
外)遍历到,所以能够用来模仿公有变量。
次要用来提供遍历接口,安排了 symbol.iterator
的对象才能够应用 for···of
循环,能够对立解决数据结构。调用之后回返回一个遍历器对象,蕴含有一个 next 办法,应用 next 办法后有两个返回值 value 和 done 别离示意函数以后执行地位的值和是否遍历结束。
Symbol.for() 能够在全局拜访 symbol
冒泡排序 – 工夫复杂度 n^2
题目形容: 实现一个冒泡排序
实现代码如下:
function bubbleSort(arr) {
// 缓存数组长度
const len = arr.length;
// 外层循环用于管制从头到尾的比拟 + 替换到底有多少轮
for (let i = 0; i < len; i++) {
// 内层循环用于实现每一轮遍历过程中的反复比拟 + 替换
for (let j = 0; j < len - 1; j++) {
// 若相邻元素后面的数比前面的大
if (arr[j] > arr[j + 1]) {
// 替换两者
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
// 返回数组
return arr;
}
// console.log(bubbleSort([3, 6, 2, 4, 1]));