# 前端面试题(一)

# 关于 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>
1
/* 这个div里面最终的字体颜色是什么? */
.box {
  color: #999;
}

.box {
  color: #333 !important;
}

.box2 {
  color: #666;
}
1
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>
1
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
1
2
3
4

# 下面代码运行结果正确的是

var c = 1;
var o = { c };
c = 8;
console.log(o.c);
1
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:nonevisibility:hidden的区别

# 1. 是否占据空间

一个(display:none)不会在渲染树中出现,一个(visibility :hidden)会.

# 2. 继承性

设置display: none的元素的子元素不会继承这个属性, 而visibility: none则会, 除非子元素设置visibility: visible可以改变

# 3. 是否渲染

display:none,会触发 reflow(回流),进行渲染. visibility:hidden,只会触发 repaint(重绘),因为没有发现位置变化,不进行渲染.

  1. link 是 HTML 标签,@import 是 css 提供的.
  2. link 引入的样式页面加载时同时加载,@import 引入的样式需等页面加载完成后再加载.
  3. link 没有兼容性问题,@import 不兼容 ie5 以下.
  4. link 可以通过 js 操作 DOM 动态引入样式表改变样式,而@import 不可以.

# 修改非匿名立即执行表达式的名字

有这样一段代码:

var b = 10;
(function b() {
  b = 20;
  console.log(b);
})(); //[Function b]
1
2
3
4
5

在具名立即执行函数表达式内部修改其名字会静默失败, 所以b=20不会成功执行, 而且名字只在表达式内部可以使用, 所以打印结果是函数b. 如果在内部加上严格模式, 则会报错. 从某种程度上可以等同于:

const foo = (function() {
  foo = 10;
  console.log(foo);
})(foo)(); // Uncaught TypeError: Assignment to constant variable.
1
2
3
4

# 立即执行表达式和变量提升

var a = 10;
(function() {
  console.log(a);
  a = 5;
  console.log(window.a);
  var a = 20;
  console.log(a);
})();
1
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
1
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: ƒ]
1
2
3
4
5
6
7
8
9
10
11

push方法根据其数组或类数组的length属性, 在其后一位添加数据. 在这个题里, length2, 那么执行obj.push(1)时, 会在length3, 也就是索引2的地方添加1. 又因为索引0, 1都是empty, 索引23, 于是改变为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}
1
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}. 1

# 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
1
2
3
4
5
6
7
8

ECMAScript 中所有函数的参数都是按值传递的. 也就是说, 把函数外部的值复制给函数内部的参数,就和把值从一个 变量复制到另一个变量一样. 所以在changeObjProperty内部有:

var o = website; // 形参o与website指向了同一个内存地址
o.siteUrl = "xxx"; // 内存记录siteUrl的值为xxx
o = new Object(); //改变o的指向, 注意!site的指向并没有改变!
1
2
3

# 字符串

String("11") == new String("11"); // true
String("11") === new String("11"); // false
1
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();
1
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();
1
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
1
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