1.
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
async function async1() {
console.log(3);
await console.log(4);
console.log(5);
}
async1();
Promise.resolve().then(function() {
console.log(6);
});
console.log(7);
答案:1 3 4 7 5 6 2
分析:这里主要是JS Event Loop相关的知识点,比较疑惑的地方是await 后面的语句,那么先复习一下async awiat 相关的知识
async: 申明一个函数是异步的,返回是一个promise对象,但必须要等到内部所有await后面的表达式都执行完,状态才会发生改变
await: 等待意思,必须出现在async修饰的函数中,等待的可以是promise对象也可以是其他的值,但如果是其他值,则会被转成一个立即resolve的
Promise对象,await实际上是一个让出线程的标志,首先await后面的函数先执行一遍,然后就会跳出整个async函数来执行后面JS执行栈的代码,
等本轮事件循环执行完以后再跳回到async函数中等待await。
所以第一轮事件循环执行完以后输出 1 3 4 7,微任务队列执行完以后输出5 6,然后执行宏任务输出2
参考链接:https://blog.csdn.net/Liu_yunzhao/article/details/90734257
2.
写出JS主要数据类型和两种复合型数据类型,并写出下列结果:
string number boolean null undefined symbol
object array function
console.log(null == NaN); // NaN不等于任何值,所以false
console.log(Number(undefined)); // 引用W3school介绍,构造函数 Number() 可以不与运算符 new 一起使用,而直接作为转化函数来使用。以这种方式调用 Number() 时,它会把自己的参数转化成一个数字,然后返回转换后的原始数值(或 NaN),undefined转成一个数字肯定
是失败的,所以返回NaN
console.log(null == undefined); // true
console.log(Number('')); // 0
console.log(null == false); // true和false与任何值比较的时候会先转化成1和0然后再做比较,所以这里是true
console.log(isNaN(23)); // false
console.log(false == ''); // true
console.log(typeof NaN); // NaN 属于数字,所以结果是"number"
console.log(false == 0); // true
console.log(isNaN(NaN)); // true
console.log(2 + 1 + '3'); // 33
console.log(NaN == NaN); // NaN不等于任何值所以false
console.log('3' + 2 + 1); // 321
console.log(undefined == undefined); // true
3.
写出执行结果:
function t(a) {
var a = 'hello';
alert(a);
function a() {
alert(null);
}
alert(a);
}
t(null);
答案:hello,hello
分析:JS编译器会对函数声明进行提升,同时也会对变量声明就行提升,但是函数提升优先于变量,所以var a = 'hello';会覆盖函数声明的提升,
所以两条alert(a);语句都会打印出来hello。
function fn(b) {
alert(b);
function b() {
alert(b);
}
b();
}
fn(10);
答案:"function b() { alert(b); }","function b() { alert(b); }"
分析:JS编译器会对函数声明进行提升,所以两个条alert(b)语句都是打印出来函数b的字符串形式
function a(b) {
alert(b);
b = function() {
alert(b);
}
b();
}
a();
答案:JS编译器不会对函数变量进行提升,所以第一个alert(b)语句访问到的是undefined,因为没有传递b参数,第二条alert(b)语句
打印出来的是函数b的字符串形式"function() { alert(b); }"
4.
封装一个函数正则匹配获取当前页面中的class类名
function getClassNames() {
let regexp = /class="[\w\s-]*"/g;
return document.documentElement.outerHTML.match(regexp);
}
参考:https://www.runoob.com/regexp/regexp-metachar.html
5.
随机打印1-100之间10个数字,去重后且取出该10个数字之间的最大值和最小值
答案:
let arr = [];
for (let i = 0; i < 10; i++) {
arr.push(Math.floor(Math.random() * 100) + 1);
}
arr = Array.from(new Set(arr));
arr = arr.sort((a, b) => a - b);
let max = arr[arr.length - 1];
let min = arr[0];
// let max = Math.max.apply([], arr);
// let min = Math.min.apply([], arr);
6.
JS实现一个对象的深拷贝?
答案:
function deepClone(target, src) {
for (let key in src) {
let val = src[key];
if (Object.prototype.toString.call(val) === '[object Object]') { // 当前属性值是对象
target[key] = {};
deepClone(target[key], val);
}
if (Array.isArray(val)) { // 当前属性值是数组
target[key] = JSON.parse(JSON.stringify(val));
}
// 其他对象
}
}
参考:https://github.com/vuejs/vuex/blob/dev/src/util.js#L22
7.
请思考以下代码运行结果,为什么?
document.body.addEventListener('click', () => {
Promise.resolve(1).then(val => {
console.log(val);
});
console.log(2);
}, false);
document.body.addEventListener('click', () => {
Promise.resolve(3).then(val => {
console.log(val);
});
console.log(4);
}, false);
答案:
在之前浏览器对DOM元素通过addEventListener绑定多次事件处理方式是不一样的,有的浏览器能够响应多次绑定的同一事件,而有的浏览器不可以,这里考察的
是在浏览器支持多次绑定同一个事件的情况。了解过JS引擎异步任务处理方式之后我们知道DOM事件属于宏任务,所以这里相当于添加了两个宏任务,宏任务会从
上到下一个一个执行,执行第一个宏任务之后打印2,接下来取出微任务执行打印出来1,接下来主线程会继续从任务队列取出下一个宏任务执行打印出来4,宏任务
执行完之后取出微任务执行打印出来3
8.
在浏览器地址栏输入一个url回车发生了什么?
答案:
1)首先会做DNS查找,域名解析
2)浏览器与服务器建立TCP连接,客户端与服务器进行3次握手,客户端开始发送HTTP请求报文,服务器返回响应报文(HTML字符串)给客户端
3)浏览器解析HTML字符串生成,生成DOM Tree,解析CSS文件CSSOM Tree,将DOM Tree和CSSOM Tree结合,生成Render Tree(渲染树),
根据Render Tree渲染绘制,把页面显示到屏幕上
参考:https://zhuanlan.zhihu.com/p/43282197
9.
介绍下深度优先遍历和广度优先遍历,如何实现?
答案:
深度优先遍历类似DFS与树的先序遍历类似,假设初始状态所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点然后依次从它的各个未被访问的邻接点出发
深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直
至所有顶点都被访问到为止。
// 递归方式
function deepTraversal(node, nodeList = []) {
if (node) {
nodeList.push(node);
let childrens = node.children;
for (let i = 0; i < childrens.length; i++) {
let children = childrens[i];
deepTraversal(children, nodeList);
}
}
return nodeList;
}
// 非递归方式
function deepTraversal(node) {
let stack = [];
let nodeList = [];
if (node) {
stack.push(node);
while (stack.length) {
let item = stack.pop();
let childrens = item.children;
nodeList.push(item);
// nodeList = [] stack = [parent]
// nodeList = [parent] stack = [child3, child2, child1]
// nodeList = [parent, child1] stack = [child3, child2, child1-3, child1-2, child1-1]
// nodeList = [parent, child1, child1-1] stack = [child3, child2, child1-3, child1-2, child1-1-3, child1-1-2, child1-1-1]
for (let i = childrens.length - 1; i >= 0; i--) {
let children = childrens[i];
stack.push(children);
}
}
}
return nodeList;
}
广度优先遍历BFS
function widthTraversal(node) {
let stack = [];
let nodeList = [];
if (node) {
statck.push(node);
while (stack.length) {
let item = stack.shift();
let childrens = item.children;
nodeList.push(item);
for (let i = 0; i < childrens.length; i++) {
let children = childrens[i];
stack.push(children);
}
}
}
return nodeList;
}
参考:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/9
10.
请分别用深度优先思想和广度优先思想实现一个拷贝函数?
function deepCloneByDFS() {
}
function deepCloneBFS() {
}
11.
ES5/ES6的继承除了写法不同以外还有什么区别?
答案:
ES5继承
function A() {
this.name = 'Herly';
}
A.prototype.sayName = function() {
alert(this.name);
}
function B() {}
B.prototype = new A(); // 子类的prototype指向父类的实例
B.prototype.__proto__ == A.prototype // true
B.__proto__ == Function.prototype // true
ES6继承
class A() {
constructor() {
this.name = 'Herly';
}
sayName() {
alert(this.name);
}
}
class B extends A {
constructor() {
super();
}
}
B.prototype.__proto__ == A.prototype; // true
B.__proto__ == A // true,这个地方有区别
其实ES6本质上也是通过原型继承,class继承只是原型继承的语法糖
12.
实现一个Promise.all方法?
答案:
Promise.all = function(arr) { // 接收数组参数
return new Promise(function (resolve, reject) {
let args = Array.prototype.slice.call(arr, 0); // 这里是为了复制一份参数
let remaining = args.length;
function res(i, promise) {
try {
promise.then(function(val) {
args[i] = val;
remaining--;
if (!remaining) {
resolve(args);
}
});
} catch (e) {
reject(e);
}
}
for (let i = 0; i < args.length; i ++) {
res(i, args[i]);
}
});
}