# 前端面试题(一)
# 关于 DOMContentLoaded 和 load 事件说法正确的是
- DOMContentLoaded 事件比 load 事件更早执行
- load 事件比 DOMContentLoaded 事件更早执行
- dom 文档完全加载完后执行 load 事件
DOMContentLoaded
发生在 HTML 文件下载并解析后,load
发生在 HTML, CSS, JS, 图片等资源下载并解析后
# CSS 优先级
<div class="box box1 box2" style="color:#222">hello</div>
Copied!
/* 这个div里面最终的字体颜色是什么? */ .box { color: #999; } .box { color: #333 !important; } .box2 { color: #666; }
Copied!
2
3
4
5
6
7
8
9
10
11
12
- #999
- #222
- #333
- #666
!important
> 内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器
# a 标签的 href 和 onclick 属性同时存在时哪个先触发
- href 先执行
- onclick 先执行
- 同时执行
应该是 onclick 属性先触发,判断依据是在 onclick 中使用 preventDefault 方法可以阻止 a 标签的跳转
# 下列资源加载顺序哪种是不可能的出现的
<html lang="en"> <head> <title>Document</title> <link rel="prefetch" href="a.js" /> <link rel="preload" href="b.css" as="style" /> <link rel="preload" href="c.js" as="script" /> <link rel="stylesheet" href="b.css" /> <link rel="stylesheet" href="d.css" /> </head> <body> <script src="c.js"></script> </body> </html>
Copied!
2
3
4
5
6
7
8
9
10
11
12
13
- d.css, b.css, c.js, a.js
- b.css, d.css, c.js, a.js
- a.js, b.css, c.js, d.css
- c.js, d.css, b.css, a.js
一般来说,最好使用 preload 来加载你最重要的资源,比如图像,CSS,JavaScript 和字体文件.preload 指令事实上克服了这个限制并且允许预加载在 CSS 和 JavaScript 中定义的资源,并允许决定何时应用每个资源. Prefetch 是一个低优先级的资源提示,允许浏览器在后台(空闲时)获取将来可能用得到的资源,并且将他们存储在浏览器的缓存中. 在本题里, prefetch 不会跑到 preload 之前加载.
# -1>>32 && -1>>>32
(-1 >> (32 - // -1 1)) >>> 32; // 4294967295
Copied!
2
3
4
# 下面代码运行结果正确的是
var c = 1; var o = { c }; c = 8; console.log(o.c);
Copied!
2
3
4
- 8
- 1
基本类型(
string
,number
,boolean
)在对象里的存储方式是按值存储. 所以o.c
存的是1
, 即使c
的值改变了,o.c
依然不变.
# difference between escape
, encodeURI
, encodeURIComponent
encURI | encURIComp | escape |
---|---|---|
/ | %2F | / |
: | %3A | %3A |
# display:none
和visibility:hidden
的区别
# 1. 是否占据空间
一个(display:none
)不会在渲染树中出现,一个(visibility :hidden
)会.
# 2. 继承性
设置display: none
的元素的子元素不会继承这个属性, 而visibility: none
则会, 除非子元素设置visibility: visible
可以改变
# 3. 是否渲染
display:none
,会触发 reflow(回流),进行渲染.
visibility:hidden
,只会触发 repaint(重绘),因为没有发现位置变化,不进行渲染.
# 页面导入样式时,使用 link 和@import 有什么区别
- link 是 HTML 标签,@import 是 css 提供的.
- link 引入的样式页面加载时同时加载,@import 引入的样式需等页面加载完成后再加载.
- link 没有兼容性问题,@import 不兼容 ie5 以下.
- link 可以通过 js 操作 DOM 动态引入样式表改变样式,而@import 不可以.
# 修改非匿名立即执行表达式的名字
有这样一段代码:
var b = 10; (function b() { b = 20; console.log(b); })(); //[Function b]
Copied!
2
3
4
5
在具名立即执行函数表达式内部修改其名字会静默失败, 所以b=20
不会成功执行, 而且名字只在表达式内部可以使用, 所以打印结果是函数b
. 如果在内部加上严格模式, 则会报错. 从某种程度上可以等同于:
const foo = (function() { foo = 10; console.log(foo); })(foo)(); // Uncaught TypeError: Assignment to constant variable.
Copied!
2
3
4
# 立即执行表达式和变量提升
var a = 10; (function() { console.log(a); a = 5; console.log(window.a); var a = 20; console.log(a); })();
Copied!
2
3
4
5
6
7
8
在全局作用域var a = undefined; a =10
, 在立即执行表达式里:
var a = undefined; console.log(a); // undefined a = 5; console.log(window.a); // 10 a = 20; console.log(a); //20
Copied!
2
3
4
5
6
# 类数组里使用push
方法
var obj = { "2": 3, "3": 4, length: 2, splice: Array.prototype.splice, push: Array.prototype.push }; obj.push(1); obj.push(2); console.log(obj); // Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]
Copied!
2
3
4
5
6
7
8
9
10
11
push
方法根据其数组或类数组的length
属性, 在其后一位添加数据. 在这个题里, length
为2
, 那么执行obj.push(1)
时, 会在length
为3
, 也就是索引在2
的地方添加1
. 又因为索引0
, 1
都是empty
, 索引2
为3
, 于是改变为1
, 然后length
加 1, 重复这个过程, 索引3
为 2, length
为 4
# .
运算符与=
运算符优先级和从右到左的赋值问题
var a = { n: 1 }; var b = a; a.x = a = { n: 2 }; console.log(a.x); console.log(b.x); // undefined {n:2}
Copied!
2
3
4
5
6
7
首先, a 和 b 同时引用了{n:2}对象, 接着执行到 a.x = a = {n:2}语句, 尽管赋值是从右到左的没错, 但是.的优先级比=要高, 所以这里首先执行 a.x, 相当于为 a(或者 b)所指向的{n:1}对象新增了一个属性 x, 即此时对象将变为{n:1;x:undefined}. 之后按正常情况, 从右到左进行赋值, 此时执行 a ={n:2}的时候, a 的引用改变, 指向了新对象{n:2},而 b 依然指向的是旧对象. 之后执行 a.x = {n:2}的时候, 并不会重新解析一遍 a, 而是沿用最初解析 a.x 时候的 a, 也即旧对象, 故此时旧对象的 x 的值为{n:2}, 旧对象为 {n:1;x:{n:2}}, 它被 b 引用着. 后面输出 a.x 的时候, 又要解析 a 了, 此时的 a 是指向新对象的 a, 而这个新对象是没有 x 属性的, 故访问时输出 undefined;而访问 b.x 的时候, 将输出旧对象的 x 的值, 即{n:2}.
# ES6 代码转成 ES5 代码的实现思路是什么
- 将将代码字符串解析成抽象语法树,即所谓的 AST
- 对 AST 进行处理,在这个阶段可以对 ES6 代码进行相应转换,即转成 ES5 代码
- 根据处理后的 AST 再生成代码字符串
# 在函数内部改变形参
function changeObjProperty(o) { o.siteUrl = "http://www.baidu.com"; o = new Object(); o.siteUrl = "http://www.google.com"; } let webSite = new Object(); changeObjProperty(webSite); console.log(webSite.siteUrl); //baidu.com
Copied!
2
3
4
5
6
7
8
ECMAScript 中所有函数的参数都是按值传递的. 也就是说, 把函数外部的值复制给函数内部的参数,就和把值从一个 变量复制到另一个变量一样. 所以在changeObjProperty
内部有:
var o = website; // 形参o与website指向了同一个内存地址 o.siteUrl = "xxx"; // 内存记录siteUrl的值为xxx o = new Object(); //改变o的指向, 注意!site的指向并没有改变!
Copied!
2
3
# 字符串
String("11") == new String("11"); // true String("11") === new String("11"); // false
Copied!
2
new String()
返回的是对象
==
的时候,实际运行的是 String('11') == new String('11').toString();
===
不再赘述.
# "a" + + "b"
"aNaNb"
# 同步, 异步, Promise
执行顺序
function wait() { return new Promise(resolve => setTimeout(resolve, 10 * 1000)); } async function main() { console.time(); const x = wait(); const y = wait(); const z = wait(); await x; await y; await z; console.timeEnd(); } main();
Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function wait() { return new Promise(resolve => setTimeout(resolve, 10 * 1000)); } async function main() { console.time(); const x = await wait(); // 每个都是都执行完才结,包括setTimeout(10*1000)的执行时间 const y = await wait(); // 执行顺序 x->y->z 同步执行,x 与 setTimeout 属于同步执行 const z = await wait(); console.timeEnd(); // default: 30099.47705078125ms console.time(); const x1 = wait(); // x1,y1,z1 同时异步执行, 包括setTimeout(10*1000)的执行时间 const y1 = wait(); // x1 与 setTimeout 属于同步执行 const z1 = wait(); await x1; await y1; await z1; console.timeEnd(); // default: 10000.67822265625ms console.time(); const x2 = wait(); // x2,y2,z2 同步执行,但是不包括setTimeout(10*1000)的执行时间 const y2 = wait(); // x2 与 setTimeout 属于异步执行 const z2 = wait(); x2, y2, z2; console.timeEnd(); // default: 0.065185546875ms } main();
Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// new Promise(xx)相当于同步任务, 会立即执行, .then后面的是微任务 console.log('----------------- start -----------------'); setTimeout(() => { console.log('setTimeout'); }, 0) new Promise((resolve, reject) =>{ // new Promise(xx)相当于同步任务, 会立即执行, .then后面的是微任务 for (var i = 0; i < 5; i++) { console.log(i); } resolve(); }).then(() => { console.log('promise实例成功回调执行'); }) console.log('----------------- end -----------------'); > ----------------- start ----------------- > 0 > 1 > 2 > 3 > 4 > ----------------- end ----------------- > promise实例成功回调执行 > setTimeout
Copied!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# {a: 8}=={a: 8} //false
← Git命令 我的git alias配置 →