我是蒟蒻,大佬求放过:sob:

BY2017秋

Level 0

尝试定义一个复制函数,接受上图所示的类似结构(只有一个根节点),返回一个全新的结构相同的对象

考点:数据结构

这题应该是定义一个可以复制二叉树的函数,不过数据结构目前还没学到这…


Level 1

请使用尽可能多的CSS选择器及伪类选中如下的”I love bingyan”,并将其变为红色(规则主体可只写一次)

考点:CSS选择器

上方的代码由自己补全:

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
29
30
31
32
33
34
35
36
37
<html>
<head>
<style>
/* 元素选择器 span{} √
* id选择器 #{} √
* 类选择器 .{] √
* 通配选择器 *{} ×
* 交/并集选择器,子元素,后代,兄弟 此处不考虑
* 重点练习伪类选择器:(child和type的区别见下方)
*/
span:first-child{
color:red;
}
span:first-of-type{
color:red;
}
span:nth-child(1){
color: red;
}
span:nth-of-type(1){
color: red;
}

</style>
</head>
<!--------------------------------------------------------------------------------------------------------------------->
<body>
<div class="hello" id="world" data-love="yes">
<p>
DO NOT SELECT ME.
</p>
<span>
I love bingyan.
</span>
</div>
</body>
</html>

css选择器中:first-child与:first-of-type的区别:总的来说,child系匹配结构上的第n个子元素,type系匹配的是某父元素下相同类型子元素中的第一个。


盒模型、border-box

请用图形方式展现如下CSS代码产生的盒模型及尺寸

考点:CSS盒子模型,border-box

1
2
3
4
5
6
7
8
9
10
11
<style>
div{
margin: 10px 20px 30px;
padding-top: 20px;
padding-left: 10px;
padding-right: 10px;
width: 100px;
height: 100px;
box-sizing: border-box;
}
</style>

三个方向的margin,分别代表:上、左右、下

  • 之前关于border-box的理解不够深刻:经过测试,对content-box修改padding值会影响盒子的大小,但对于border-box,只要padding不超过width或height,对盒子的大小没有影响

(擅自加了背景颜色和边框,右侧为content-box对比版)

  • 用开发者工具看一看:


由于一般情况下padding的值默认为0,又没有设置padding-bottom,所以padding-bottom == 0,而height == 100 - 20 = 80px


双栏等宽布局

使用HTML和CSS实现一个双栏等宽布局

考点:HTML和CSS实现网页布局
首先来了解一下什么是“双栏布局”,这个布局方式见过,但这个名词我还没听说过:kokodayo

用box-shadow将两栏隔开

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
29
30
31
32
33
34
!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.wrapper{
width: 1210px;
margin: 100px auto;
}
.box1{
width: 600px;
height: 400px;
margin-right: 10px;
box-shadow: 1px 1px 5px rgba(0, 0, 0, .3);
float: left;
}
.box2{
width: 600px;
height: 400px;
box-shadow: 1px 1px 5px rgba(0, 0, 0, .3);
float: left;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="box1"></div>
<div class="box2"></div>
</div>

</body>
</html>

效果展示:
在这里插入图片描述

JS选中HTML元素的方法


列出在JavaScript中可以选中HTML元素的方法,他们的返回值分别是什么类型

  • getElementById,返回值是id为某个值的对象
  • getElementsByTagName,返回值是封装了标签名为某个值的对象的伪数组
  • getElementsByName,返回值是封装了name为某个值的对象的伪数组
  • getElementsByClassName,返回值是封装了class为某个值的对象的伪数组

突然想到一个问题:type=”text/javascript”是必须的吗:kokodayo


列出你所知道的JavaScript基本类型

USONB you are so niubility (滑稽)

u undefined
s string symbol
o object(引用类型)
n null number
b boolean

继续思考,怎么对object进行分类:kokodayo


css三角形

请思考如何在网页中显示一个三角形,列出尽可能多的方法(请放飞自我)

请参考我的这篇博客:kokodayo

不过里面只有两种方法,最常见的方法估计还是第一种


Level 2

表单元素

列出尽可能多的专用于创建一个表单的HTML元素(如table,tr)

w3school表格
w3school表单

table、tbody、td、tr、th(表头)

form、input、button

表单的属性众多:

  • form的属性:action、method、name、target、autocomplete
  • input的属性:type

(表单相关内容在学习了nodejs后再复习更佳)


垂直居中

如何让一个元素相对其父元素垂直居中

可以参考:HTML/CSS 实现居中的方法

方法一:绝对定位+margin
方法二:table-cell和align管垂直+margin管水平
方法三:绝对定位和平移
补充:方法四:弹性盒中的垂直居中


行内和块元素的区别

行内元素和块级元素有什么区别

  1. 行内元素可以排在一行中,块级元素独占一行
  2. 行内元素只能包含数据和其他行内元素,而块级元素可以包含行内元素和其他块级元素
  3. 行内元素不能设置宽高,但块级元素可以

JS创建对象并添加属性

列出在JavaScript中创建一个对象并给其增加一个可读取的属性的方法

先创建对象再设置属性:

1
2
3
var obj = new Object();
obj.attr1 = "attr1"
obj.attr2 = 1;

对象字面量:

1
2
3
4
var obj = {
attr1 = "attr1",
attr2 = 1;
}

原型链——Array和Object

JavaScript的Array和Object之间有什么关系

参考:JS对象分类

Array是Object,
Object是Array的原型,Array是Object的实例(更准确的说法是,Object.prototype是Array.prototype的原型,反过来是实例…)

参考:js中Object与Function与Array的关系图

特别喜欢这个博客说的一句话:万物皆对象,所有的函数都是function的实例


Level 3

new的过程

在JavaScript中使用new运算符创建一个对象的过程中发生了什么

参考博客1
参考博客2
数组的shift方法
阮一峰博客

过程四(+1)步走:

  1. 创建一个空对象(1)
  2. 获取到构造函数(2)
  3. 把空对象作为构造函数的上下文(3)
  4. 把该对象关联到构造函数(4)
  5. 如果原始对象或者构造函数有返回值且为对象,则返回该对象,否则返回新创建的对象(5)
    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
    //一个简单的构造函数
    function Rocker(name){
    this.name = name;
    };
    Rocker.prototype.getName = function(){
    return this.name;
    }

    var createObject = function(){
    var obj = new Object(),
    //(1)创建一个空对象
    Constructor = [].shift.call(arguments);
    //(2)shift方法会返回数组中的第一个元素,
    //该语句的意思是,将传入的第一个参数(即构造函数对象,也就是下方的Rocker)赋值给Constrctor对象
    //(因为prototype的constructor属性会指向该原型对象的构造函数,所以这里给变量起名为Constructor,不懂的去看阮一峰博客)
    obj.__proto__ = Constructor.prototype;
    //(3)把obj的原型链指向Constructor的原型
    var ret = Constructor.apply(obj, arguments);
    //(4)把参数传入Constructor(即Rocker)函数中,并把this修改为obj(修改上下文)
    return typeof ret === 'object' ? ret : obj;
    //(5)如果传参正确,就返回ret(正确的实例对象),否则返回空对象
    };

    var Freak = createObject(Rocker, 'Freak');
    console.log(Freak.name); //Freak
    console.log(Freak.getName()); //Freak
    console.log(Object.getPrototypeOf(Freak) === Rocker.prototype); //true


空对象和空数组不相等

计算返回值(已用console.log表示)

1
2
3
4
5
6
<script type="text/javascript">
console.log({}=={});//false
console.log([]==[]);//false
console.log([]==0);//true
console.log("0"==false);//true
</script>

空对象和空数组不相等的原因:kokodayo
思否:JavaScript 深入了解基本类型和引用类型的值

因为对象是引用类型,比较两个对象时,是引用的比较,两个对象的地址不一样,所以返回false。


类数组转化为数组

如何将一个类数组对象转化为数组对象?如果想不转化便使用Array上定义的方法该如何做?

学习一下“伪数组”:
思否:伪数组(ArrayLike)
JavaScript中的数组与伪数组的区别
CSDN:如何把一个伪数组转化成真正的数组?

方法一:ES6的拓展运算符

1
2
3
4
5
  const divs = document.querySelectorAll('div');
const divArr = [...divs];
console.log(divArr);

//即:var arr = [...arrLike];

方法二:遍历添加入一个空数组

1
2
3
4
var arr = [];
for(var i = 0; i < arrLike.length; i++){
arr.push(arrLike[i]);
}

方法三:ES6的Array.from方法

1
var arr = Array.from(arrLike);

方法四:slice()方法

1
2
3
var arr = [].slice.call(arrlike);
//对于call和apply,slice之前是[]或Array.prototype都可以
var arr = Array.prototype.slice.apply(arrlike);

以上几种方法,不会保留索引值以外的其他额外属性


方法五:修改原型指向

1
arrLike.__proto__ = Array.prototype;

这样arrLike就继承了Array.prototype中的方法,可以使用push(),unshift()等方法了,length值也会随之动态改变。
另外这种直接修改原型链的方法,还会保留下伪数组中的所有属性,包括不是索引值的属性。

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
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
div{
width: 100px;
height: 100px;
background-color: #bfa;
border-bottom: 3px solid black;
}
</style>
<script>
window.onload = function(){
var arrlike = document.getElementsByClassName("box");
//var arrlike = document.getElementsByTagName("div");
//不要这么写,因为页面中有隐藏div,占用伪数组的第一个位置
console.log(arrlike);
console.log(arrlike instanceof Array);
var arr = Array.from(arrlike);
console.log(arr);
console.log(arr instanceof Array);
}
</script>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</body>
</html>

在测试这个例子的时候,发现以后应尽量避免使用getElementsByTagName(“div”)

不转化如何使用数组方法

用call或apply:

使用slice:

1
2
3
4
5
6
7
8
9
10
11
12
window.onload = function(){
var arrlike = document.getElementsByClassName("box");
//比如,此时想对伪数组使用slice方法
var arr = [].slice.call(arrlike,2,5);
var arr = Array.prototype.slice.call(arrlike,2,5);
var arr = [].slice.apply(arrlike,[2,5]);
var arr = Array.prototype.slice.apply(arrlike,[2,5]);

console.log(arr);
console.log(arr instanceof Array);
}

使用其他方法:在类数组中使用数组方法

我试图照猫画虎使用push(),但是一直在报错…

1
2
3
var myul = document.getElementById("myul");

Array.prototype.push.call(arrlike,myul);

这个地方先放着,我在网上查到的方式:

1
2
3
4
5
(function() {
Array.prototype.push.call(arguments, 4)
console.log(arguments)
// [1,2,3,4]/{ '0': 1, '1': 2, '2': 3, '3': 4 }
})(1,2,3)

但我照着写,发现还是失败了…


3.10 我回来了,这样写就ok了,之前是没理解call

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
window.onload = function () {
var arrlike = document.getElementsByClassName("box")
var arr = [].slice.call(arrlike).concat("Danmo")
// arr.push("Danmo") 输出结果正常
console.log(arr)
}
</script>
</head>

<body>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
<div class="box">
</div>
</body>

</html>

也可以先将伪数组转化为数组,再使用push,但这样不符合“伪数组使用数组方法”的题意


请写出以下代码的日志结果

阮一峰——学习Javascript闭包(Closure)
廖雪峰——闭包
考点:闭包

1
2
3
4
5
6
7
8
9
var fn = (function() {
var i = 1;
return function() {
console.log(i++);
}
})();
fn();
fn();
fn();

输出结果是:1 2 3

fn的返回值是一个匿名函数(它是函数中的函数,即闭包),它调用了变量i,导致fn第一次调用后产生的i还留在内存中

Final Stage

let和暂时性死区

请描述let,const和var之间的区别,并解释暂时性死区的意义

可参考:
let,const和var之间的区别
阮一峰的ES6教程

var:变量可重复声明,无块级作用域,存在变量提升
let:变量不能重复声明,存在块级作用域,不存在变量提升
const:声明必须赋初值,不能重复声明,值不能修改,块级作用域,不存在变量提升

暂时性死区:如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
在代码块内,使用let命令声明变量之前,该变量都是不可用的。 这在语法上,称为“暂时性死区”。

1
2
3
4
5
6
var tmp = 123;

if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}

请写出使用XMLHttpRequest发送一个GET请求的代码

使用XMLHttpRequest发送一个GET请求

参考:MDN
由于还没学ajax,暂时不能看懂

1
2
3
4
5
6
7
8
9
10
var xhr = new XMLHttpRequest();
xhr.open('GET', '/server', true);

xhr.onload = function () {
// 请求结束后,在此处写处理代码
};

xhr.send(null);
//XMLHttpRequest.send() 方法接受一个可选的参数,其作为请求主体;
//如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null

5.12回来:现在能看懂了

AJAX的作用:不刷新页面,即可更新页面的一部分
以下GET请求的作用:点击按钮后,在框中显示“HELLO AJAX”

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const btn = document.getElementsByTagName("button")[0];
const result = document.getElementById("result");

btn.onclick = function() {
const xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:8000/server');
xhr.send();
xhr.onreadystatechange = function() {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
result.innerHTML = xhr.response;
}
}
}
}

服务端(express):

1
2
3
4
5
6
7
8
9
10
11
12
const express = require('express');

const app = express();

app.get('/server', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.send('HELLO AJAX'); //对应上方的xhr.response
})

app.listen(8000, () => {
console.log('服务已经启动,8000端口监听中');
})

请求从客户端发出后的流转过程

请简单描述一个请求从客户端发送出去的流转过程

参考:一个HTTP请求的曲折经历

  • 输入URL后,会先进行域名解析
  • 浏览器拿到服务器的IP地址后,会向它发送HTTP请求。HTTP请求经由一层层的处理、封装、发出之后,最终经由网络到达服务器,通过三次握手建立TCP连接,服务器接收到请求并开始处理
  • 服务器构建响应,再经由一层层的处理、封装、发出后,到达客户端,浏览器处理请求
  • 浏览器开始渲染页面,解析HTML,构建render树,根据render树的节点和CSS的对应关系,进行布局,绘制页面

(现在理解并不深入,学过计网后再看一遍吧)


BY2018秋

Level 0

如何对⼀个整数乱序数组进⾏排序?请写出代码或简单思路

选择排序和插入排序,之前的博客里有…这里先不写了,其他的排序方法还不是太熟悉…(简单的桶排序其实也可)


有如下的⼆维数据,请⽤最适当的数据结构进⾏描述并能够较好扩展,并⽤代码实现以下⽬的

emm,涉及数据结构的题目啊,先放着


Level 1

  1. < img> 中⼀般都会有的属性是 () A. src B. alt C. title D. href

A


  1. ⿏标点击时超链接样式为 () A. a:link B. a:visited C. a:hover D. a: active

D


  1. 下⾯哪⼀个不是JavaScript基本类型 () A. symbol B. object C.array D. null

C


数组方法中的纯函数

  1. (不定项)纯函数是⼀类没有副作⽤的函数,其输出结果只依赖其输⼊,以下关于 Array 的⽅法中你认为是纯函数的有哪些 () A. sort B. filter C. map D. reverse E. push F. pop G. every H. splice I. slice

CGI

这道题介绍了纯函数的概念,可参考博客:
JavaScript 数组纯函数
JS 数组详细操作方法及解析合集

菜鸟教程——Array every方法
(some和every类似,只不过some只要有一个符合条件就会返回true,而every要求全部符合条件)

菜鸟教程——Array map方法
(在原数组的基础上构建一个新数组)

可以简单认为,对原数组没有影响的方法就是纯函数


区分行内元素和块级元素

  1. 请尽可能多的写出你知道的html元素标签,并区分⾏内元素与块级元素

这篇博客概括得极为全面:行内元素和块级元素都有哪些

列举一些常见的块级元素和行内元素:
块级:div、nav、h1~h6、p、table、ul、ol、li、form
行内:a、span、i、em、img(易错)、input(易错)、br(易忽略)


列举css选择器

  1. 请尽可能多的写出你知道的css 选择器

可参考:阮一峰——CSS选择器笔记
元素选择器、id选择器、类选择器、通配选择器、伪类选择器、复合选择器、交集选择器、并集选择器、子元素选择器、后代元素选择器、兄弟选择器、属性选择器(用得很少)


引用css的几种方法

  1. 请尽可能多的写出引⽤css的⼏种⽅法,并区分其优先级

内联样式:在标签内style=” : “,比如:< body style=”background-color: gray”>
内部样式表:不必多言
外部样式表:不必多言,link引入

优先级:!improtant > 内联样式 > 内部样式表 > 外部样式表


  1. 请你写出合适的html、css,使⽤在(6)中写出的css 选择器来选择HTML元素,随意发挥

这题不想做Orz……


Level 2

变量作用域/内外变量重名

  1. 请写出以下代码的运⾏结果:
1
2
3
4
5
6
7
var a = 1; 
function b(){
console.log(a);
var a = a + 1;
console.log(a);
}
b();

参考:廖雪峰——变量作用域与解构赋值

这篇博客里有句话值得关注:JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

对于这道题,由于外部函数和内部函数都定义了a,而且内部函数是用var定义的,存在变量提升,也就是说,内部函数在初始化的时候a就赋值为undefined了…

所以输出结果是:undefined NaN(这题考的确实巧妙)


对call和bind的深入理解

  1. 请说明以下输出的结果,并说明原因:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
'use strict';
var maple = {
baz: 3
};
var foo = {
baz: 4,
bar: function(){
console.log(this);
for(var i = 0; i < this.baz; i++){
setTimeout(function(){
console.log(this,i);
});
}
}
};
foo.bar.bind(foo).call(maple);

先了解一下什么是“严格模式”吧:阮一峰——Javascript 严格模式详解

(严格模式下,有很重要的一条:禁止this关键字指向全局对象,菜鸟教程中说,在函数中,在严格模式下,this 是未定义的(undefined))

我们回到这道题,输出结果为:

foo对象
window对象
window对象
window对象
window对象

首先foo.bar.bind(foo)会把this值指定为foo,这个整体会成为新的函数
然后.call(maple)会调用这个新的函数,并再次把this值修改为maple(但实际上this的值还是为foo)

参考这篇博客:深入理解call、apply、bind(改变函数中的this指向)

注意:一旦函数通过bind绑定了有效的this对象,那么在函数执行过程中this会指向该对象,即使使用call、apply也不能改变this的指向

关于计时器呢,可以看看这篇博客:Javascript定时器中的this指向

如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的。


ES5实现find

  1. 请⽤ es5 的语法实现JavaScript数组的find⽅法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Array.prototype.myfind = function(fun){
for(var i=0;i<this.length;i++){
//误打误撞把this给用对了
if(fun(this[i]))
return this[i];
}
}

var ages = [3, 10, 18, 20];
function checkAdult(age) {
return age >= 18;
}
var foo = ages.myfind(checkAdult);
console.log(foo);//18
//测试后,和find的效果别无二致

undefined 和 not defined

  1. 简述undefined和not defined的区别。

可参考:javaScript中not defined,undefined和null的区别

总结一下,区别就是:undefined表示定义但未赋值,而not defined表示没有定义。用var声明变量时存在变量提升,此时也只是先声明变量但不给变量赋值。


http method,状态码

  1. 你知道哪些 http method?返回状态码⾸位数字含义(1、2、3、4、5)。
  • GET:用于请求服务器发送某个资源
  • HEAD:与 GET 方法的行为很类似,但服务器在响应中只返回首部,不会返回实体的主体部分,允许客户端在未获取实际资源的情况下,对资源的首部进行检查
  • POST:用于向服务器发送数据
  • PUT:用于向服务器上的资源(例如文件)中存储数据,比如写入文档
  • DELETE:请服务器删除请求 URL 所指定的资源
  • TRACE:主要用于诊断,用来查看代理和其他应用程序对用户请求所产生的效果

返回状态码首位数字含义:
1** 信息。服务器收到请求,请继续执行请求
2** 成功。请求被成功接收并处理
3** 重定向。需要进一步操作来完成请求
4** 客户端错误。无法完成请求,或请求包含语法错误
5** 服务器错误。服务器在处理请求的过程中发成错误


发送POST请求

  1. 请⽤你喜欢的⽅式向 https://google.com 发送⼀个 POST 请求,在请求体上附带上 hello google 信息。

客户端:(还没解决跨域问题)

1
2
3
4
5
6
7
8
9
10
11
12
13
const btn = document.getElementsByTagName("button")[0];
btn.onclick = function() {
const xhr = new XMLHttpRequest();
xhr.open('POST','https://google.com');
xhr.send('hello google');
xhr.onreadystatechange = function() {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log('OK');
}
}
}
}

Level 3

浏览器缓存方法和区别

  1. 简述浏览器缓存的多种⽅法及其区别(从时效、特性等出发)

参考博客:简述浏览器端的九大缓存

  1. http缓存是基于HTTP协议的浏览器文件级缓存机制,针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从本地读取文件
  2. websql这种方式只有较新的chrome浏览器支持,并以一个独立规范形式出现。它是将数据以数据库的形式存储在客户端,根据需求去读取。跟Storage的区别是: Storage和Cookie都是以键值对的形式存在的;Web Sql 更方便于检索,允许sql语句查询,让浏览器实现小型数据库存储功能
  3. indexDB 是一个为了能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API,一般用户保存大量用户数据并要求数据之间有搜索需要的场景
  4. Cookie一般网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)
  5. Localstoragehtml5的一种新的本地缓存方案,目前用的比较多,一般用来存储ajax返回的数据,加快下次页面打开时的渲染速度。不适合存放过多的数据
  6. Sessionstorage和localstorage类似,但是浏览器关闭则会全部删除,api和localstorage相同,实际项目中使用较少。
  7. application cache 是将大部分图片资源、js、css等静态资源放在manifest文件配置中,便于离线浏览网页
  8. cacheStorage是在ServiceWorker的规范中定义的,可以保存每个serverWorker申明的cache对象
  9. flash缓存 这种方式基本不用,这一方法主要基于flash有读写浏览器端本地目录的功能

再看一篇:浏览器缓存看这一篇就够了


如何看待JS继承

  1. 你如何看待JavaScript ‘继承’?简要谈谈你的理解。
  • 继承可以让实例对象拥有其原型对象的属性和方法
  • 实现继承的方式:原型链继承/使用构造函数继承
  • 继承的应用:
           原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象
           创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了

  1. 请写出以下内容的输出(环境限定为当前最新 lts 版 node, v8.12.0)

我希望再学一次event loop和promise,现在对异步JS编程的理解还是不够

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
29
30
31
32
33
34
35
36
console.log('script start');  // 同步代码 (1)

var foo = {
value: 'foo',
then: function () {
throw new Error('error in foo');
},
bar: function () {
return 'this is bar';
}
};

setTimeout(function () {
console.log('time out'); // timers阶段,执行setTimeout(6)
}, 0);

new Promise(function (res, rej) {
console.log('in promise'); //同步代码 (2)
setTimeout((function () { //由于res的执行被延迟了,所以直接执行rej
res(foo);
return foo.bar;
})(), 0);
rej(new Error('error in promise')); //经测试,即便注掉这句,也不影响'error in foo'的输出
}).then(function (o) { //不执行res,因此then中的代码不会生效
console.log('get promise val');
console.log(o.value);
})
.catch(function (err) { //catch用于指定发生错误时的回调函数,和throw对应
console.log(err.message); //promise的then或catch,此处控制输出'error in foo' (5)
});

process.nextTick(function () {
console.log('next tick'); //遇到后event loop会停下来,立马执行,此处它会在第一个event loop开始之前执行 (4)
});

console.log('script end') //同步代码 (3)

  1. 你了解哪些有趣的前端或者互联⽹历史

网景公司和微软公司的较量,第一二次浏览器大战


BY不知年份

(不定项)选择题

  1. 选出下列四个选项中不是html标签的一项 A. tr B. td C. th D. tb

D


  1. 选出以下不为css属性的一项 A. font-­color B. margin C. text­-size D. z–index

我认为A和C都没有欸


  1. 以下哪项能获取到DOM中的第一个 元素? A. document.getElementById(“test”) B. document.getElementsByClassName(“test”)[0] C. dom.getElementById(“test”) D. dom.getElementByClassName(“test”)[0]

B


JS字符串隐式转换

  1. JavaScript: 2 + “1” + 2 = ? A. “23” B. 5 C. “5” D. “212”

D

‘+’两侧只要有一侧是字符串,另一侧的数字则会自动转换成字符串,因为其中存在隐式转换


  1. JavaScript: 2 / 0 = ? A. 报错 B. 0 C. infinity

C


简答题

  1. css有几种选择器(写不出名字可直接上代码)
  2. 写出所有你能想到的方法,在网页上呈现出一个三角形

这两题做过了~


  1. 写出对元素实现绝对(水平,垂直)居中布局的方式

margin,position:absolute,transform,display:table-cell,vertical-align:center…上边也说过了


构造函数和普通函数的区别

  1. 指出以下两行代码的区别
1
2
var xiahao = new Person() 
var xiahao = Person()

第二种方式,仅仅是把函数Person的返回值赋值给xiahao;
而第一种方式,xiahao成为构造函数Person的一个实例对象


图片格式

  1. 写出常见的一些图片格式

参考:常用的几种图片格式

jpg、png、gif、psd、webp(限于chrome)

图片的格式:

  • jpeg(jpg)

    • 支持的颜色比较丰富,不支持透明效果,不支持动图
    • 一般用来显示照片
    • gif
    • 支持的颜色比较少,支持简单透明,支持动图
    • 颜色单一的图片,动图
    • png
    • 支持的颜色丰富,支持复杂透明,不支持动图
    • 颜色丰富,复杂透明图片(专为网页而生)
    • webp
    • 这种格式是谷歌新推出的专门用来表示网页中的图片的一种格式
    • 它具备其他图片格式的所有优点,而且文件还特别的小
    • 缺点:兼容性不好
    • base64
    • 将图片使用base64编码,这样可以将图片转换为字符,通过字符的形式来引入图片
    • 一般都是一些需要和网页一起加载的图片才会使用base64

三栏等宽布局

  1. 用HTML和CSS实现一个简单的等宽三栏式布局

之前写的双栏等宽布局是可以的,不过我发现width写成百分比的形式好像更符合题意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
.box1{
margin-top: 100px;
width: 33.33%;
height: 500px;
box-shadow: 3px 3px 5px rgba(0,0,0,.3);
float: left;
}
</style>
</head>
<body>
<div class="box1"></div>
<div class="box1"></div>
<div class="box1"></div>
</body>
</html>

在这里插入图片描述


JS字符串逆序

  1. 如何用JavaScript将字符串“abcdefg”变成”gfedcba”

这题考到了sort方法呀 ←No,字符串和数组是不一样的,sort失败了…

参考博客:
JavaScript字符串逆序(2种方法)
join方法

主要有如下两种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
//先将字符串用push存入一个数组,再将数组用join转化为字符串
var reverse = function( str ){
var stack = [];//生成一个栈
for(var len = str.length,i=len;i>=0;i-- ){
stack.push(str[i]);
}
return stack.join('');
};

//将字符串用split存入另一个数组,然后reverse,join一气呵成,干净利落(推荐)
var reverse = function( str ){
return str.split('').reverse().join('');
};

  1. 请写出以下输出日志结果,并解释。
1
2
3
4
5
for(var i = 0; i < 5; ++i){
setTimeout(function() {
console.log(i)
}, 500);
}

参考:JS中For循环中嵌套setTimeout()方法的执行顺序

结果:输出5次5
原因:定时器是一个异步函数,在执行它之前,先跑完for循环,所以定时器会在控制台输出5次5。


  1. 和2017年秋final stage前的那道题是一样的,考点为闭包

附加题

  1. 一个乱序数字数组长度为n,如[3,2,5,7…],如何将该数组按由大到小排序?尽量用多种方式。(不熟悉JavaScript可以直接用高中的那种流程框图表达思路)

JS由大到小排序

传统的冒泡排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var array=[3,2,5,7];
console.log(array);//(4)[3,2,5,7]
bubbleSort(array,array.length);//冒泡排序
function bubbleSort(arr,n){
for(var i=1;i<n;i++){
for(var j=0;j<n-i;j++){
if(arr[j]>arr[j+1]){
var t=arr[j];
arr[j]=arr[j+1];
arr[j+1]=t;
}
}
}
}
console.log(array);//(4)[2,3,5,7]

传统的选择排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var array=[3,2,5,7];
console.log(array); //(4)[3,2,5,7]
selectSort(array,array.length);//选择排序
function selectSort(arr,n){
for(var i=0;i<n-1;i++){
for(var j=i,k=j+1;k<n;){
if(arr[j]<=arr[k]){
var min=j;
k++;
}
else{
var min=k;
j=k;
k++;
}
}
var t=arr[i];
arr[i]=arr[min];
arr[min]=t;
}
}
console.log(array);//(4)[2,3,5,7]

使用JS特色的sort方法:

1
2
3
4
5
6
var array=[3,2,5,7];
console.log(array);
array.sort(function(a,b){
return a-b;
})
console.log(array);

剩下几个聊天题就不做了

BY2020春

Level 0

  1. 列举⾏内元素、块级元素各三个。

行内:a、span、img
块级:h1、p、div


content-box

  1. content-box 盒⼦模型结构如何?其与 border-box 有何区别?
  • content-box:默认值,width和height设置内容区的大小,加上padding (内边距)、border(边框)后盒子可见框变大。
  • border-box:width和height设置可见框的大小,可见框大小固定,如果添加了padding和border会导致内容区变小。

辨析单位

  1. px,em,rem,vw 分别作何解释?

参考博客:px、em、rem、%、vw、vh、vm这些单位的区别

  • px:pixel,像素,一张图片最小的一个点
  • em:1em=父元素的字体大小,如果自身的字体大小被定义,则1em=自身字体大小,默认下1em=16px
  • rem:1rem=根元素字体大小
  • vw:视窗宽度,1vw=视窗宽度1%,主要用于适配移动端

display:none 和 visibility:hidden

  1. display:none 与 visibility:hidden 有何区别

参考博客:visibility :hidden和display:none的区别

这篇博客说得很全面了,不过按我的经验,仅仅是:

  • display:none 既不显示,还不占页面空间
  • visibility:hidden 不显示,但占页面空间

对齐方式

  1. inline-block 的 vertical-align 属性默认是以下哪种对⻬?
    middle 中线 baseline 基线 bottom 底线 top 顶线

当然是 baseline!


  1. rgba() 和 opacity 的透明效果在应⽤上有何不同?

参考博客:css透明度之rgba和opacity的区别及兼容

主要是作用对象和继承性不同:

  • opacity作用于元素,以及元素内的所有的元素的透明度
  • 而rgba()只作用于元素的颜色或其背景色,设置rgba透明的元素的子元素不会继承透明效果

!Doctype

  1. 简述 <!Doctype> 声明的作⽤。

参考博客:HTML文件里开头的!Doctype有什么作用?

< !Doctype>是文档类型声明,即告诉浏览器当前 HTML 是用什么版本编写的,使得浏览器以W3C的标准解析渲染页面,从而使该页面在所有浏览器中以相同的方式显示。


CSS继承

  1. 分别列举出三种有继承性和没有继承性的css属性。

参考博客:CSS有哪些属性可以继承?

有继承性:opacity,font-size,color(字体系列和文本系列的很多属性可以被继承)
无继承性:width,background-color,position(盒子模型、定位、背景属性都不能被继承)


前端实现动画

  1. 想要在前端实现动画,如何应对?写出尽可能多的途径。

参考博客:前端实现动画的6种方式详解

在下不才,只遇到过三种,总结如下:

途径一:CSS3 transition过渡动画,不能实现独立的动画,只能在某个标签元素样式或状态改变时进行平滑的动画效果过渡,而不是马上改变。

途径二:CSS3 animation动画,通过对关键帧和循环次数的控制,页面标签元素会根据设定好的样式改变进行平滑过渡。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!--一个简单的CSS3动画-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
#box1{
/*width: 100px;*/
/*height: 100px;*/
/*background-color: #bfa;*/
position: absolute;
animation: Ani1 2500ms linear alternate infinite;
/*注意中间用空格隔开,而不是逗号*/
}

#box1:hover{
animation: Ani1 2500ms linear alternate infinite paused;
/*当鼠标放在box上时,停止动画*/
}

@keyframes Ani1{
0%{
width: 100px;
height: 100px;
left: 100px;
top: 50px;
background-color: #bfa;
}
50%{
width: 300px;
height: 300px;
left: 700px;
top: 350px;
background-color: orange;
}
100%{
width: 100px;
height: 100px;
left: 1300px;
top: 50px;
background-color: blue;
}
}
</style>
</head>
<body>
<div id="box1">
</div>
</body>
</html>

途径三:JS定时器

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
* 尝试创建一个可以执行简单动画的函数
* 参数:
* obj:要执行动画的对象
* attr:要执行动画的样式,比如:left top width height
* target:执行动画的目标位置
* speed:移动的速度(正数向右移动,负数向左移动)
* callback:回调函数,这个函数将会在动画执行完毕以后执行
*/
function move(obj, attr, target, speed, callback) {
//关闭上一个定时器
clearInterval(obj.timer);
//获取元素目前的位置
var current = parseInt(getStyle(obj, attr));
//判断速度的正负值
//如果从0 向 800移动,则speed为正
//如果从800向0移动,则speed为负
if(current > target) {
//此时速度应为负值
speed = -speed;
}
//开启一个定时器,用来执行动画效果
//向执行动画的对象中添加一个timer属性,用来保存它自己的定时器的标识
obj.timer = setInterval(function() {
//获取box1的原来的left值
var oldValue = parseInt(getStyle(obj, attr));
//在旧值的基础上增加
var newValue = oldValue + speed;
//判断newValue是否大于800
//从800 向 0移动
//向左移动时,需要判断newValue是否小于target
//向右移动时,需要判断newValue是否大于target
if((speed < 0 && newValue < target) || (speed > 0 && newValue > target)) {
newValue = target;
}
//将新值设置给box1
obj.style[attr] = newValue + "px";
//当元素移动到0px时,使其停止执行动画
if(newValue == target) {
//达到目标,关闭定时器
clearInterval(obj.timer);
//动画执行完毕,调用回调函数
callback && callback();
}
}, 30);
}
/*
* 定义一个函数,用来获取指定元素的当前的样式
* 参数:
* obj 要获取样式的元素
* name 要获取的样式名
*/
function getStyle(obj, name) {
if(window.getComputedStyle) {
//正常浏览器的方式,具有getComputedStyle()方法
return getComputedStyle(obj, null)[name];
} else {
//IE8的方式,没有getComputedStyle()方法
return obj.currentStyle[name];
}
}

BFC与高度塌陷详解

  1. BFC是什么?请尽量简要地概括。

b站视频:带你用最简单的方式理解最全面的BFC
前端精选文摘:BFC 神奇背后的原理(没有视频好理解)

BFC的全称为Box Formatting context,即块级格式化上下文。BFC可以看作元素的一种属性,当元素开启BFC时,它可以视作隔离了的独立容器,容器内的元素不会影响到外部的元素。


  1. 何为 float 流式布局?遇到元素塌陷如何解决?

将float属性设置为left或right,来实现在水平方向上的布局。

如果元素塌陷,两种方式解决:

  • 开启父元素的BFC(设置overflow:hidden),由于BFC布局规则:计算BFC的高度时,浮动元素也参与计算,所以父元素的高度不会塌陷。
  • 使用伪类after,开启display:block,并且开启clear:both(我的理解是:display只要是块级元素,拥有上外边距的都可以,所以display:table也行,但是不能是inline-block,原因未知)

clear的作用是清除浮动元素对该元素的影响,比如两个块元素本来应该垂直分布,期中A块开启了浮动(假设是left),则B块上移动,开启clear:left后,B不受影响,还在原先的位置。clear的实现原理,是浏览器为B添加了上外边距(不过检查不到),clear:both的作用是清除较大浮动元素的影响。

所以对after开启clear:both后,浏览器会把浮动元素的大小转换为相同大小的上外边距给after,就能撑起父元素了。

完善版.clearfix:

1
2
3
4
5
6
7
<style>
.clearfix::before,.clearfix::after{
content:'';
display:table;
clear:both;
}
</style>

响应式布局详解

  1. 请列举你所知道的响应式布局⽅案,以及如何实现响应式。

可参考博客:
前端响应式布局原理与方案(详细,推荐)
响应式布局的实现

  1. 媒体查询:先完成非响应式布局,即页面宽度是固定大小的。在完成了非响应式后,可以通过添加媒体查询(Media Query)和响应式代码实现响应式特性。

一段典型的媒体查询代码:

1
2
3
4
5
6
7
<style>
@media only screen and (min-width: 500px) and (max-width:700px){
body{
background-color: #bfa;
}
}
</style>

  1. 百分比布局:通过百分比单位,可以使得浏览器中组件的宽和高随着浏览器的高度的变化而变化,从而实现响应式的效果。

参考博客:前端小知识–为什么你写的height:100%不起作用?

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
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
html,body
/*注意需要同时设置根元素和body元素*/
{
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
#box1{
width: 100%;
height: 100%;
/*background-color: red;*/
display: flex;
justify-content: space-around;
}
#box2{
/*宽度固定,这样视口缩小时,box2间的距离减小*/
width: 100px;
height: 50%;
background-color: #bfa;
}
</style>
</head>
<body>
<body>
<div id="box1">
<div id="box2"></div>
<div id="box2"></div>
<div id="box2"></div>
<div id="box2"></div>
<div id="box2"></div>
</div>
</body>
</body>
</html>

窗口较大时

窗口变小时

  1. vw和rem结合布局:将根元素的font-size用vw设置,其他元素的单位都用rem。rem单位都是相对于根元素的font-size来决定大小的,当页面的视口大小发生变化时,vw的值改变,那么以rem为固定单位的元素的大小也会发生响应的变化。
    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
    29
    30
    31
    32
    33
    34
    35
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
    html{
    /*
    * 一般在适配移动端时,为了得到一个较好的浏览效果,
    * css像素要对应2或3个物理像素,对应的视口大小为750px或1125px
    * 此处以750px为例:1vw=7.5px =>1px=0.1333vw =>100px=13.33vw
    * 则设置根元素font-size:13.33vw
    * (根元素无法设置小于12px的字体,所以无法使用10px=1.333vw)
    * 其他元素的大小都用rem设置,1rem=100px
    * 这个大小,在全屏的浏览器窗口下,测出的宽度为255/1.25=204px
    */

    /*
    * 如果想要在分辨率1920x1080电脑的浏览器中得到正确的100px,
    * 则设置font-size:13.33*100/204=6.534vw
    */
    font-size: 13.33vw;
    }
    #box1{
    width: 1rem;
    height: 1rem;
    background-color: #bfa;
    }
    </style>
    </head>
    <body>
    <div id="box1"></div>
    </body>
    </html>


  1. 图片响应式:窗口拉伸时图片等比放大(最大不超过自身大小),窗口压缩时图片等比缩小
    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
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
    #img-wrapper{
    /*border: 10px solid red;*/
    display: flex;
    justify-content: center;
    }
    img{
    display: inline-block;
    max-width: 100%;
    height: auto;
    /*height不设置auto也可,因为auto是默认值*/
    }
    </style>
    </head>
    <body>
    <div id="img-wrapper">
    <img src="../picture/6.jpg"/>
    </div>
    </body>
    </html>

Grid布局

(补充)如何实现Grid布局?

参考博客:CSS Grid 网格布局教程(阮一峰)(以后看)


瀑布流

(补充)如何实现瀑布流?

参考博客:纯CSS实现瀑布流布局(以后看)


  1. 读代作画(不做)

text-align和vertical-align

  1. 请补全以下代码,使之呈现下图。(代码中圆圈序号为填空部分,其中有⼀个为⾮必填)

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
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
.wrapper{
width: 100%;
background-color: black;
font-size: 0;
border: 20px solid /*①*/ yellow;
padding: 20px;
text-align: center;
vertical-align: /*②*/ middle;
/*上方这句可有可无,②为非必填项*/
}
.big{
height: 200px;
}
.inline{
background-color: white;
font-size: 20px;
margin: 10px;
border: solid 4px blue;
width: 50px;
display: /*③*/ inline-block;
/*④*/vertical-align: middle;

/*text-align应写在父元素中,而vertical-align应写在子元素中*/
}
</style>
</head>
<body>
<div class="wrapper">
<div class="inline big">这是盒子</div>
<div class="inline">这是盒子</div>
</div>
</body>
</html>

Level1

  1. 请写代码实现:如何在不影响 let arr = [1,2,3,4,5] 的条件下,为数组添加⼀个新元素6, 并赋值给 arr1 。

一道简单的考察常见数组方法的题。

1
2
3
4
5
6
7
8
9
10
11
window.onload = function(){
//最简朴的做法:
let arr = [1,2,3,4,5];
let arr1 = [];
for(i=0;i<arr.length;i++){
arr1[i]=arr[i];
//或arr1.push(arr[i]);
}
arr1[arr.length]=6;
console.log(arr1);
}
1
2
3
4
5
6
7
8
9
10
11
window.onload = function(){
let arr = [1,2,3,4,5];
/*以下四行,只要有任意一行即可*/
let arr1 = Array.prototype.slice.apply(arr);
let arr1 = [].slice.call(arr);
let arr1 = arr.slice();
let arr1 = arr.slice(0,arr.length);

arr1.push(6);
console.log(arr1);
}

window.onload的灾难

  1. 请在合适的⾏尾写出输出结果,并注释原因。

抄代码的时候把代码抄进window.onload了,引发了灾难,参考博客:
window.onload 函数引发的血案

如果不加var,即便在window.onload之内声明,变量都为顶层(全局)变量;而var声明的就成了局部变量,所以window.a才为undefined。

第一版代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
window.onload = function(){
function foo(){
console.log(this.a);
}
var a = 1;
//如果去掉var,输出结果会变成2 1 1 undefined 2
const obj = {
a:2,
foo:foo
}
obj.foo();//2
foo();//undefined
console.log(window.a);//undefined
const c = new foo();//undefined
/*
*注意:new的时候把原函数执行了一次
*此时this.a就是c.a,由于c的a未定义,
*沿着原型链找回去,foo的a也未定义,所以输出undefined
*/
const d = Object.create(obj);
//使用create方法,d是obj的实例对象
d.foo();//2
//d的a未定义,但沿原型链可以找到obj.a=2
}

原题代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function foo(){
console.log(this.a);
}
var a = 1;
const obj = {
a:2,
foo:foo
}
obj.foo();
//2,foo作为obj的方法调用时,this指向obj
foo();
//1,this指向window
const c = new foo();//undefined
/*
*注意:new的时候把原函数执行了一次
*此时this.a就是c.a,由于c的a未定义,
*沿着原型链找回去,foo也是一个对象,foo的a未定义,
*所以输出undefined
*/
const d = Object.create(obj);
//使用create方法,d是obj的实例对象
d.foo();//2
//d的a未定义,但沿原型链可以找到obj.a=2

变量与函数同名

  1. 写出输出结果并说明原因。

参考回答:js变量与函数同名

有如下原则:

  • 函数声明比变量声明更提前
  • 变量只是提前声明,还在原来的位置赋值
  • 变量只能声明一次,再次声明无效(如果再次赋值则会覆盖)
  • 标识符a的值取决于最后一次赋的值(如果a为函数,其后再声明并赋值为变量,则a的值为变量的值,如果只声明不赋值,则a还是函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var a = 10;
var a;
function a(){
console.log('aoe');
}
console.log(a);//10

/*
以上代码等价于:
function a(){
console.log('aoe');
}
var a;
var a;(无效)
a = 10;
console.log(a);//10
//如果没有a=10,则输出结果为函数
*/

  1. 下⾯两个 for 语句的结果有何不同?为什么?
1
2
3
4
5
6
7
8
9
10
11
12
for(var i=0;i<3;i++){
setTimeout(()=>{
console.log(i);
},0)
}// 3 3 3
// var声明的i为全局变量,循环结束执行定时器时,i的值已经变为3
for(let i=0;i<3;i++){
setTimeout(()=>{
console.log(i);
},0)
}// 0 1 2
// let声明的i存在块级作用域,每一轮给计时器绑定一个不同的i

Level 2

Node.js不会,做不了


判断是否处于node环境

node.js 是什么?如何判断⾃⼰是否处于 node 环境中?

node.js是运行在服务器端的javascript,它不是一种新的语言,而是js的一种运行环境

由于node中不包括DOM,通过判断全局对象是否为window,如果不为window,则当前脚本运行在node.js环境中

1
this === window ? console.log('browser') : console.log('node');

node.js运行模型


请按照⾃⼰的理解,描述 node.js 的运⾏模型。

参考博客:
Node.js 运行机制:Event Loop

事件循环模型 Event Loop

  • node的执行可以分为两个阶段:初始化代码阶段、事件循环
  • 在初始化代码阶段,执行所有的同步操作代码(包含绑定dom事件监听、设置定时器、发送ajax请求的代码),永远一步步执行。
  • 在执行完所有同步代码以后,进入事件循环。在事件循环里的每一个阶段都查看该阶段的任务队列是否为空,如果不为空则尝试同步执行(以先进先出顺序一个一个执行)所有队列里的任务直到队列为空。如果回调队列为空且没有需要等待完成的异步操作,这个 Node.js 进程就结束了。

module.export 与 exports


在 node.js 中, module.export 与 exports 有何区别?

  • module.export可以实现导出单个成员,而exports导出整个对象,通过exports.xxx来访问每个成员
  • node在模块内加了一句:exports = modules.exports。也就是说,exports和modules.exports指向堆内存中的同一块区域,由引用数据类型的相关知识点可知,通过exports可以修改modules.exports,但最后返回的是modules.exports,所以如果直接对modules.exports赋值,比如modules.exports = “Bingyan”,它就不会指向原先的区域,而指向另一块区域,从而可以导出单个成员。

setImmediate和setTimeOut

写出以下代码在 node 环境中的输出。

参考博客:setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop(强推)

这题和原先一道浏览器下的js题目很像,多考了setImmediate和setTimeout的先后顺序问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'use strict'
var maple = {
baz: 3,
};
var foo = {
baz: 4,
bar: function() {
console.log(this);
for(var i = 0; i < maple.baz; i++) {
setImmediate(function() {
console.log(i);
});
}
for(let i = 0; i < this.baz; i++){
setTimeout(function() {
console.log(i);
})
}
}
};
foo.bar.bind(foo).call(maple);

输出结果是:

1
2
3
4
5
6
7
8
{ baz: 4, bar: [Function: bar] }
3
3
3
0
1
2
3

解析:
node.js的Event Loop是分阶段的:timers、pending callbacks、idle/prepare、poll、check、close callbacks,其中setTimeout在timers阶段,而setImmediate在check阶段。在同步任务执行完成,开始执行异步任务的时候,由于setImmediate在上,且pending callbacks、idle/prepare、poll等队列都是空的,直接到check阶段,由主线程执行setImmediate中的代码,再到下一个事件循环,到timers阶段,由主线程执行setTimeout中的代码。

如果把setImmediate和setTimeout反过来写,则可能先进行前者,也可能先执行后者。因为会直接到timers阶段,只要此时时间到了1ms,就执行setTimeout(没到1ms就到下一个循环的timers阶段),然后到check阶段,执行setImmediate。


Level 3

做不了 我觉得自己还可以抢救一下

阅读理解:请先通读⽂档:https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/,回答以下问题:

setImmediate

  1. 为什么下⾯这段代码中的 setImmediate ⼀定先执⾏?
1
2
3
4
5
6
7
8
9
const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
});
setImmediate(() => {
console.log('immediate');
});
});

node.js的Event Loop是分阶段的:timers、pending callbacks、idle/prepare、poll、check、close callbacks,其中setTimeout在timers阶段,而setImmediate在check阶段。在同步任务执行完成,开始执行异步任务的时候,由于代码都写在fs.readFile的函数中,readFile属于I/O操作,会直接跳到poll阶段,poll结束后到了check阶段,执行setImmediate中的代码,然后到下一次循环的timers阶段,执行setTimeout中的代码


  1. 上⾯⼀段代码中, setTimeout 存在第⼆个参数,这个参数默认为多少?

默认是0,但node.js限制setTimeout时间的最小值为1ms,所以会被强制改为1ms


process.nextTick() 与 setImmediate()

  1. process.nextTick() 与 setImmediate() 有何区别?

SetImmediate()属于check阶段,而process.nextTick是一个特殊的异步API,他不属于任何的Event Loop阶段。

Node在遇到这个API时,Event Loop根本就不会继续进行,会马上停下来执行process.nextTick(),这个执行完后才会继续Event Loop。


  1. 写出下⾯代码的执⾏结果并说明理由。
1
2
3
4
5
6
7
8
9
10
11
12
new Promise((resolve) => {
setImmediate(() => {
console.log('setImmediate')
})
resolve()
}).then(() => {
process.nextTick(() => {
console.log('nextTick')
setImmediate(() => console.log('nextImmediate'))
})
console.log('promise.then')
})
  • 先执行console.log(‘promise.then’),它是同步代码
  • 然后执行console.log(‘nextTick’)
  • 接着console.log(‘setImmediate’)
  • 最后console.log(‘nextImmediate’)

一定要记住,同步代码最先执行,console.log(‘setImmediate’)比console.log(‘nextImmediate’)先进入任务队列


2.4 刷题暂告一段落,感到H5/CSS/JS有了一定提升。
继续学习ES6和Git,练习做网页吧,Node和计网不着急。

3.10 从现在开始,补上一部分以前没做的题

拓展

去掉数组中的重复性数据

参考博客:JavaScript数组去重

  1. 两层循环法

这段代码性能或许不好,因为我拿它对下一段代码中的原始数组去重时,浏览器说内存不足,无法打开网页

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
<!DOCTYPE html>
/*
* 1.创建一个新数组,把原数组中的第一个元素插入到新数组中
* 2.遍历原数组中的每一个元素分别和新数组中的每一个元素进行比较
*/
//原数组
var arr = [8,11,20,5,20,8,0,2,4,0,8];
// 新数组
var t = [];//var t = [8,11];
t[0] = arr[0];
//arr中的每个元素
for(var i=0;i<arr.length;i++){
//t中的每个元素
for(var k = 0;k<t.length;k++){
//当原数组中的值和新数组中的值相同的时候,就没有必要再继续比较了,跳出内循环
if(t[k] == arr[i]){
break;
}
//拿原数组中的某个元素比较到新数组中的最后一个元素还没有重复
if(k == t.length-1){
//将数据插入新数组
t.push(arr[i]);
}
}
}
console.log(t);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function unique(arr){            
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){
//第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]

NaN和{}没有去重,两个null直接消失了

  1. indexOf法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function unique(arr) {
    if (!Array.isArray(arr)) {
    console.log('type error!')
    return
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {
    if (array.indexOf(arr[i]) === -1) {
    //indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
    array.push(arr[i])
    }
    }
    return array;
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}]

    NaN、{}没有去重

  2. 利用sort()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return;
}
arr = arr.sort()
var arrry= [arr[0]];
for (var i = 1; i < arr.length; i++) {
//排列好后,只需要一层循环就可以了
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined]

NaN、{}没有去重

  1. 利用filter() + indexOf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function unique(arr) {
    return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
    });
    }
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

    NaN不见了,{}没有去重

  2. 利用hasOwnProperty()
    解释一下unique函数的原理:
    一开始obj.hasOwnProperty(typeof item + item)必然为false,这样会执行冒号后的语句,即使得(obj[typeof item + item] = true),当出现重复元素时,判断obj.hasOwnProperty(typeof item + item)为true,执行冒号前的语句,给filter函数返回false,就能筛选了
    之所以是typeof item + item,是为了区分数字和字符串:

    因为object的key是string类型的,无论你传入什么,他都会解析成字符串(symbol和Map等新特性除外),所以这就导致了数字1和字符串1,他在object中的key是相同的,而你应该不想把他们当成相同的数据给去除掉。所以这里使用了typeof来简单判断了下类型。

不用觉得typeof item + item很奇怪,只要明白,每个元素对应的 typeof item + item 都是不同的,只要它能起到标识每一个元素的作用,那么就足够了

1
2
3
4
5
6
7
8
9
function unique(arr) {
var obj = {};
return arr.filter(function(item, index, arr){
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] //所有的都去重了

全部实现去重

  1. ES6 Set法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let arr = [1,2,3,4,5,4,3,2,1];
    let result = [...new Set(arr)];
    console.log(result);

    //也可以这样写:
    function unique (arr) {
    return Array.from(new Set(arr))
    //from()方法的作用是生成数组
    }
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

    //最简化的写法:
    [...new Set(arr)]
    缺陷:无法去除{}空对象