2018 年 8 月 17 日的面试题
- 你了解 http 吗?讲一些 http 吧
- http 和 https 的区别
- http 有哪些方法?
- head
- 与 get 相似,没有响应体
- 请求头,放置参数
- get
- 应用请求指定资源,用于获取数据
- 请求头,放置参数
- post
- 将实体提交到指定资源,状态或服务器的副作用更改,通俗讲,提交数据,或发送,并得到结果
- 请求体,放置参数
- put (放的意思)
- 请求有效载荷替换目标资源的所有当前表示
- 传输文件,自身不带验证机制
- 更新资源,幂等 (多次提交更新结果都和一次执行的一样)。
- 类似 post
- 请求体,放置参数
- patch (补丁)
- 对资源应用部分修改
*
delete- 删除指定资源。
- 请求参数放在哪?*
- 很少出现
*
connect (链接)- 建立一个到由目标资源标识到服务器的隧道
- 请求链接转换到 tcp/ip 通道
*
trace (追踪)- 沿着目标资源的路径执行一个消息环回测试
- options
- 返回服务器支持的 http 方法
- get 和 post 的区别
操作 | get | post |
---|---|---|
后退/刷新 | 无害 | 重复提交 |
编码类型 | application/x-www-form-urlencoded | application/x-www-form-urlencoded/multipart/form-data/二进制 |
历史 | 参数保留在浏览器 history 里 | 参数不会保留 |
数据类型限制 | ASCII 字符 | 没有限制 |
安全性 | 与 post 相比,比较差,敏感信息不要用 get | post 比 get 安全,参数不保留的缘故,在客户端和服务端 |
可见性 | 所有人在 url 可见 | 在 url 中不可见 |
- put 和 post 的区别
操作 | put | post |
---|---|---|
特性 | 幂等 | 非幂等 |
场景 | 更新资源,修改密码(因为提交参数不同,但结果一样,重复结果,多次不同请求的场景) | 注册账号 |
- 讲一下 IE 下碰到的那些内存泄露问题#IE 内存泄漏问题总结
参考@3
泄露描述 | 解决方案 |
---|---|
动态刷新 | |
页面 f5 反复刷新,内存 biubiu | |
退出变量占用内存的页面,内存依然无法回收 | |
退出变量占用内存 iframe,内存依然无法回收 | |
span/td/js 变量 | |
同作用域内,js 对象引用 dom 对象,dom 又引用同作用域内的 js 对象,将发生泄露 | |
同作用域内,dom 对象应用一个在函数内的闭包函数,如事件绑定,且闭包又引用上层对象 |
- 讲一下碰到的兼容性问题
- 讲下闭包
- 原理
- 应用场景
- 如何解决闭包
- 跨域问题
- 布局问题,css 选择器有哪些?
- 布局问题,css 左右布局,如何让右边自适应,左边固定?
- 垂直居中有哪几种方式?分别怎么实现
- vue 响应式原理
- defineprotoperty 的 get 和 set 分别做了什么?
- get 依赖收集
- 处理哪些订阅者去做响应式变化处理,
- set 派发更新
- 数据发送变化中触发 setter 逻辑,把依赖过程中订阅的所有观察者也就是 watcher 都触发 update 过程,优化队列,在 nextTick 后执行素有 watcher 的 run
- get 依赖收集
- 观察者
- 如何用原生 js+css 选择
<div>
<div></div>
<p></p>
<!-- css 选择这个,用js 选择这个-->
<p></p>
</div>
原型链
前端缓存问题 (这是那个脸红的妹纸问题的,其实她想问的是 localstorage、和 sessionstorage 和 cookie 的知识,但其实有多种) 比较,彼此之间的异同。
Storage 存储
- cookie
- sessionStorage
- localstorage
indexedDB 还有这个
cache 缓存
- application Cache 我也说道这个
- cache storage
- setTimeout 设置执行时间,一定会执行吗?
- 属于 EventLoop 部分的问题,假如栈里面没有其他任务的话,就会执行。
- 先执行 stack 的普通 console 之类的
- 碰到 setTimeout 会放到 web Api 中,交给浏览器的 timer 模块
- 执行引起处理其他之后,才会回来执行 timer
- 基本流程如下:
- 同步任务
- console 之类主逻辑线
- 微任务
- process.nextTick(node)
- promise
- mutataionObserver
- 宏任务
- script 全局
- setTimeout
- setInterval
- setImmedidate
- I/O
- UI 渲染
- setTimeout 会引起内存泄露吗?
/*1 反复点击的代码~~*/
function leap() {
var li = document.getElementsByClassName('li');
console.log('li');
for (var i = 0; i < li.length; i++) {
li[i].onclick = function () {
console.log('leap boooooooom');
};
}
}
/*2 避免乱用,以下代码可以保证不重复引用或者死循环*/
var test = function () {
setTimeout(function () {
test();
}, 1000);
};
test();
css 部分
什么是标准盒子模型?
- 即对 doctype 定义时,默认是标准盒子模型,content 不包含 padding + border
- 总宽度:
- 总高度:content
什么是 IE 盒子模型?
- 即不对 doctype 定义时,是
- 总高度:width = content
- 总宽度:content + padding + border
如何将标准模型改为 IE 盒子模型?
- box-sizing:content-box 采。用标准模型解析计算,默认的
- box-sizing:border-box,采用怪异模型
如何实现垂直居中?
如果加一个背景,会影响盒子模型的哪部分?
absolute 和 relative (仔细一看,突然了解了 relative 的用法!!)
- absolute 绝对定位,相对于 static 定位以外的第一个父元素定定位,如果没有,那会是 html?有 left top right bottom 值。会超越 overflow
- fixed 相对于窗口定位类似 absolute
- relative 相对定位,相对于自身的基础位置进行定位,left、top、right、bottom 相对于其正常位置定位,left:20 则向元素 left 位置添加 20。【尽量避免用 relative】
- static,默认值,没有定位忽略 top left right bottom z-index
- inherit 继承
js 部分
- js 基础类型与引用类型
typeof 操作符区分基本类型
instanceof 操作符区分引用类型
- 基本类型 什么叫堆内存?自己买菜做饭,就是堆,主动的
- 有 undefined、boolean、number、string、null。按值访问的意思。
- 任何方法都无法改变基本类型的值,比如一个字符串
var name = 'Veaba';
name.toUpperCase();
console.log(name); /*Veaba ,说明无法改变原始变量里面的值 */
不能给基本类型添加属性和方法
基本类型的比较是值的比较,只有他们的值相同的比较,最好使用三等号符
基本类型的变量是存放在栈区,内存里面的栈内存 (那堆呢?)
赋值不影响
引用类型
去菜馆吃饭,叫栈,被动
对象。属性和方法的集合
引用类型可以拥有属性和方法,属性又可以包含基本类型和引用类型
引用类型的值是可以变的。
引用类型的值是同时保存在栈内存和堆内存的对象
- 操作对象的引用
- 栈区内存保存变量和标识符和指向堆内存中该对象的指针 (该对象在对内存的地址)
引用类型的比较是引用的比较。
赋值会影响指向同一个对象
typeof null?object
typeof array?object
如何判断 array 的方法?
- Array.prototype.isPrototypeOf(obj) 原型
- Array.prototype.isPrototypeOf([])
- Array.prototype.isPrototypeOf({})
- obj instanceof Array 构造函数
- 'ff' instanceof Array
- [] instanceof Array
- class 属性,跨原型链调用 toString()
- Object.prototype.toString.call([]) '[object Array]'
- Object.prototype.toString.call({}) '[object Object]'
- Object.prototype.toString.call(null) '[object Null]'
- Array.isArray()//es6 提供的
- Array.prototype.isPrototypeOf(obj) 原型
js 的异步机制
js 是单线程的
任务队列和事件循环 (queue、eventLoop)
定时器 t
异步机制
总结
JavaScript 单线程和其异步机制就如上所述。所谓的单线程并不孤单,它的背后有浏览器的其他线程为其服务,其异步也得靠其他线程来监听事件的响应,并将回调函数推入到任务队列等待执行。 单线程所做的就是执行栈中的同步任务,执行完毕后,再从任务队列中取出一个事件 (没有事件的话,就等待事件),然后开始执行栈中相关的同步任务,不断的这样循环。
浏览器的 100
- 101 webSocket (size 0B)
浏览器的 200 状态的区别
- 灰色的 200 from disk cache (来自磁盘缓存),比如 f5 百度之后的,某个 js,或者 (from memory cache) (使用该功能,必须在 chrome 里面 Network 取消勾选 Disable cache)
- 正常 200
了解 chrome 控制台工具
- Elements 元素
- Console 控制台
- Sources 源
- Network 网络
- Performance 性能
- Memory 内存
- Application 应用程序
- Security 安全
- Audits 审计/审查
浏览器的 300 状态
- 304 (意义:原来缓存的文档还可以使用) png document。依然会与服务器通信。如果 cache-control:max-age>0 直接从浏览器提取。否则,向服务器发送 http 请求,确认该资源是否修改,有 200,无修改 304
svg 与 canvas
- svg 数量小,静态,不失真,dom 绘制,类似图片,logo,简单
- canvas 数量大,密集,js 绘制。像素级。可塑性高,可添加各类事件。处理处理重绘。效率高,复杂度高。
base64 图片与 src 引用图片
base64 好处。小文件嵌入,不需要额外的请求。
base64 坏处。浏览器不会缓存。
根据 base64AndSrcImage.html 测试结果来看
width 1200px 下
base 编译的代码灰色 200,从内存取缓存 from memory cache
src,正常 200,从网络取 /304 产生 7ms
width 750px 下
base 编译的代码灰色 200,从内存取缓存 from memory cache
src,正常 200。从网络取 /304 产生 7ms
得出的结论是,base 占用内存,速度快。
- css 响应式,src 按需加载。base64
css 有间隙
- 当使用 inline-block 父级设置 font-size:0,子级正常 font-size
- 或者 letter-spacing:0
- 或者:块级至今是紧凑的,不换行
css lvhfa 记忆方法,倒叙记忆
css 纯 css 无法实现父选择器
- 目前的实验上去测试了,发现以往的记忆是 JQuery 来实现的,纯 css 无法实现该效果
二叉树
特殊的二叉树,满二叉树。深入 k 且含有 2^k - 1 的节点,深度 3,节点为 2^3-1=7。满二叉树,一定是完全二叉树。 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑫ ⑫ ⑫
完全二叉树。最后一层左边是满的,右边可能满或不满,其余层是满的。 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫
二叉树第 i 层最多有 2^(i-1) 个节点,i>=1
二叉树深度为 k 最多有 2^k-1 个节点 kk>=1
遍历。前序遍历 (DLR),D 根 L 左 R 右。
遍历。中序遍历 (LDR),L 左 D 根 R 右
遍历。后序遍历 (LRD),L 左 R 右 D 根
Number 方法 (2018 年 4 月 10 日面试遇到该类型的题目,简直日了狗的难受)
原始值 NaN (not a number)
原始值 undefined
原始值 null
原始值 boolean
原始值 string
原始值 number
如果对象的值无法转为数字,则 Number 的函数返回 NaN
加法有两种情况,数字与数字相加;字符串与字符串相加。
加法会触发三种类型转换,转换为原始值;转为数字;转为字符串
结论 1、先两个两个操作数,转为原始值
结论 2、如果存在一个任意字符串,则也将另外一个也转为字符串,然后返回两个字符串链接结果
结论 3、否则将两个值转为数字类型,并返回和
结论 4、任何数字与 NaN 相加都是 NaN
toNumber | 将值转为数字 |
---|---|
undefined | NaN |
null | 0 |
boolean | true/1 false/0 |
number | 无需转换 |
string | 由字符串解析为数组 '324' ->324 |
[] | 0 |
toString | 将值转为数字 |
---|---|
undefined | 'undefined' |
null | 'null' |
boolean | 'true'/'false' |
number | 数字转字符串 |
string | 无需转换 |
function(){} | 'function(){}' |
console.info(1 + 1); //2 typeof number
console.info(1 + '1'); // 11 typeof stirng
console.info('1' + 1); // 11 typeof string
console.info(1 + ''); // '1'
console.info('' + 1); // '1'
console.info(1 + undefined); //NaN
console.info('' + undefined); //undefined
console.info(true + undefined); // NaN Number(true)+Number(undefined)=NaN,String(true)+String(undefined)='trueundefined'
console.info(false + undefined); // NaN
console.info('' + true); // 'true'
console.info(' ' + true); // ' true'
console.info('' + false); //'false'
console.info(' ' + false); //' false'
console.info(1 + function () {}); //'1function(){}'
console.info(1 + Object()); //'1[object Object]'
console.info('' + Object()); // [object object]
console.info(1 + Array()); // 1+Array().toString()=>1 +[].toString()
console.info(1 + Array); //1 +Array.toString()= >1+function Array(){[native code]}
console.info(1 + NaN); // NaN
console.info('' + NaN); //'NaN'
console.info('1' + NaN); //'1+NaN'
console.info(true + false); //1
console.info(true + true); //2
console.info(true + undefined); //NaN
console.info(true + NaN); //NaN
console.info(true + function () {}); //'truefunction(){}'
replace 理解
let string = '22dda';
// $1 找到的
// $2 找到所在的索引
// $3 替换前的源码
string.replace('要替换的正则、字符等', function ($1, $2, $3) {
return $2;
});
'我是{{name}} ,年龄{{age}},性别{{sex}}'.replace(/\{{(.+?)\}}/g, function ($1, $2, $3) {
console.info($1);
});
// {{name}}{{age}}{{sex}}
sort 理解
- sort 数组元素排序,排序可以使字母或者数字,并按升续降序
- 默认是字母升序
- 【注意】:数字是按字母顺序排序时 40 在 5 前面
- 【注意】:使用数字排序,必须通过一个函数作为参数调用
- 函数是指定数字是升序还是降序
- 会改变原始数组
语法
let array = [];
array.sort(sortFunction); //可选,但排序顺序,必须是函数
function sortFunction() {
return Math.round(Math.random()) ? 1 : -1;
}
// Math.round(0)// 0 四舍五入,向上取舍
// Math.round(0.49)// 0
// Math.round(0.2)// 0
// Math.round(0.5)// 1
// 返回值
// Array 对数组的引用,数组在原数组进行排序,不生成副本
冒泡算法
- 有几种冒泡算法?
- 分别实现冒泡算法?
typeof 常见类型
typeof | 值 |
---|---|
typeof null | "object" |
typeof undefined | "undefined" |
typeof [] | "object" |
typeof [''] | "object" |
typeof ['a'] | "object" |
typeof {} | "object" |
typeof {a:["test"]} | "object“ |
typeof NaN | "number" |
typeof true | "boolean" |
typeof false | "boolean" |
typeof new Date() | "object" |
typeof function(){alert('22')} | "function" |
typeof console.info('tt') | "tt" "undefined" |
typeof console | "object" |
typeof 1 | "number" |
typeof '2' | "string" |
typeof '' | "string" |
用一行代码将 [1,2,3,4] 随机打乱
[1, 2, 3, 4].sort(function () {
return Math.round(Math.random()) ? 1 : -1;
});
typeof null
'object';
let domList = document.querySelectorAll (‘div’),一句话将 domlist 转为数组
Array.from(domList);
哪个 http response header 不会影响浏览器缓存行为
a.cache-control b.etag c.age d.last-modified
通用首部字段 (请求头报文+响应报文)
- Cache-Control 控制缓存的行为
- Pragma http1.0 遗留,no-cache 时禁用缓存
请求首部字段
- If-Match 比较 ETag 是否一致
- IF-None-Match 同上
- If-Modified-Since 比较资源最后更新的时间是否一致
- IF-Unmodified-Since 比较资源最后更新的时间是否一致
响应首部字段
- ETag 资源的匹配信息
实体首部字段
- Expires http1.0 的遗留物,实体主体过期的时间
- Last-Modified 资源的最后一次修改时间
合理的前端项目结构分层
挑选自己或者公司项目,遇到的问题、解决的思路简单阐述下
全面解析一个任意 url 的所有参数为 object,注意边界条件
var url = 'https://www.baidu.com/?user=admin&id=23&id=555&city=%E9%A2%9C%E8%89%B2&status=disabled';
var newUrl = url.replace(/^.+(\?)/, '');
var url1 = newUrl.split('&');
var ob = {};
var keys = [];
var values = [];
url1.map((item, index) => {
var temp = [];
temp = item.split('=');
keys.push(temp[0]);
values.push(temp[1]);
if (ob[temp[0]]) {
ob[temp[0]] = [ob[temp[0]]].concat(temp[1]); //如果存在 则合并成为数组
} else {
ob[temp[0]] = decodeURIComponent(temp[1]);
}
});
console.info(ob);
ob = {
user: 'admin',
id: [23, 555], //合并id相同的为数组
city: '颜色', //中文编码
enabled: true, // 未指定的key约定值为true
};
实现一个最简单的模板渲染引擎
- 要点一 replace 的用法
第一个替换的,第二回调函数,回调函数有三个参数,第一个要找到的,第二个找到的索引,第三个原先的字符
- 对象 key 转数组
# {{(.+?)}}
- 正则这个或者那个:
{{|}}
结果:我是姓名,年龄 18,性别 undefined
let template = '我是{{name}} ,年龄{{age}},性别{{sex}}';
let data = {
name: '姓名',
age: 18,
};
function cover(template, data) {
let arr = Object.keys(data);
let temp = template.replace(/\{{(.+?)\}}/g, function ($1) {
for (let i = 0; i < arr.length; i++) {
if ($1.indexOf(arr[i]) > -1) {
return $1.replace(arr[i], arr[i]).replace(/\{{|\}}/g, '');
}
}
});
return temp;
}
cover(template, data);
字符串查找
使用最基本遍历实现查找字符串,并返回第一次出现的问题,找不到返回-1 a=‘34’ b=‘1234567’ 返回 2 a=‘35’ b=‘1234567’ 返回-1 a=‘355’ b=‘’ 12354355 返回 5
function compare(a, b) {
return b.indexof(a);
}
compare(a, b);
数据绑定基本实现
详见 MDN 的对象 Object.defineProperty() 方法的使用
- 问题一 Object.defineProperty(obj,key,options) 的使用
- 其中 option 里面的选项以及主要的 get 和 set 方法
{
get: function name() {
return '你要改变的值';
}
set: function value(value) {
//set会返回一个value
//这个value就是变更后的值,怎么处理
obj.key = value;
}
}
- bind()。【与此同类似的需要懂 call bind apply】其次是使用函数时候,怎么给另外一个对象绑定 this,因为此题目用到一个返回并返回这个 this,的 key 值,这时候需要处理
func.bind(obj)('你的参数'); // func 是函数,里面有this, obj 就是要操作的函数的那个
let obj = {
key_1: 1,
key_2: 2,
};
function func(key) {
console.info(key + '的值子发生变化' + this[key]);
}
bindData(obj, func);
obj.key_1 = 2; //此时自动输出 变化为2
obje.key_2 = 1; //此时自动输出变化为1
let obj = {
key_1: 1,
key_2: 2,
};
function func(key) {
console.info(key + '的值子发生变化' + this[key]);
}
function bindData(obj, func) {
for (let item in obj) {
Object.defineProperty(obj, item, {
get: function () {
return obj.item;
},
set: function (value) {
obj.item = value;
func.bind(obj)(item);
},
});
}
}
bindData(obj, func);
obj.key_1 = 2; //此时自动输出 变化为2
obj.key_2 = 1; //此时自动输出变化为1
数据结构处理
输出有多个儿子的人的名字
let data = {
name: 'jack',
child: [
{ name: 'jack1' },
{ name: 'jack2', child: [{ name: 'jack2_1', child: { name: 'jack2-1-1' } }, { name: 'jack2_2' }] },
{ name: 'jack3', child: { name: 'jack3-1' } },
],
};
程序题 1
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.info(new Date(), i);
}, 1000);
}
console.info(new Date(), i);
function Person(name) {
this.name = name;
}
var a = Person('a');
var b = new Person('b');
var c = Person;
console.info(a.name);
console.info(b.name);
console.info(c.name);