HTML、CSS
HTML
1、如何理解HTML语义化?
两个方面来理解:
- 让代码更易理解,更具有可读性。
- 让搜索引擎更容易读懂,也就是方便做SEO
2、默认情况下,哪些HTML标签是块级元素,哪些是内联元素?
- 块级元素:display:block/table;有div、h1、h2、table、ul、ol、p等。
- 内联元素:display:inline-block/inline;有span、img、input、button、i、b等。
CSS
布局
1、盒子模型的宽度如何计算?(盒子模型的考察)
offsetWidth = width + padding + border + scrollbar(如果有)
拓展:box-sizing中的两个属性content-box和border-box的区别是什么?
- 浏览器默认设置的是content-box,即width就是内容的宽度,如果设置padding和border,会在元素内容区域外叠加宽度。
- 如果设置为border-box,padding和border会在width内绘制,也就是说width其实包含了内容区、padding以及border。
2、观察以下代码,AAA和BBB的距离是多少?(margin纵向重叠的问题)
<style>
p {
font-size: 16px;
line-height: 1;
margin-bottom: 15px;
margin-top: 10px;
}
</style>
<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>
答案:15px
解析:1、相邻元素的margin-top和margin-bottom会重叠。2、空白元素内容的
也会重叠。
3、margin-top、margin-left、margin-bottom、margin-right分别设置负值会怎么样(margin负值的问题)
- margin-top 设置负值,元素会往上移。
- margin-left 设置负值,元素会往左移。
- margin-rigt 设置负值,右侧元素往左移,元素自身不动。
- margin-bottom 设置负值,下侧元素往上移,元素自身不动。
4、什么是BFC?如何应用?(BFC的理解和应用)
- BFC 即Block format context,块级格式化上下文。它是一块独立渲染区,内部元素的渲染不会影响边界以外的元素。
- 形成BFC的常见条件:①float不是none;②position不是absolute或fixed③overflow不是是visible④display是inline-block或flex。
- 应用:清除浮动。
5、手写圣杯布局和双飞翼布局 (float布局的问题)
圣杯布局和双飞翼布局的目的:
- 三栏布局,中间一栏最先加载和渲染
- 两边内容固定,中间内容随着宽度自适应
- 一般用于PC网页
圣杯布局和双飞翼布局的技术总结:
- 使用float布局
- 两侧使用margin负值,以便和中间内容横向重叠。
- 为了防止中间内容被两侧覆盖,一个用padding,一个用margin
圣杯布局:
<style>
body {
min-width: 550px;
}
#header, #footer {
background: grey;
text-align: center;
}
#container {
padding-left: 200px;
padding-right: 150px;
}
#container .column {
float: left;
}
#center {
background: yellow;
width: 100%;
}
#left {
position: relative;
background: red;
margin-left: -100%;
right: 200px;
width: 200px;
}
#right {
background: blue;
margin-right: -150px;
width: 150px;
}
#footer {
clear: both;
}
</style>
<body>
<div id="header">
Header
</div>
<div id="container">
<div id="center" class="column">Center</div>
<div id="left" class="column">Left</div>
<div id="right" class="column">Right</div>
</div>
<div id="footer">
Footer
</div>
</body>
双飞翼布局:
<style>
body {
min-width: 550px;
}
.col {
float: left;
height: 200px;
text-align: center;
}
#main {
background: yellow;
width: 100%;
}
#main-wrap {
margin: 0 200px 0 200px;
}
#left {
background: red;
margin-left: -100%;
width: 200px;
}
#right {
background: blue;
margin-left: -200px;
width: 200px;
}
</style>
<body>
<div id="main" class="col">
<div id="main-wrap">
this is main.
</div>
</div>
<div id="left" class="col">
this is left
</div>
<div id="right" class="col">
this is right
</div>
</body>
6、手写一个clearfix (clearfix的考察)
.clearFix::after {
content: "";
display: table;
clear: both;
}
定位
7、absolute和relative分别依据什么定位?
- relative是依据自身去定位。
- absolute是依据最近一层的定位元素定位。
8、居中对齐的方式有哪些?
水平居中:
- inline元素: text-align: center。
- block元素: margin:auto。
- absolute元素:left:50%; + margin-left 负的一半的宽度。
- absolute元素:left:50%; + transform: translateX(-50%)。
- absolute元素:left:0;right:0;margin:auto;
垂直居中:
- inline元素:将line-height和height设置的一样。
- absolute元素:top:50%; + margin-top 负的一半的高度。
- absolute元素:top:50%; + transform: translateY(-50%)。
- absolute元素:top:0;bottom:0;margin:auto;
图文样式
9、line-height 如何继承?
如下代码P标签的行高是多少?
<style>
body {
font-size: 20px;
line-height: 200%;
}
p {
font-size: 16px;
}
</style>
<body>
<p>AAAA</p>
</body>
答案:40px。 line-height的继承原则:
- 具体数值,如30px,直接继承该数值。
- 具体比例,如1.5,直接继承该比例。
- 百分比,如200%,继承计算后的值。
响应式
10、什么是rem?em与rem的区别?
- rem是个相对长度单位,相对于根元素,常用于响应式布局。
- em是个相对长度单位,相对于父元素,不常用。
11、单位vw、vh
- 1vw是网页视口宽度的1/100。
- 1vh是网页视口高度的1/100。
- vmax取两者最大值,vmin取两者最小值。
CSS3
12、CSS3新增伪类有哪些?
- first-of-type
- last-of-type
- nth-child(n)
- only-of-type
- only-child
- enabled
- disabled
- checked
- before
- after
13、CSS3新增特性(参考掘金收藏)
JavaScript
JS基础
变量的基本类型和计算
1、typeof 能判断哪些类型
- 识别所有值类型
- 识别所有函数
- 判断是否是引用类型
2、何时使用===, 什么时候使用==?
除了 == null之外,其他时候都用 ===。if(obj.a == null) {} 相当于if(obj.a === null || obj.a === undefined){}
3、手写一个深拷贝。
const obj1 = {
age: 20,
name: 'XXX',
address: {
city: 'shangHai'
},
arr: ['a', 'b', 'c']
};
const obj2 = deepClone(obj1);
function deepClone(obj) {
// obj 是null,或者是对象或者数组
if (typeof obj !== 'object' || typeof obj == null) {
return obj;
}
let result;
if (obj instanceof Array) {
result = [];
} else {
result = {};
}
for (const key in obj) {
// 保证key不是原型的属性
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
}
obj2.city = 'Beijing';
console.log(obj2.city, obj1.city); // 'Beijing', 'ShangHai'
4、类型转换
- 字符串拼接
- ==
100 == '100' //true
0 == '' //true
false == '' //true
0 == false // true
null == undefined //true
- if语句和逻辑运算
// 以下是falsely变量,除此之外都是truly变量。
!!0 === false
!! '' === false
!! null === false
!! undefined === false
!! false === false
原型和原型链
1、如何判断一个变量是不是数组
用instanceof Array来判断
2、class的原型本质,怎么理解?
原型关系:
- 每个class都有显示原型Prototype
- 每个实例都有隐式原型_proto_
- 实例的_proto_指向对应的class的Prototype
基于原型的执行规则:
- 获取属性或者执行方法时,先在自身属性和方法寻找,如果找不到则自动去_proto_中去寻找。
闭包和作用域
1、this的不同应用场景,如何取值?
应用场景:
- 在class方法中调用
- 箭头函数
取值:
- 一、在一般函数方法中使用 this 指代全局对象
- 二、作为对象方法调用,this 指代上级对象,数组同理
- 三、函数作为window内置函数的回调函数调用:this指向window对象(setInterval、setTimeout 等)
- 四、作为构造函数调用,this 指代 new 实例化的对象
- 五、apply、call、bind改变函数的调用对象,此方法的第一个参数为改变后调用这个函数的对象
var x = 0;
function test(){
console.log(this.x)
}
var obj = {}
obj.x = 1;
obj.m = test;
obj.m.apply(); //0
obj.m.apply(obj); //1
- 六、匿名函数的执行环境具有全局性,this对象通常指向window对象
2、手写bind函数
Function.prototype.bind1 = function() {
//将参数拆解为数组
const args = Array.prototype.slice.call(arguments);
//获取this(数组第一项)
const t = args.shift();
//fn1 中的bind(...)中的fn1
const self = this;
//返回一个函数
return function() {
return self.apply(t, args);
}
}
3、实际开发中闭包的应用场景,举例说明。
- 闭包隐藏数据,只提供API
function createCache() {
const data = {};
return {
set: function(key, val) {
data[key] = val;
},
get: funtion(key) {
return data[key]
}
}
}
const c = createCache();
c.set('a', 100);
console.log(c.get('a'));
4、什么是自由变量?
- 一个变量在当前作用域没有被定义,但是被使用了。
- 向上级作用域,一层一层依次寻找,直至找到为止。
- 如果到全局作用域依然没有找到,则报错 XX is not defined.
5、什么是闭包?
作用域应用的特殊情况,有两种表现:
- 函数作为参数被传递。
- 函数作为返回值被返回。
function create(){
const a = 100;
return function(){
console.log(a);
}
}
const fn = create();
const a = 200;
fn(); //100
function print(fn) {
const a = 200;
fn();
}
const a = 100;
function fn() {
console.log(a);
}
print(fn); //100
自由变量的查找, 是在函数定义的地方,向上级作用域查找,不是在执行的地方。
异步和同步
1、异步和同步的区别是是什么?
由于js是单线程,所以同步可能会发生代码的阻塞,但是异步不会。
2、手写用promise加载一张图片
function loadImg(src){
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.onload = () => {
resolve(img);
};
img.onerror = () => {
const err = '图片加载失败';
reject(err);
}
img.src = src;
});
}
const url = 'https://pics4.baidu.com/feed/902397dda144ad34944818f8148d3efc30ad8596.jpeg?token=456bb4130259b0a298dfbbb9e6c44aee&s=CF916A810AB7F0DE4215A59E0300C0A2';
loadImg(url).then(img => {
console.log(img.height);
return img;
}).then(img => {
console.log(img.width);
}).catch(err => {
console.error(err);
})
JS异步进阶
1、请描述event loop(事件循环/事件轮询)的机制,可画图
call stack、web apis、callback queue、event loop.
- 1、同步代码,一行行放入call stack执行
- 2、遇到异步会先记录下,然后等待执行时机
- 3、时机到了之后,移入callback queue
- 4、call stack 为空之后,启动event loop
- 5、轮询查找callback queue, 如有则移动到call stack中执行
- 6、继续轮询查找
2、什么是宏任务和微任务,两者有什么区别?
- 宏任务:在DOM渲染后触发,是由浏览器规定的,有setTimeout、setInterval、Ajax、DOM事件
- 微任务:在DOM渲染前触发,是由ES6语法规定的,有promise、async-await
- 微任务执行时机比宏任务早。
3、Promise有哪三种状态?如何变化?
- pending、resolved、rejected
- pending -> resolved, pending -> rejected
- 变化不可逆
4、场景题-promise then 和 catch的连接
// 第一题
Promise.resolve().then(() => {
console.log(1);
}).catch(() => {
console.log(2);
}).then(() => {
console.log(3)
});// 1 2
// 第二题
Promise.resolve().then(() => {
console.log(1);
throw new Error('erro1');
}).catch(() => {
console.log(2);
}).then(()) => {
console.log(3);
}// 1 2 3
// 第三题
Promise.resolve().then(() => {
console.log(1);
throw new Error('erro1');
}).catch(() => {
console.log(2);
}).catch(()) => {
console.log(3);
}// 1 3
5、场景题-async/await 语法
// 第一题
async function fn() {
return 100;
}
(async function() {
const a = fn();
const b = await fn();
})();// a 是个resolved状态的Promise,b是100
// 第二题
(async function() {
console.log('start');
const a = await 100;
console.log('a', a);
const b = await Promise.resolve(200);
console.log('b', b);
const c = await Promise.reject(300);
console.log('c', c);
console.log('end');
})();// start, a 100, b 200 报错终止后续执行
6、场景题-promise和setTimeout的顺序
console.log(100);
setTimeout(() => {
console.log(200);
});
Promise.reslove().then(() => {
console.log(300);
});
console.log(400);
// 100 400 300 200
7、场景题-外加async/await的顺序问题
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
setTimeout(() => {
console.log('setTimeout');
}, 0);
console.log('script start');
async1();
//初始化promise时,被传入的函数会被立即执行
new Promise(function(resolve){
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
8、async-await和Promise有什么关系?
- 执行async函数返回的是一个Promise对象。
- await相当于Promise的then
- try...catch可捕获异常,代替了Promise的catch
DOM
1、如何优化DOM操作性能?
- 将DOM查询做缓存
// 不缓存查询结果
for (let i = 0; i < document.getElementsByTagName('p').length; i++) {
//每次循环都会计算length,频繁查询dom.
}
// 缓存查询结果
const pList = document.getElementsByTagName('p');
const length = pList.length;
for (let i = 0; i < length; i++) {
// 缓存length,只进行一次DOM查询
}
- 将频繁操作改为一次性操作
const listNode = document.getElementById('list');
// 创建一个文档片段,此时还没有插入到DOM树中!
const frag = document.createDocumentFragment();
for (let i = 0; i < 10; i++) {
const li = document.createElement('li');
li.innerHTML = 'List item' + i;
frag.apendChild(li);
}
// 都完成后一次性插入DOM
listNode.appendChild(frag);
2、attribute和property的区别
- property,修改的是对象属性,不会体现到html结构中
- attribute,修改的是html属性,会体现到HTML结构中
- 两者都有可能引起DOM的重新渲染
BOM
1、如何识别浏览器的类型
// navigator
const ua = navigator.userAgent;
const isChrome = ua.indexOf('Chrome');
console.log(isChrome);
2、如何解析URL
- location的各个api: href、protocol、pathname、search、hash
- history的各个api: forward、back
事件
1、编写一个通用的事件监听函数
// 考虑用到事件代理的情况,传 selector 就代表用到事件代理了
function bindEvent(ele, type, selector, fn) {
if (fn === null) {
fn = selector;
selector = null;
}
ele.addEventListener(type, event => {
const target = event.target;
if (selector) {
if(target.matches(selector)) {
fn.call(selector, event);
}
} else {
fn.call(selector, event);
}
});
}
2、描述事件流
- 有捕获阶段、目标阶段以及冒泡阶段。
- 捕获从上至下,冒泡从下至上。
3、无限下拉的图片列表,如何监听每个图片的点击?
- 运用事件代理
- 获取target
- 用matches去判断
4、什么是事件代理
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
Ajax
1、用XMLHttpRequest手写一个Ajax.
function ajax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(
JSON.parse(xhr.responseText);
);
} else if (xhr.status === 404) {
reject(new Error('404 not found'));
}
}
};
xhr.send(null);
});
}
2、Ajax 核心API-XHR
xhr.readyState
- 0 - (未初始化) 还没有调用send()方法
- 1 - (载入) 已调用send()方法,正在发送请求
- 2 - (载入完成) send()方法执行完成,接收到全部响应内容
- 3 - (交互) 解析响应内容
- 4 - (完成) 解析完成,可以在客户端调用
xhr.statu
- 2XX 表示成功处理请求,例如200
- 3XX 重定向,例如301(永久),302(临时),304(资源未改变)
- 4XX 客户端请求错误,例如404(找不到), 403(没有权限)
- 5XX 服务端错误,例如500, 501, 504
3、什么是跨域
同源策略就是,ajax请求时,浏览器要求当前网页必须与server同源。同源:协议、域名、端口三者一致。
4、实现跨域的常见方式:Jsonp和cors
jsonp:
- script不受跨域的限制
- 服务端可以动态拼接返回数据
cors: 服务器设置http header的方式。
存储
1、描述cookie、sessionStorage、localStorage的区别
- 容量上:sessionStorage和localStorage的存储最大都有5M, cookiede 存储大小最大只有4KB
- 是否发送到服务端:http请求时cookie需要发送到服务端,增加了请求数据量, 而sessionStorage和localStorage不需要
- 修改方式上:Cookie只能通过document.cookie的方式修改,非常简陋。sessionStorage和localStorage有简单易用的API:setItem、getItem。
- 存储时效上:sessionStorage和localStorage的区别在于localStorage可以永久存储,sessionStorage是页面关闭后存储就消失了。
HTTP
1、HTTP的状态码有哪些?
- 1xx,服务端收到请求
- 2xx,请求成功,例如200
- 3xx,重定向,例如301(永久),302(临时)、304(资源未修改)
- 4xx,客户端错误,例如404
- 5xx,服务端错误,例如500(服务器错误)、504(网关超时)、502(网关请求错误)
2、HTTP的methods有哪些?
- Get 获取数据
- Post 新建数据
- patch/put 更新数据
- Delete 删除数据
3、HTTP常见的Header有哪些?
Request Headers:
- Accept 浏览器可接收的数据格式。
- Accept-Encoding 浏览器可接收的压缩算法,如gzip
- Accept-Language 浏览器可接收的语言,如zh-CN
- Connection: keep-alive 一次TCP连接重复使用
- Cookie
- Host
- User-Agent 浏览器信息
- content-type 发送数据的格式,如application/json
Response Headers:
- Content-type 返回数据的格式
- Content-length 返回数据的大小,多少字节
- Content-Encoding 返回数据的压缩算法,如gzip
- Set-Cookie 服务端向客户端设置cookie的时候
缓存相关的Headers:
- Cache-control
- Expires
- Last-Modified
- If-Modified-Since
- Etag
- If-None-Match
4、什么是Restful API?
- 是一种新的API设计方法。
- 传统的API设计:把每个URL当做一个功能。
- Restful API设计:把每个URL当做一个为一个的资源。
5、描述一下HTTP缓存机制。
- 强制缓存: 直接从浏览器中读取缓存。
- 协商缓存:与服务端建立通信,带着资源标识请求以判断资源是否是最新的,未更新的话直接从浏览器缓存中读取资源,如果已更新,返回200和最新资源。
- 缓存标志:etag、Last-modified,etag优于last-modified。
- 以画流程图的形式去描述。
运行环境
1、从输入URL到渲染页面的整个过程。
2、window.onload和DOMContentLoaded的区别
- onload,全部资源加载完成才会执行,包括图片、视频等。
- DOMContentLoaded,DOM加载完成就会执行,不用等到图片、视频全部加载完毕。
3、前端性能优化有哪些方式
让加载更快:
- 减少资源体积:压缩代码
- 减少访问次数: 合并代码,SSR服务端渲染,缓存
- 使用更快的网络:CDN
让渲染更快:
- CSS放在head里,js放在body的最下面
- 尽早开始执行JS,用DOMContentLoaded触发
- 懒加载
- 缓存DOM查询
- 频繁操作DOM,统一处理一次性插入
- 节流、防抖
4、手写一个防抖
function debounce(fn, delay = 500) {
let timer = null;
return function() {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
},delay)
}
}
5、手写一个节流
节流的应用场景:
1、拖拽一个元素时,要随时拿到该元素被拖拽的位置
2、直接用drag事件,则会频繁触发,很容易造成卡顿
3、节流:无论拖拽速度有多快,都会每隔一段时间(比如100ms)触发一次。
function throttle(fn, delay = 100) {
let timer = null;
return function() {
if(timer) {
return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
}, delay);
}
}
6、防抖和节流的区别
防抖:动作发生一定时间后再执行,如果在这段时间内再次触发,则需要重新等待一段时间后再触发。
防抖是虽然事件持续触发,但只有等事件停止触发后n秒才执行函数
节流:动作发生后一定时间后再执行,如果在这段时间内再次触发,则忽略该动作,直到执行完成,如有触发才会再次响应。
节流是持续触发的时候,每隔n秒执行一次函数。
7、常见的web前端攻击方式有哪些?
- XSS跨站请求攻击,预防:替换特殊字符,前后端都替换。如<变为<,>变为>
- XSFR跨站请求伪造, 预防:使用Post接口;增加验证,比如指纹支付、验证码、密码等。
习题
1、何为变量提升?
1、var 和 let const 的区别
- var存在变量提升,let、const 没有
- let、var是变量可修改,const 是常量不可修改。
- let、const是块级作用域,var不是。
2、typeof 返回那些类型
- undefined、string、number、boolean、symbol
- object
- function
3、列举强制类型转换和隐式类型转换
- 强制:parseInt、parseFloat、toString等
- 隐式:if、逻辑运算、== 、+拼接字符串。
4、JavaScript的数据类型。
- 值类型(基本类型):string、number、Boolean、undefined、symbol、null
- 引用类型:Object、function、array
2、手写深度比较isEqual.
1、手写深度比较,模拟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;
}
// 判断两个对象的key的长度是否相等
const objKeys1 = Object.keys(obj1);
const objKeys2 = Object.keys(obj2);
if (objKeys1.length !== objKeys2.length) {
return false;
}
// 递归判断value值
for (let key in obj1) {
const res = isEqual(obj1[key], obj2[key]);
if (!res) {
return false;
}
}
return true;
}
2、split()和join()的区别
- split是把字符串分割成数组。
- join是把数组连接成字符串。
3、数组的pop、push、unshift、shift分别做什么?
- push是尾部插入、unshift是头部插入,返回的是数组的长度
- pop是尾部删除,shift是头部删除,返回的是被删除的元素。
- 哪些纯函数的数组API,即不会改变原数组,返回原数组的? concat、map、filter、slice
3、是否会真的用数组map.
1、数组的slice和splice的区别
- 功能:slice是切片截取,splice是剪接
- 参数和返回值:slice的参数是开始位置和结束位置。splice是开始位置和剪接长度,后面还可以跟要插入数组的参数。
- 是否为纯函数:slice是纯函数,splice不是,splice会改变原数组。
2、[10,20,30].map(parseInt)返回值是什么?
[10,20,30].map(parseInt) 相当于是 [10,20,30].map((number, index) => parseInt(number, index))。parseInt(10, 0)默认返回湿巾纸,parseInt(20, 1)和parseInt(30, 2)都无法转,所以是NAN。最终结果是[10, NAN, NAN]
4、闭包
1、函数call和apply的区别
第二个参数不一样,call的第二个参数是列表,apply的第二个参数是参数数组。
2、事件代理(委托)是什么?
事件代理就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
3、闭包是什么,有什么特性?有什么负面影响?
5、回顾DOM操作和优化 (看上文)
6、是否用过Object.create()?
1、函数声明和函数表达式的区别
函数声明会在代码执行前预加载,而函数表达式不会。
2、new Object()和Object.create()的区别
- {} 等同于 new Object(),原型Object.prototype
- Object.create(null)没有原型
3、关于this的场景题
const User = {
count: 1,
getCount: function() {
return this.count;
}
};
console.log(User.getCount()); // 1
const func = User.getCount;
console.log(func()); // undefined
7、常见的正则表达式。
1、关于作用域和自由变量的场景题一
let i;
for (i=0; i<=3; i++) {
setTimeout(() => {
console.log(i);
},0);
}
// 4 4 4 4
2、关于作用域和自由变量的场景题二
let a = 100;
function test() {
alert(a);
a = 10;
alert(a);
}
test();
alert(a);
// 100 10 10
3、判断字符串以字母开头,后面字母数字下划线,长度6-30
const reg = /^[a-zA-Z]\w{5,29}$/
8、如何获取最大值。
1、手写一个trim方法,保证浏览器兼容性
if (!String.prototype.trim) {
String.prototype.trim = function(){
return this.replace(/^\s+/, '').replace(/^\s+$/, '');
}
}
2、如何获取多个数字中的最大值
方法一:
function max() {
const nums = Array.prototype.slice.call(argument);
let max = nums[0];
for (let n of nums) {
if(n > max) {
max = n;
}
}
return max;
}
方法二:Math.max();
3、如何用JS实现继承?(看上文)
9、解析url参数。
1、如何捕获JS中的异常?
- try...catch
- 用window.onerror自动捕获。这个方法有两个缺陷:①对于跨域的js,如CDN的,不会有报错信息。②对于压缩的js,还要配合sourceMap反查到未压缩代码的行、列
2、什么是json?
json 是一种数据格式,本质上是一个字符串。通过window.JSON这个对象的JSON.stringify和JSON.parse进行转换。
3、获取当前页面url参数。
// 传统方式
function query(name) {
const search = location.search.substr(1);
const params = search.split('&');
params.forEach(item => {
const key = item.split('=')[0];
let result;
if(name === key) {
result = item.split('=')[1];
return;
}
})
return result;
}
// URLSearchParams的方式
function query(name) {
const search = location.search;
const p = new URLSearchParams(search);
return p.get(name);
}
10、数组去重有几种方式?
1、将url参数解析为JS对象
// 传统方式
function queryToObj() {
let result = {};
const search = location.search.substr(1);
const params = search.split('&');
params.forEach(item => {
const arr = item.split('=');
const key = arr[0];
const val = arr[1];
result[key]= val;
})
return result;
}
// URLSearchParams的方式
function queryToObj() {
let result = {};
const pList = new URLSearchParams(location.search);
pList.forEach((val, key) => {
result[key] = val;
})
return result;
}
2、手写数组flatten, 考虑多层级
function flatten(arr) {
const isDeep = arr.some(item => item instanceof Array);
if (!isDeep) {
return arr;
}
const res = Array.prototype.concat.apply([], arr);
return flatten(res);
}
3、数组去重
- 遍历push进空数组
function unique(arr) {
const res = [];
arr.forEach(item => {
if(res.indexOf(item) < 0) {
res.push(item);
}
})
return res;
}
- [ ...new Set(arr)]
11、是否用过requestAnimationFrame
1、手写深拷贝(见上文)
2、介绍一下requestAnimationFrame
- 要想动画流畅,更新频率为60帧/s。
- setTimeout要手动控制频率,而RAF浏览器会自动控制。
- 后台标签或隐藏iframe中,RAF会暂停,但是setTimeout依然执行。