# 前端面试题(一)
# 关于 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>
/* 这个div里面最终的字体颜色是什么? */
.box {
color: #999;
}
.box {
color: #333 !important;
}
.box2 {
color: #666;
}
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>
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
2
3
4
# 下面代码运行结果正确的是
var c = 1;
var o = { c };
c = 8;
console.log(o.c);
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]
2
3
4
5
在具名立即执行函数表达式内部修改其名字会静默失败, 所以b=20
不会成功执行, 而且名字只在表达式内部可以使用, 所以打印结果是函数b
. 如果在内部加上严格模式, 则会报错. 从某种程度上可以等同于:
const foo = (function() {
foo = 10;
console.log(foo);
})(foo)(); // Uncaught TypeError: Assignment to constant variable.
2
3
4
# 立即执行表达式和变量提升
var a = 10;
(function() {
console.log(a);
a = 5;
console.log(window.a);
var a = 20;
console.log(a);
})();
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
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: ƒ]
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}
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
2
3
4
5
6
7
8
ECMAScript 中所有函数的参数都是按值传递的. 也就是说, 把函数外部的值复制给函数内部的参数,就和把值从一个 变量复制到另一个变量一样. 所以在changeObjProperty
内部有:
var o = website; // 形参o与website指向了同一个内存地址
o.siteUrl = "xxx"; // 内存记录siteUrl的值为xxx
o = new Object(); //改变o的指向, 注意!site的指向并没有改变!
2
3
# 字符串
String("11") == new String("11"); // true
String("11") === new String("11"); // false
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();
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();
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
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配置 →