页面布局
1. 浮动定位 (兼容性好)
2. 绝对定位 (脱离文档流)
3. flex布局 (高等级浏览器/移动端,优先于1,2)
4. 表格布局 (兼容性好,ie8,高度一致性)
5. 网格布局 (css3,高等级浏览器/移动端)
6. inline-block
表格布局
延伸: 1.解决方案的优缺点 2.如果高度条件去掉,中间内容溢出,那些布局不适用? 3.兼容性,实用性
2.如果高度条件去掉,中间内容溢出,那些布局不适用?
=> 浮动定位(x 溢出部分会往左边靠,解决方案: 创建bfc -> overflow 除了 visible 以外的值(hidden,auto,scroll)),
=> 绝对定位(√ 中间内容高度自动变,左右不变)
=> flexbox布局,网格布局,表格布局(√ 左右跟着变)
BFC
原理/特性:
- 内部的Box会在垂直方向上一个接一个的放置。
- 垂直方向上的距离由margin决定
- bfc的区域不会与float的元素区域重叠。
- 计算bfc的高度时,浮动元素也参与计算 (清除浮动)
- bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。
- calc() 函数用于动态计算长度值。
需要注意的是,运算符前后都需要保留一个空格
width: calc(100% - 600px);
- 几种解决inline-block间隙的方案 (inline-blcok块之间的不可见符号会被保留父层字体的1/3大小的空间)
- 改变书写结构
<li>item1</li><li>item2</li><li>item3</li><li>item4</li><li>item5</li>
- 打包工具
- css hack
> 1、将父容器的字体大小设置为0,可解决绝大多数浏览器(老版本safari不支持)
> 2、针对不支持上条的浏览器设置字块或字符间间隙letter-spacing/word-spacing,推荐letter-spacing,因为此属性不会产生负间隙,但需要注意,要在子元素上设置letter-spacing:0
> 3、如果你转化但是块对象,那需要为低版本浏览器设置inline兼容,不让样式会乱掉
.parent {
letter-spacing: -.3333em;
font-size: 0;
}
.child {
display: inline;
display: inline-block;
}
从Safari 10.1, Firefox 52, Chrome 60,Edge 15开始受到支持。
网格中的行和列
grid-template-columns和grid-template-rows属性来定义网格中的行和列
fr单位: 新的fr单位代表网格容器中可用空间的一等份
在轨道清单中使用repeat()
grid-auto-rows 和 grid-auto-columns 定义一个设置大小尺寸的轨道
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 200px;
}
网格线
grid-column-start, grid-column-end, grid-row-start 和 grid-row-end
.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 100px;
}
.box1 {
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 3;
}
.box2 {
grid-column-start: 1;
grid-row-start: 3;
grid-row-end: 5;
}
网格间距
grid-column-gap 和 grid-row-gap 属性来创建,或者直接使用两个合并的缩写形式 grid-gap
/* 网格间距 */
/* grid-column-gap: 10px; */
grid-gap: 10px;
嵌套网格
当这些元素不是网格容器的直接子级元素时,它们不会参与到网格布局中,并显示为正常的文档流。
如果我把 box1 设置成 display: grid 我可以给它定义轨道然后它也会变成一个网格元素,它的子级元素也会排列在这个新网格元素中。
使用z-index控制层级
多个网格项目可以占用同一个网格单位。如果我们回到之前根据网格线编号放置网格项目的话,我们可以更改此项来使两个网格项目重叠。
ccs盒模型
ccs盒模型: 标准模型 + IE模型(怪异盒)
标准模型和IE模型的区别?
宽高计算方式不同
标准模型: content
IE模型: content+border+padding
css如何设置这两种模型?
box-sizing: content-box; //默认
box-sizing: border-box; //IE
JS如何获取盒子的宽高?
dom.style.width // 内联样式
dom.currentStyle.width // IE
window.getComputeStyle(dom).width // chrome firefox
dom.getBoundingClientRect().width
根据盒模型解释边距重叠?
外边距重叠. 规则取最大值
边距重叠解决方案?
BFC
DOM事件类
DON事件的级别
DOM0 dom.onload = function() {}
DOM2 dom.addEventListener('click', function(){}, false)
DOM3 dom.addEventListener('keyup', function(){}, false) // 多了事件类型(鼠标,键盘)
DOM事件模型 (冒泡,捕获)
DOM事件流
事件捕获 ===> 目标阶段 => 冒泡window 对象
第一阶段: 捕获
第二阶段: 目标
第三阶段: 冒泡
描述DOM事件捕获的流程
window -> document -> html(document.documentElement) -> body -> … -> 目标元素
Event对象的常见应用
event.preventDefault() // 阻止默认事件
event.stopPropagation() // 阻止冒泡
event.stopImmediatePropagation() // 阻止相同类型的事件触发, eg: 注册点击事件a,b. a使用stop,触发a, b不再执行.
event.currentTarget // 当前绑定事件的对象
event.target // 事件代理
自定义事件
//创建事件, Event是无法传递参数的
var event = new Event('build');
//创建事件, CustomEvent是可以传递参数的
var event = new CustomEvent('build', { detail: elem.dataset.time });
// 监听事件Listen for the event.
elem.addEventListener('build', function (e) { //... }, false);
// 分发/触发事件Dispatch the event.
elem.dispatchEvent(event);
http协议类(理论)
HTTP协议的主要特点
- 无连接 (连接一次就断开)
- 无状态 ( ? <–> 服务端 )
- 简单快速
- 灵活
HTTP报文的组成部分
请求报文: 请求行 + 请求头 + 空行 + 请求体
响应报文: 响应行 + 响应头 + 空行 + 响应体
HTTP的方法
POST和GET的区别
- GET在浏览器回退时是无害的(不会再次请求), 而POST会再次请求
- GET请求会被浏览器主动缓存, 而POST不会, 除非主动设置
- GET请求参数会被完整的保留在浏览器历史记录里, 而POST中的参数不会被保留
- GET请求参数是有长度限制的, 而POST没有
- GET参数通过URL传递, POST在request body中
HTTP状态码
1xx - 指示信息
2xx - 成功
3xx - 重定向
4xx - 客户端错误
5xx - 服务期错误
什么是持续连接
什么是管线化
基本概念
特点/注意点
缓存/CORS
原型链类
创建对象的几种方法
// 字面量
var a = {name: 'a'}
var b = new Object({name: 'b'})
// 构造函数
function m(){}
var c = new m()
// Object.create() 放在prototype下(通过原型链关联)
var p = {name: 'd'}
var d = Object.create(p)
d.__proto__ === p // true
原型, 构造函数, 实例, 原型链
原型链的最终是Object
instanceof 的原理
用来判断某个构造函数的 prototype 属性所指向的对象是否存在于另外一个要检测对象的原型链上( 实例.__proto__, Object )
简单说就是判断一个引用类型的变量具体是不是某种类型的对象
判断实例是哪个创建的()
a.prototype === b.__proto__ || b.__proto__.__proto__
a.__proto__.constructor === 构造函数
new 运算符
new运行原理
var new2 = function (func) {
var o = Object.create(func.prototype);
var k = func.call(o); // 触发构造函数
if (typeof k === 'object') {
return k;
} else {
return o;
}
};
面向对象类
类的声明
// es6之前
function Animal () {}
// es6
class
如何实现继承
继承的几种方式
几种继承的区别
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>OOP</title>
</head>
<body>
<script>
function Parent() {
this.son = 123
this.play = [1,2,3]
}
Parent.prototype.say = function() {
console.log(this.name)
}
// 构造函数继承 .不继承原型
function Son() {
Parent.call(this)
this.name = 'son'
}
// new Son().say() // err say is not
/**
* 借助原型链实现继承
**/
function Son2(){
this.name = 'son2'
}
Son2.prototype = new Parent()
// 可以继承原型上的方法
var a = new Son2()
console.log(a.say()) // son2
// 缺点
var a1 = new Son2()
var a2 = new Son2()
console.log(a1.__proto__ === a2.__proto__)
// 对象修改 (通过修改原对象) 会影响到原型上的对象 => 影响所有继承
a1.play.push(4)
console.log(a2.play) // [..., 4]
// 组合方式
function Son3() {
Parent.call(this)
this.name = 'son3'
}
Son3.prototype = new Parent()
// 解决了引用对象一样的问题
var a3 = new Son3()
var a4 = new Son3()
a1.play.push(4)
console.log(a2.play) // [...,3]
// 缺点: 父类构造函数执行了两次, 不能区分实例是谁构造的.(构造函数是父类的构造函数)
console.log(a3 instanceof Son3 , a3 instanceof Parent)
console.log(a3.constructor)
// 组合方式优化1
function Son4() {
Parent.call(this)
this.name = 'son4'
}
// 解决了父类构造函数执行了两次
Son4.prototype = Parent.prototype
var a5 = new Son4()
var a6 = new Son4()
// 缺点: 还是不能区分实例是谁构造的.(构造函数是父类的构造函数)
console.log(a5 instanceof Son4 , a6 instanceof Parent)
console.log(a5.constructor)
// 组合方式优化2
function Son5() {
Parent.call(this)
this.name = 'son5'
}
// Son5.prototype => {}
Son5.prototype = Object.create(Parent.prototype)
Son5.prototype.constructor = Son5
// 解决了不能区分实例是谁构造的.
var a7 = new Son5()
var a8 = new Son5()
console.log(a7 instanceof Son5 , a8 instanceof Parent)
console.log(a7.constructor)
</script>
</body>
</html>
console.log
通信类
什么是同源策略及限制(限制跨域通信)
源: 协议(http) + 域名 + 端口(默认80) (哪个不一样就是跨域了)
ajax(同源通信)
前后端通信
- Ajax
- WebSocket
- CORS
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
如何创建ajax
- XMLHttpRequest对象的工作流程
- 兼容性处理(IE)
- 事件的触发条件
- 事件的触发顺序 (readyState)
/**
* [function 对象浅复制]
* @param {[type]} dst [description]
* @param {[type]} obj [description]
* @return {[type]} [description]
*/
util.extend = function (dst, obj) {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
dst[i] = obj[i];
}
}
};
/**
* [json 实现ajax的json]
* @param {[type]} options [description]
* @return {[type]} [description]
*/
util.json = function (options) {
var opt = {
url: '',
type: 'get',
data: {},
success: function () {},
error: function () {},
fail: function () {},
};
util.extend(opt, options);
if (opt.url) {
var xhr = XMLHttpRequest
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP');
var data = opt.data,
url = opt.url,
type = opt.type.toUpperCase(),
dataArr = [];
for (var k in data) {
dataArr.push(k + '=' + data[k]);
}
if (type === 'GET') {
url = url + '?' + dataArr.join('&');
xhr.open(type, url.replace(/\?$/g, ''), true);
xhr.send();
}
if (type === 'POST') {
xhr.open(type, url, true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send(dataArr.join('&'));
}
xhr.onload = function () {
// 206 媒体文件
if (xhr.status === 200 || xhr.status === 304 || xhr.status === 206) {
var res;
if (opt.success && opt.success instanceof Function) {
res = xhr.responseText;
if (typeof res === 'string') {
res = JSON.parse(res);
opt.success.call(xhr, res);
}
}
} else {
if (opt.error && opt.error instanceof Function) {
opt.error.call(xhr, res);
}
if (opt.fail && opt.fail instanceof Function) {
opt.fail.call(xhr, res);
}
}
};
}
};
跨域的几种方式 => jsonp WebSocket CORS Hash postMessage
- jsonp (利用script标签异步加载)
原理:
创建请求script标签,带回调函数名, 全局中有回调函数接受请求回来的数据, 添加到document中. script标签响应完后移除,并清除回调函数.
/**
* [function 判断是否为函数]
* @param {[type]} source [description]
* @return {[type]} [description]
*/
util.isFunction = function (source) {
return '[object Function]' === Object.prototype.toString.call(source);
};
/**
* [function 在页面中注入js脚本]
* @param {[type]} url [description]
* @param {[type]} charset [description]
* @return {[type]} [description]
*/
util.createScript = function (url, charset) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
charset && script.setAttribute('charset', charset);
script.setAttribute('src', url);
script.async = true;
return script;
};
/**
* [function jsonp]
* @param {[type]} url [description]
* @param {[type]} onsucess [description]
* @param {[type]} onerror [description]
* @param {[type]} charset [description]
* @return {[type]} [description]
*/
util.jsonp = function (url, onsuccess, onerror, charset) {
var callbackName = util.getName('tt_player');
window[callbackName] = function () {
if (onsuccess && util.isFunction(onsuccess)) {
onsuccess(arguments[0]);
}
};
var script = util.createScript(url + '&callback=' + callbackName, charset);
script.onload = script.onreadystatechange = function () {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
script.onload = script.onreadystatechange = null;
// 移除该script的 DOM 对象
if (script.parentNode) {
script.parentNode.removeChild(script);
}
// 删除函数或变量
window[callbackName] = null;
}
};
script.onerror = function () {
if (onerror && util.isFunction(onerror)) {
onerror();
}
};
document.getElementsByTagName('head')[0].appendChild(script);
};
- websocket
- CORS (支持跨域请求的类Ajax ==> fetch) (头信息之中,增加一个Origin字段)
CORS与JSONP的使用目的相同,但是比JSONP更强大。
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
// CORS【参考资料】http://www.ruanyifeng.com/blog/2016/04/cors.html
// url(必选),options(可选)
fetch('/some/url/', {
method: 'get',
}).then(function (response) {
}).catch(function (err) {
// 出错了,等价于 then 的第二个参数,但这样更好用更直观
});
- Hash (#,url改变页面不刷新)(iframe)
// 利用hash,场景是当前页面 A 通过iframe或frame嵌入了跨域的页面 B
// 在A中伪代码如下:
var B = document.getElementsByTagName('iframe');
B.src = B.src + '#' + 'data';
// 在B中的伪代码如下
window.onhashchange = function () {
var data = window.location.hash;
};
- postMessage (h5新添加,两窗口的跨域通信)
// postMessage
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Awindow.postMessage('data', 'http://B.com');
// 在窗口B中监听
Bwindow.addEventListener('message', function (event) {
console.log(event.origin); // 消息的来源/域名+端口
console.log(event.source); // 消息的窗口对象的引用
console.log(event.data); // 传递过来的对象
}, false);
安全类
- CSRF (跨域请求伪造)
- 基本概念和缩写
- 攻击原理
- 防御措施
攻击原理:
CSRF攻击的条件
1. 用户在被攻击的网站登录过
2. 被攻击的网站某个api存在漏洞
防御措施:
- token的验证 (本地存储token, 访问api时需要带token)
- Referer验证 (访问来源验证 -> 是否是自己许可的域名.)
- 隐藏令牌 (类cookie. eg: 放在header中)
- XSS (跨域脚本攻击)
攻击原理:
在你合法的操作中注入脚本(js/函数)执行
防御措施:
反转义, DOM Parse, 过滤, 校正
算法类
按部分解,一步一步来,卡住可需要面试官.
先从数列中取出一个数作为“基准”。
分区过程:将比这个“基准”大的数全放到“基准”的右边,小于或等于“基准”的数全放到“基准”的左边。
再对左右区间重复第二步,直到各区间只有一个数。
var quickSort = function (arr) {
if (arr.length <= 1) return arr
// (中心)中间index 取整
var pivotIndex = Math.floor(arr.length / 2)
// 基准值
var pivot = arr.splice(pivotIndex, 1)[0]
var left = []
var right = []
for (let i = 0; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(pivot,quickSort(right)) //链接左数组、基准数构成的数组、右数组
}
var arr = [12,312,3,21,31,2,341,24,1,312,4,12,3,124,123412]
console.log(quickSort(arr))
在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
从剩余未排序元素中(第二…)继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
挑最小的出来放排序的第一位(互换位置), 其他继续挑最小的放下一位(第二…)
// 选择排序
var selectSort = function (arr) {
// 最小值的Index, 存储当前被替换的值
var minIndex, temp;
var len = arr.length
for (let i = 0; i < len; i++) {
minIndex = i
for (let j = i + 1; j < len - 1; j++) {
if(arr[j] < arr[minIndex]) {
minIndex = j
}
}
temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
return arr
}
冒泡排序
// 冒泡排序
var bubblingSort = function (arr) {
arr = arr.slice() // 防止改变源数组
var bool = true;
var temp;
while(bool) {
bool = false
for (let i = 0; i < arr.length; i++) {
if(arr[i] > arr[i+1]) {
temp = arr[i]
arr[i] = arr[i+1]
arr[i+1] = temp
bool = true
}
}
}
return arr
}
插入排序
var insertSort = function (arr) {
arr = arr.slice() // 防止改变源数组
var bool = true
// 最后排序过的index
let lastIndex = 0
while (bool) {
// 提取X
let xIndex = lastIndex
if (xIndex === arr.length - 1) bool = false
let x = arr.splice(xIndex, 1)[0]
// 记录位置 如果现在排序过的元素 > 提取的元素 将排序过的元素向右移一格
let tempIndex = lastIndex;
for (let i = lastIndex; i >= 0; i--) {
if (arr[i] >= x) {
tempIndex = i
}
}
// 插入的位置
arr.splice(tempIndex, 0, x)
lastIndex++
}
return arr
}
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
function factorial(n) {
if (n === 0) {
return 1
}
return n * factorial(n - 1)
}
如果传入的参数值特别大,那么这个调用栈将会非常之大,最终可能超出调用栈的缓存大小而崩溃导致程序执行失败。那么如何解决这个问题呢?使用尾递归。
尾递归
尾递归是一种递归的写法,可以避免不断的将函数压栈最终导致堆栈溢出。通过设置一个累加参数,并且每一次都将当前的值累加上去,然后递归调用。
function factorial(n, total = 1) {
if (n === 0) {
return total
}
return factorial(n - 1, n * total)
}
<!--factorial(3, 1) -->
<!--factorial(2, 3) -->
<!--factorial(1, 6) -->
<!--factorial(0, 6) -->
调用栈不再需要多次对factorial进行压栈处理,因为每一个递归调用都不在依赖于上一个递归调用的值。因此,空间的复杂度为o(1)而不是0(n)。
注意:尾递归不一定会将你的代码执行速度提高;相反,可能会变慢。不过,尾递归可以让你使用更少的内存,使你的递归函数更加安全 (前提是你要开启harmony模式)。
在Nodejs下面,我们可以通过开启strict mode, 并且使用–harmony_tailcalls来开启尾递归(proper tail call)。
node --harmony_tailcalls factorial.js
递归虽然使用起来方便,但是递归是在函数内部调用自身,当递归次数达到一定数量级的时候,他形成的调用栈的深度是很可怕的,很可能会发生“栈溢出”错误。尾递归优化,就是利用尾调用优化的原理,对递归进行优化。
function fibonacci (n) {
return n <= 1 ? 1 : fibonacci(n - 1) + fibonacci(n - 2);
}
优化
function fibonacci (n, ac1, ac2) {
(ac1 = ac1 || 1), (ac2 = ac2 || 1);
return n <= 1 ? ac2 :fibonacci(n - 1, ac2, ac1 + ac2);
}
此优化有两个点:首先进行了算法上的优化,减少了很多重复的计算,时间复杂度大大降低;第二进行了尾递归优化,按理说不会发生“栈溢出”。我们可以到控制台中再尝试,发现速度的提升不是一般的快,证明第一个优化生效了,但是当我们允许fibonacci(10000)的时候,报错了:Uncaught RangeError: Maximum call stack size exceeded,这就说明我们的尾递归优化并没有生效。为什么呢?
局限性
上面说到,我们直接再浏览器的控制台中执行fibonacci(10000)的时候,发生了栈溢出,这是为什么呢?关于这一点,我目前查阅资料之后的理解就是,虽然es6已经提出了要实现尾递归优化,但是真正落地实现了尾递归优化的浏览器并不多。所以当我们使用尾递归进行优化的时候,依旧发生了“栈溢出”的错误。
优化方法->蹦床函数
function trampoline(f) {
while (f && f instanceof Function) {
f = f();
}
return f;
}
function fibonacci (n, ac1, ac2) {
(ac1 = ac1 || 1), (ac2 = ac2 || 1);
return n <= 1 ? ac2 :fibonacci.bind(null, n - 1, ac2, ac1 + ac2);
}
trampoline(fibonacci (100000))
// Infinity
两个函数结合就可以将递归状态为循环,栈溢出的问题也就解决了。
波兰式 逆波兰式