数据结构与算法
栈和队列的区别
网络基础
HTTP 无状态怎么理解
可以从REST的角度来理解这个问题。我们知道REST风格是无状态的。而REST是基于HTTP协议的,所以REST的无状态基本就可以解释HTTP的无状态。
TCP三次握手与四次挥手
三次握手
为了准确无误地将数据送到目标处,TCP采用三次握手策略,过程中使用了TCP的标志:SYN和ACK.
三次握手
Client --> 置SYN标志 序列号 = J,确认号 = 0 ----> Server
Client <-- 置SYN标志 置ACK标志 序列号 = K, 确认号 = J + 1 <-- Server
Clinet --> 置ACK标志 序列号 = J + 1,确认号 = K + 1 --> Server
四次挥手
Client -> 发送FIN 序列号 = J,确认号 = 0 --> Server
Server -> 发送ACK 确认号 = J + 1
Server -> 发送FIN 序列号 = K, 确认号 = 0 -> Client
Client -> 发送ACK 确认号 = K+1
HTTPS
HTTPS是在HTTP与TCP之间添加一个安全协议层(SSL或TSL).
网络请求中往往中间需要很多服务器或者路由器的转发,中间的节点都可能篡改信息,而如果使用HTTPS,密钥在请求客户端和终点站才有,所以相对于HTTP会更安全,就是因为HTTPs利用SSL/TSL协议传输,它包含证书等安全信息,保证了传输过程的安全性。
前端其他问题
前端工程价值
- 解放前后端互相在开发进度上的依赖问题,前后端可以同时进行
- 为简化用户使用,提高交互体验/用户体验
- 解决浏览器兼容问题
- 提高浏览速度(性能)
- 跨平台应用的支持
- 展现数据,数据处理
- 降低后端压力
浏览器缓存技术
** Etag **
当发送一个服务器的请求时,浏览器会首先进行缓存过期的判断,浏览器根据缓存过期的时间判断缓存文件是否过期。
- 若没有过期,则不向服务器发送请求,直接使用缓存中的结果
Session Cookie LocalStorage
全局环境与局部环境
JS对象 BOM DOM
浏览器的基本组成与页面渲染原理
为什么不能频繁操作DOM
重排与重绘
前端模块化
https://segmentfault.com/p/1210000007731421?from=singlemessage&isappinstalled=1
CommonJs规范 - NodeJs实现 同步
分支 异步
AMD RequireJs
CMD SeaJs
UMD 前后端整合
ES6 - I import export
前端优化
前端兼容性
渐进增强和优雅降级
** 渐进增强 ** :针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
** 优雅降级 ** :一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。
前端安全性
常见的几种安全攻击
SQL 注入
XSS
CSRF
前端工具
模块打包工具
** Webpack **
- 模块打包工具
- 管理依赖模块间依赖,生成优化并且合并后的静态资源文件
- 编译输出静态文件,将代码分割成不同的chunk,实现按需加载,降低初始化时间
- 所有文件都是模块, html, js, css, 图片等
- 模块加载器, 支持串联操作
- 以commonJs的形式书写,但对AMD/CMD的支持也比较全面,方便旧项目进行代码迁移
HTML5
Webworker
Js中的多线程实现
Websocket
SVG
Canvas
CSS
position的值, relative和absolute分别是相对于谁进行定位的?
盒模型
IE盒模型与其他浏览器的盒模型
CSS选择器的优先级
JavaScript
ES6
Js运行机制
- Javascript 是单线程
Javascript 的单线程与它的用途有关 - 任务队列
任务可分为两种,一种是同步任务,一种是异步任务。
** 同步任务 :在主线程上排队执行的任务,只有前一个任务执行完毕后,才能执行后一个任务;
** 异步任务 :不进入主线程,而进入任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务要以执行了,该任务才会进入主线程执行。
** 异步任务运行机制如下:
1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
4)主线程不断重复上面的第三步。
** 只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
- 事件和任务队列
** 任务队列 **
- 是一个事件的队列,也可以理解成是消息队列。主线程读取任务队列就是读取里面有哪些事件。
- 任务队列里面不止有IO设备的事件,也有用户操作产生的事件。只要指定过回调函数,这些事件发生时就会进入任务队列中,等待主线程读取。主线程执行异步任务,就是执行对应的回调函数。
-
主线程会首先检查执行时间,某些事件只有到了规定的时间,才能返回主线程。如setTimeout().
** Event Loop **
主线程从事件队列中读取事件的过程是不断循环的,所以整个的运行机制就是一个Event Loop,也叫事件循环。
- 定时器
除了放置异步任务的事件,"任务队列"还可以放置定时事件,即指定某些代码在多少时间之后执行。这叫做"定时器"(timer)功能,也就是定时执行的代码。
setTimeout()
setInterval()
HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。
需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。
Js事件模型
参考:https://segmentfault.com/a/1190000006934031?from=singlemessage&isappinstalled=1
DOM事件探秘
- ** 发布订阅模式(观察者模式)**
Javascript的事件模型基于发布请阅模式,可以让多个观察者对象同时监听某一个主题对象,这个主题对象的状态变化会通知所有的订阅者,使得它们能够做出反应。
以下是用js实现的一个发布订阅模式
var events = (function () {
var topics = {};
return {
publish: function (topic, info) {
console.log("publish a topic:" + topic);
if (topics.hasOwnProperty(topic)) {
topics[topic].forEach(function(handler) {
handler(info ? info : {});
});
}
},
subscribe: function(topic, handler) {
console.log("subscribe an topic" + topic);
if (!topics.hasOwnProperty(topic)) {
topics[topic] = [];
}
topics[topic].push(handler);
},
remove: function(topic, handler) {
if (!topics.hasOwnProperty(topics)) {
return;
}
var handlerIndex = -1;
topics[topic].forEach(function (element, index) {
if (element === handler) {
handlerIndex = index;
}
});
if (handlerIndex >= 0) {
topics[topic].splice(handlerIndex, 1);
}
}
};
})();
var handler = function (info) {
console.log(info);
}
events.subscribe("hello", handler);
events.publish("hello", "hello world");
- ** 事件与事件流 **
事件是与浏览器或文档交互的瞬间,如点击按钮,填写表格等,它是JS与HTML之间交互的桥梁。DOM是树形结构,如果同时给父子节点都绑定事件时,当触发子节点的时候,这两个事件的发生顺序如何决定?这就涉及到事件流的概念,它描述的是页面中接受事件的顺序。
事件流有两种:
** 事件冒泡(Event Capturing): ** 是一种从下往上的传播方式。事件最开始由最具体的元素(文档中嵌套层次最深的那个节点接受, 也就是DOM最低层的子节点), 然后逐渐向上传播到最不具体的那个节点,也就是DOM中最高层的父节点。
** 事件捕获(Event Bubbling): **与事件冒泡相反。事件最开始由不太具体的节点最早接受事件, 而最具体的节点最后接受事件。 - ** 事件模型 **
** DOM 0级模型 **
HTML代码中直接绑定:
<input type="button" onclick="fun()">
通过JS代码指定属性值:
var btn = document.getElementById('.btn');btn.onclick = fun;
移除监听函数:
btn.onclick = null;
这种方式所有浏览器都兼容,但是逻辑与显示并没有分离。
** IE事件模型 **
IE事件模型共有两个过程:
事件处理阶段(target phase)。事件到达目标元素, 触发目标元素的监听函数。
事件冒泡阶段(bubbling phase)。事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
事件绑定监听函数的方式如下:
attachEvent(eventType, handler)
事件移除监听函数的方式如下:
detachEvent(eventType, handler)
参数说明:
eventType指定事件类型(注意加on)
handler是事件处理函数
Example:
var btn = document.getElementById('.btn');
btn.attachEvent(‘onclick’, showMessage);
btn.detachEvent(‘onclick’, showMessage);
** DOM 2级模型 **
属于W3C标准模型,现代浏览器(除IE6-8之外的浏览器)都支持该模型。在该事件模型中,一次事件共有三个过程:
事件捕获阶段(capturing phase)。事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
事件处理阶段(target phase)。事件到达目标元素, 触发目标元素的监听函数。
事件冒泡阶段(bubbling phase)。事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
事件绑定监听函数的方式如下:
addEventListener(eventType, handler, useCapture)
事件移除监听函数的方式如下:
removeEventListener(eventType, handler, useCapture)
Example:
var btn = document.getElementById('.btn');
btn.addEventListener(‘click’, showMessage, false);
btn.removeEventListener(‘click’, showMessage, false);
参数说明:
eventType指定事件类型(不要加on)
handler是事件处理函数
useCapture是一个boolean用于指定是否在捕获阶段进行处理,一般设置为false与IE浏览器保持一致。
DOM事件模型中的事件对象常用属性:
- type用于获取事件类型
- target获取事件目标
- stopPropagation()阻止事件冒泡
- preventDefault()阻止事件默认行为
IE事件模型中的事件对象常用属性: - type用于获取事件类型
- srcElement获取事件目标
- cancelBubble阻止事件冒泡
- returnValue阻止事件默认行为
Javascript垃圾回收机制
** 标记清除 ** -> 最常见的垃圾回收方式
这是JavaScript最常见的垃圾回收方式,当变量进入执行环境的时候,比如函数中声明一个变量,垃圾回收器将其标记为“进入环境”,当变量离开环境的时候(函数执行结束)将其标记为“离开环境”。垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了
** 引用计数 ** -> 会出现因为循环引用而出现的无法回收而导致的内存泄漏
参考: http://www.jianshu.com/p/80ed3805edc3
创建对象的几种方式
- ** 使用{}
var bar = {
color: "blue"
};
- ** 使用new关键字 **
var bar = new Object();
bar.color = "blue";
- ** 使用构造函数 **
var Fun = function (color) {
this.color = color;
}
var bar = new Fun();
面向对象与继承的几种方式
** 原型编程泛型基本规则 **
- 所有的数据都是对象
- 要得到一个对象,不是通过 实例化类,而是找到一个对象作为原型并克隆它
- 对象会记住它的原型
- 如果对象本身无法响应某个请求,它会把这个请求委托给它自己的原型
** 实现方式 **
http://www.jb51.net/article/81766.htm
解决跨域问题
Jsonp
Cors
Document.domain
Document.name
HTML5 postMessage
AJAX
创建过程
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.(6)使用JavaScript和DOM实现局部刷新.
参考:https://segmentfault.com/a/1190000004322487
说说你对作用域链的理解
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
闭包
实现延迟打印1-5
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, i * 1000)
})(i);
}
for (var i = 0; i < 5; i++) {
setTimeout(function (i) {
return function() { console.log(i); };
}(i), i * 1000)
}
[1,2,3,4,5,6].forEach(i => {
setTimeout(function () {
console.log(i)
}, i * 1000);
});
严格模式 (use strict)
- 非严格模式下有些错误会在运行时被悄悄地忽略掉,而在严格模式下,这些错误会被抛出来,从而方便debug,通常来说,这是一种很好的实践。
- 防止给未声明的变量赋值
- 非严格模式下如果引用的this是null或者undefined的话会自动将this指向global对象,这种情况会造成一些比较难发现且头疼的bug。而在严格模式下,则会抛出错误。
- 防止在一个对象内重复定义属性。
- 安全使用eval()
- delete操作符
如何判读一个变量是个整数 (实现 isInteger(x))
在ES6中,Number.isInteger() 可以用来判断是否为整数。
在ES6之前的版本中这个问题则比较复杂,因为numberic的值通常是作为一个浮点数来存储的,只能用比较取巧的方式来实现这样的判断逻辑。
function isInteger(x) { return (x^0) === x; }
function isInteger(x) { return Math.round(x) === x; }
function isInteger(x) {
return (typeof x === "number") && (x % 1 === 0;;
}
此外也可以使用parseInt来实现
function isInteger(x) { return parseInt(x, 10) === x;}
数组操作 (split, reverse, push, slice, concat)
What will the code below output to the console and why?
var arr1 = "john".split('');
var arr2 = arr1.reverse();
var arr3 = "jones".split('');
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
JavaScript 设计模式
Angular
双向绑定如何实现
脏检查机制
React
虚拟DOM 实现机制
JQuery
Jquery 绑定事件的方法
target.on("click");
target.click(function() {
});
target.live("click", function () {
});
target.bind("click", function () {
});
#### Jquery的链式操作如何实现
# 正则表达式
****
#### 语法
> https://msdn.microsoft.com/zh-cn/library/ae5bf541(VS.80).aspx
#### 验证身份证
> ```
var reg=/^[1-9]{1}[0-9]{14}$|^[1-9]{1}[0-9]{16}([0-9]|[xX])$/;
面试题
更多面试题: https://www.toptal.com/javascript/interview-questions
鼠标点击页面中的任意标签,alert标签名称 (兼容性)
<script type="text/javascript" >
document.onClick() = function(event) {
var e = event || window.event;
var src = event.target || event.srcElement;
alert(src.tagName.toLowercase());
}
</script>
异步加载js方案,不少于两种
** 同步加载 **
就是我们平时使用的最多的方式,在页面中使用script标签
这种模式也叫阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当加载完成,才能进行下一步的操作,所以默认同步执行才是比较安全的。但是这种方式会造成页面的阻塞,所以一般建议把<script>标签放在<body>结尾,这样尽可能减少页面阻塞。
** 异步加载 **
异步加载 又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。主要有以下几中方式:
- ** Script Dom Element **
(function() {
var scriptEle = document.createElement("script");
scriptEle.type = "text/javascript";
scriptEle.async = true;
scriptEle.src="http://xxx/jquery.js";
var x = document.getElementByTagName("head")[0];
x.insertBefore(scriptEle, x.firstChild);
})();
google的使用方式
(function(){;
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
缺点: 执行完成之前会阻止onLoad事件的触发,而现在很多页面的代码都会在onLoad的时候做一些额外的渲染动作,所以还是会阻塞部分页面的初始化的处理。
- ** onload时的异步加载 **
;(function () {
if (window.attachEvent) {
window.attachEvent('load', asyncLoad)
} else {
window.addEventListener('load', asyncLoad)
}
var asyncLoad = function () {
var scriptEle = document.createElement('script')
scriptEle.type = 'text/javascript'
scriptEle.async = true
scriptEle.src = 'http://xxx/jquery.js'
var x = document.getElementByTagName('head')[0]
x.insertBefore(scriptEle, x.firstChild)
}
})()
注:DOMContentLoaded与load区别
前者是在document 已经解析完成,页面中的dom元素可用,但是页面中的图片,视频,音频等资源还未加载完,作用同jquery的ready. 后者的区别在于页面中所有资源包括js都加载完成。
- ** 其他方法**
** XHR Injection **
通过XMLHttpRequest来获取javascript,然后创建一个script元素插入到DOM结构中。ajax请求成功后设置script.text为请求成功后返回的responseText
var getXmlHttp = function () {
var obj
if (window.XMLHttpRequest)
obj = new XMLHttpRequest()
else
obj = new ActiveXObject('Microsoft.XMLHTTP')
return obj
}
var xhr = getXmlHttp()
xhr.open('GET', 'http://xxx.com/jquery.js', true)
xhr.send()
xhr.onReadyStateChange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var script = document.createElement('script')
script.text = xhr.response.text
document.getElementsByTagName('head')[0].appendChild(script)
}
}
** XHR Eval **
与XHR Injection对responseText的执行方式不同,直接把responseText放在eval()函数里面执行。
** Script In IFrame **
在父窗口插入一个iframe元素,然后再iframe中执行加载JS的操作。
var insertJS = function(){alert(2)};
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var doc = iframe.contentWindow.document;//获取iframe中的window要用contentWindow属性。
doc.open();
doc.write("<script>var insertJS = function(){};<\/script><body onload='insertJS()'></body>");
doc.close();
- ** HTML5新属性:async和defer属性 **
** defer属性 **:IE4.0就出现。defer属声明脚本中将不会有document.write和dom修改。浏览器会并行下载其他有defer属性的script。而不会阻塞页面后续处理。注:所有的defer脚本必须保证按顺序执行的。
<script type="text/javascript" defer></script>
** async属性 **:HTML5新属性。脚本将在下载后尽快执行,作用同defer,但是不能保证脚本按顺序执行。他们将在onload事件之前完成。
<script type="text/javascript" defer></script>
Firefox 3.6、Opera 10.5、IE 9和最新的Chrome和Safari都支持async属性。可以同时使用async和defer,这样IE 4之后的所有IE都支持异步加载。
没有async属性,script将立即获取(下载)并执行,期间阻塞了浏览器的后续处理。如果有async属性,那么script将被异步下载并执行,同时浏览器继续后续的处理。
** 总结 **: 对于支持HTML5的浏览器,实现JS的异步加载只需要在script元素中加上async属性,为了兼容老版本的IE还需加上defer属性;对于不支持HTML5的浏览器(IE可以用defer实现),可以采用以上几种方法实现。原理基本上都是向DOM中写入script或者通过eval函数执行JS代码,你可以把它放在匿名函数中执行,也可以在onload中执行,也可以通过XHR注入实现,也可以创建一个iframe元素,然后在iframe中执行插入JS代码。
设计一种方案,确保页面中所有js加载完全
function loadScript (url, callback) {
var script = document.createElement('script')
script.type = 'text/javascript'
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || script.readyState == 'complete') {
script.onreadystatechange = null
callback()
}
}
} else {
script.onload = function () {
callback()
}
}
script.src = url
document.getElementsByName('head')[0].appendChild(script)
}