面试整理 - JS篇(二)
JSAjax核心API手写ajaxconst xhr = new XMLHttpRequest();// get请求xhr.open("GET", "/data.json", true);// true表示异步// 状态改变时xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if (xhr.status === 200
·
文章目录
- JS
- 面试题整理
- 1. var let const
- 2. typeof 返回哪些类型
- 3. 强制类型转换 / 隐式类型转换
- 4. 深度比较,模拟lodash、isEqual
- 5. split() join()
- 6. pop push unshift shift
- 7. slice splice
- 8. [10,20,30].map(parseInt)返回结果是什么
- 9. ajax请求get post区别?
- 10. call apply
- 11. 事件代理(委托)是什么?
- 12. 闭包?有什么特性?有什么负面影响?
- 13. 如何阻止事件冒泡和默认行为?
- 14. 查找、添加、删除、移动DOM节点的方法?
- 15. 如何减少DOM操作
- 16. 解释jsonp原理,为何它不是真正的ajax
- 17. document load和ready的区别
- 18. `==` 和 `===` 的区别
- 19. 函数声明和函数表达式的区别
- 20. new Object() 和 Object.create()区别
- 21. this的场景题
- 22. 关于作用域和自由变量的场景题 - 1
- 23. 判断字符串以字母开头,后面数字下划线,长度6-30
- 24. 手写trim方法,保证浏览器兼容性
- 25. 如何获取多个数字中的最大值
- 26. 如何用js实现继承
- 27. 如何捕获js中的异常
- 28. 什么是json?
- 29. 获取当前页面url参数
- 30. 将url参数解析为js对象
- 31. 手写数组flatern,考虑多层级(排平)
- 32. 数组去重
- 33. 手写深拷贝
- 34. 介绍RAF requestAnimationFrame
- 35. 前端性能如何优化?从哪几方面考虑?
- 36. 如何理解async
JS
面试题整理
1. var let const
- var - ES5,变量提升,没有块级作用域
- let const - ES6,有块级作用域
2. typeof 返回哪些类型
- undefined string number boolean symbol
- object (注意,typeof null === ‘object’)
- function
3. 强制类型转换 / 隐式类型转换
- parseInt parseFloat toString
- if、逻辑运算、==、+拼接字符串
4. 深度比较,模拟lodash、isEqual
function isObject(obj) {
return typeof obj === "object" && obj !== null;
}
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型(一般参与比较的不是函数)
return obj1 === obj2;
}
if (obj1 === obj2) {
return true;
}
//两个都是对象或数组
// 1. 先取出obj1和obj2的keys,比较个数
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if (obj1Keys.length !== obj2Keys.length) {
return false;
}
// 2. 以obj1为基准,和obj2一次递归比较
for (let key in obj1) {
// for in 适用于对象和数组
// 比较当前key的val 递归
const res = isEqual(obj1[key], obj2[key]);
if (!res) {
return false;
}
}
// 3. 全相等
return true;
}
const obj1 = {
a: 100,
b: {
x: 100,
y: 200,
},
};
const obj2 = {
a: 100,
b: {
x: 200,
y: 200,
},
};
console.log(isEqual(obj1, obj2));
5. split() join()
'1-2-3'.split('-'); // [1,2,3]
[1,2,3].join('-')' // '1-2-3'
6. pop push unshift shift
- 纯函数:1. 不改变原数组(没有副作用)2. 返回一个数组
concat map filter slice
const arr1 = arr.filter(num => num > 25);
非纯函数:
- pop push unshift shift forEach splice
7. slice splice
const arr = [10, 20, 30, 40, 50];
// slice 剪切 纯函数
const arr1 = arr.slice(); // 返回原数组
const arr2 = arr.slice(1, 4); // [20, 30, 40]
const arr3 = arr.slice(2); // [30, 40, 50]
const arr4 = arr.slice(-2); // 截取最后两个 [40, 50]
// splice 剪接 非纯函数
const spliceRes = arr.splice(1, 2, "a", "b", "c");
// arr = [10, "a", "b", "c", 40, 50]
// spliceRes1 = [20, 30]
const spliceRes1 = arr.splice(1, 2);
// arr = [10, 40, 50]
// spliceRes1 = [20, 30]
const spliceRes2 = arr.splice(1, 0, "a", "b", "c");
// arr = [10, "a", "b", "c", 20, 30 40, 50]
// spliceRes2 = []
8. [10,20,30].map(parseInt)返回结果是什么
const res = [10, 20, 30].map(parseInt);
// 拆解
res = [10, 20, 30].map((n, idx) => {
return parseInt(n, idx);
});
// parseInt 函数返回的是十进制结果
// 第二个参数的确是进制,是表明以该进制解析第一个参数。
//如果第二个参数没有或者为0,应该是按浏览器默认设置去解析(一般都会默认十进制,但有些环境可能会有偏差,比如把0开头的数值字符串解析为八进制数)。
//例如 parseInt(10, 0) 以十进制来说自然就是 10 。
//但是如果是 parseInt(30, 2) 30 不是 二进制的数,所以会返回 NaN 。
//一般做进制转换,都会先用 parseInt 转换为十进制,然后用 toString 转换为其它进制。
9. ajax请求get post区别?
- get 一般用于查询,拼接在url上,
- post 一般用于提交,放在请求体内
- 安全性:post易于防止CSRF
10. call apply
fn.call(this, p1, p2, p3);
fn.apply(this, arguments);
11. 事件代理(委托)是什么?
冒泡
12. 闭包?有什么特性?有什么负面影响?
- 回顾作用域和自由变量
- 回顾闭包应用场景:作为参数被传入,作为返回值被返回
- 回顾:自由变量的查找,要在函数定义的地方(而非执行的地方)
- 影响:变量会常驻内存,得不到释放,闭包不要乱用(内存泄露一般是bug导致的)
13. 如何阻止事件冒泡和默认行为?
event.stopPropagination();
event.preventDefault();
14. 查找、添加、删除、移动DOM节点的方法?
document.getElementsById();
const p1 = document.querySelector('p');
p1.className = 'red';
p1.setAttribute('data-name', 'yyy');
p1.getAttribute('data-name');
const newP = document.createElement('p');
// 插入节点(将新节点appendChild)
div2.appendChild(newP);
// 获取节点(将已存在节点appendChild)
div2.appendChild(p1);
// 获取父元素
p1.parentNode;
// 获取子元素
p1.childNodes;
// 删除子元素
p1.removeChild(xxxx)
15. 如何减少DOM操作
- 缓存DOM查询结果
- 多次操作,合并到一起一次操作
const frag = document.createDocumentFragment();
for(){
frag.appendChild(i);
}
16. 解释jsonp原理,为何它不是真正的ajax
- 浏览器的同源策略(服务端没有同源策略,nginx叫转发)和跨域
- 哪些html标签能绕过跨域?
img script
17. document load和ready的区别
window.addEventListener("load", function () {
//页面的全部资源加载完成才会执行,包括图片、视频等
});
document.addEventListener("DOMContentLoaded", function () {
// DOM渲染完即可执行,此时图片、视频可能还没有加载完
});
18. ==
和 ===
的区别
- == 会类型转换
- 应用 == 的场景
19. 函数声明和函数表达式的区别
- 函数声明function fn(){},会变量提升
- 函数表达式const fn = function(){}
20. new Object() 和 Object.create()区别
- {}等于new Object(), 原型Object.prototype
- Object.create(null)没有原型,因为Object.create({…})可以指定原型
const obj1 = {
a: 10,
b: 20,
sum() {
return this.a + this.b;
},
};
const obj2 = new Object({
a: 10,
b: 20,
sum() {
return this.a + this.b;
},
});
const obj3 = new Object(obj1);
console.log(obj1 === obj2); // false
console.log(obj1 === obj3); // true
const obj3 = Object.create(null); // {} 没有原型
const obj4 = new Object() // {}
Object.create创建了一个空对象,然后把传入的原型指向当前对象
21. this的场景题
22. 关于作用域和自由变量的场景题 - 1
23. 判断字符串以字母开头,后面数字下划线,长度6-30
const reg = /^[a-zA-Z]\w{5,29}$/
- ^ 表示字符串的开头
- []表示字符串的选择
- \w命中数字字母下划线
- {}范围
- $结束
// 邮政编码 6位
/\d{6}/
// 小写英文字母 + 一次到多次
/^[a-z]+$/
// 英文字母
/^[a-zA-Z]+$/
// 日期格式 \d{1,2} 一到两位的数字
/^\d{4}-\d{1,2}-\d{1,2}$/
// 用户名
/^[a-zA-Z]\w{5,17}$/
// ip
/\d+\.\d+\.\d+\.\d+/
24. 手写trim方法,保证浏览器兼容性
String.prototype.trim = function (){
return this.replace(/^\s+/,'').replace(/\s+$/,'');
}
25. 如何获取多个数字中的最大值
Math.max 或:
function max() {
const nums = Array.prototype.slice.call(arguments); //改变为数组
let max = 0;
nums.forEach((n) => {
if (n > max) {
max = n;
}
});
}
26. 如何用js实现继承
- class继承
- prototype继承
27. 如何捕获js中的异常
// 一
try {
// todo
} catch (ex) {
console.error(ex); // 手动捕获catch
} finally {
// todo
}
// 二 自动捕获
/**
*
* @param {*} message 报错信息
* @param {*} source 源码
* @param {*} lineNum 行号
* @param {*} colNum 列号
* @param {*} error 错误栈,抛出
*/
window.onerror = function (message, source, lineNum, colNum, error) {
// 第一,对跨域的js,如CDN的,不会有详细的报错信息
// 第二,对于压缩的js,还要配合sourseMap反查到未压缩代码的行、列
};
28. 什么是json?
- 一种数据格式,本质是一段字符串
- 格式和js对象结构一致,对js语言友好
- window.JSON是一个全局对象:JSON.parse JSON.stringify
- key要用双引号引起来
29. 获取当前页面url参数
- 传统方式,查找location.search
- 新API,URL
// 传统方式
// url: 127.0.0.1/pages/info?a=10&b=20&c=30
function query(name) {
const search = location.search.substr(1); // 类似数组的slice,截取问号后的参数
// search: 'a=10&b=20&c=30'
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, "i");
const res = search.match(reg);
if (res == null) {
return null;
}
console.log(res);
return res[2];
}
query("b");
// URLSearchParams
function query(name) {
const search = location.search;
const p = new URLSearchParams(search); // 注意浏览器兼容
return p.get(name);
}
console.log(query("b"));
30. 将url参数解析为js对象
// 传统方式
function queryToObj() {
const res = {};
const search = location.search.substr(1);
search.split("&").forEach((i) => {
const arr = i.split("=");
const key = arr[0];
const val = arr[1];
res[key] = val;
});
return res;
}
// URLSearchParams方式
function queryToObj() {
const res = {};
const p = new URLSearchParams(location.search);
p.forEach((item, index) => {
res[index] = item;
});
return res;
}
31. 手写数组flatern,考虑多层级(排平)
function flat(arr) {
// 验证arr中还有没有深层数据
const isDeep = arr.some((item) => item instanceof Array);
if (!isDeep) {
return arr;
}
const res = Array.prototype.concat.apply([], arr);
// 或
// Array.prototype.concat.call([], ...arr);
// 或
// [].concat(...arr);
return flat(res);
}
const res = flat([1, 2, [3, 4, [67, 89]], 5]);
console.log(res);
32. 数组去重
// 传统方式
function unique(arr) {
const res = [];
arr.forEach((item) => {
if (res.indexOf(item < 0)) {
res.push(item);
}
});
return res;
}
// set (无需结构,不能重复),效率更高
function unique(arr) {
const set = new Set(arr);
return [...set];
}
33. 手写深拷贝
Object.assign不是深拷贝,第一层级的浅层拷贝,有副作用,层级深的话就不是深拷贝了,更改一个另一个也会变
34. 介绍RAF requestAnimationFrame
- 想要动画流畅,更新频率要60帧/s,即16.67ms更新一次视图
- setTimeout要手动控制频率,而RAF浏览器会自动刷新
- 后台标签或隐藏iframe中,RAF会暂停,而setTimeout依然执行
// 3s把宽度100px变为640px,即增加540px
// 60帧/s,3s 180帧,每次变化3px
const $div1 = $("#div1");
let curWidth = 100;
const maxWidth = 640;
//setTimeout
function animate() {
curWidth = curWidth + 3;
$div1.css("width", curWidth);
if (curWidth < maxWidth) {
setTimeout(animate, 16.7);
}
}
//requestAnimationFrame
function animate() {
curWidth = curWidth + 3;
$div1.css("width", curWidth);
if (curWidth < maxWidth) {
window.requestAnimationFrame(animate); // 时间不用自己控制,浏览器会帮运行,如果切换别的标签就会自动停止
}
}
animate();
35. 前端性能如何优化?从哪几方面考虑?
36. 如何理解async
- 是Generator的语法糖
- 是解决异步的问题
- 返回的是一个Promise函数
- 与await搭配使用
- 一般用try catch搭配
- 嵌套低,代码可读性强
更多推荐
已为社区贡献2条内容
所有评论(0)