栅格化布局中的元素:column列,row行,gutter列之间的距离,container容器
栅格是否可以嵌套
SVG:
SVG 是一种使用 XML 描述 2D 图形的语言。
在 SVG 中,每个被绘制的图形均被视为对象。
Canvas:
Canvas 相当一个画布,通过 JavaScript 来绘制 2D 图形。
Canvas 是逐像素进行渲染的。
可以在canvas中引入jpg或png这类格式的图片,在实际开发中,大型的网络游戏都是用canvas画布做出来的,并且canvas的技术现在已经相当的成熟。百度地图就是用canvas技术做出来的。
另外从技术发面来讲canvas里面绘制的图形不能被引擎抓取
input是单行框,html标签不闭合
而textarea是多行文本输入框,html标签闭合
若要实现高度随内容自适应可以用 contenteditable=“true”
语义化标签:nav; header; content; footer; article;video
input增加了许多表单输入类型。比如说email,url。
增加许多表单元素。比如datalist,output
增加了画布Canvas和地理定位
meta有name属性用于描述网页,比如网页的关键词,叙述等。与之对应的属性值为content,content中的内容是对name填入类型的具体描述,便于搜索引擎抓取。
meta设置utf-8
meta移动端适配的viewport
监听网络状态
window.addEventListener('online',function(){
//连接
})
window.addEventListener('offline',function(){
//断开
})
获取定位
navigator.geolocation//判断是否可定位
navigator.geolocation.getCurrentPosition()
button 定义可点击按钮(多数情况下,用于通过 JavaScript 启动脚本)。
submit 定义提交按钮。提交按钮会把表单数据发送到服务器。
radio 定义单选按钮。
checkbox 定义复选框。
file 定义输入字段和 "浏览"按钮,供文件上传。
hidden 定义隐藏的输入字段。
password 定义密码字段。该字段中的字符被掩码。
reset 定义重置按钮。重置按钮会清除表单中的所有数据。
text 定义单行的输入字段,用户可在其中输入文本。默认宽度为 20 个字符。
inline-block元素间有空格或是换行,因此产生了间隙。
父级设置
.box {
font-size: 0;
letter-spacing: -3px;
}
//font-size:0
//letter-spacing 属性明确了文字的间距行为。
代码层面:css解析从右往左,合理设置选择器。css放上面,script放下面
缓存利用:使用CDN加速,http缓存头等
请求数量:合并css样式和js脚本,使用css图片精灵,图片懒加载
请求带宽:压缩文件,开启GZIP
CDN加速:当用户发起访问时,他的访问请求被智能DNS定位到离他较近的缓存服务器。
当服务器没有缓存数据,服务器就去请求离他最近的服务器中的数据。天猫超市就是这样用的。
图片等静态资源在使用前提前请求。 比如通过点击等事件才会用到的图片。
最常见的就是预加载图片新建一个image,添加src路径,当浏览器渲染时就会使用预加载的图片
1、打开能看到源码和request、response的浏览器,如chrome,查看源码输出
2、如果是后端问题,那么后端查看accesslog、程序日志,看看是否有问题
3、如果是前端问题,那么根据给出的js异常之类的排查
大部分的PC端的优化其实都适用移动端。
这里说一下移动端需要的。
移动端:
收到一个HTML页面的请求时,到屏幕上渲染出来要经过很多个步骤。
构建DOM树
树建CSSOM树
运行JavaScript
创建Render树
生成布局
绘制(Painting)
优化方法:
首屏内联CSS
使用媒体查询优化CSS
JS脚本async异步
减小CSS和JS的体积
脚本放到最底部
首先用3g网络测试一下:如果卡就是http请求数据可能过大。
可以合并JS脚本和CSS文件,css精灵图,对HTTP传输进行gzip压缩。
css放顶部,javascript放底部。
可能服务端出问题:
比如用户访问量大,并发量大。
mysql没有优化好,造成死锁。
可以用CDN加速把数据放在离用户更近的位置。
请求报文:
1,请求行。 该行包含了一个方法和一个请求的URL,还包含HTTP 的版本。
2,请求头
3,空行
4,请求体。
响应报文:
1,状态行。 该行包含了请求资源的状况。
2,响应头
3,空行
4,响应体。
width=可视区域的宽度
inital-scale=页面首次显示可视区域的缩放级别
user-scalable=no 用户不可以手动缩放
前端用html5的input的type:file。FormData二进制流传输。
服务器获取后可以写到本地文件中。
移动端当弹窗点击关闭时可能触发底层元素的click事件
原因:当一个用户在点击屏幕的时候,系统会触发touch事件和click事件,touch事件优先处理,touch事件经过 捕获,处理, 冒泡 一系列流程处理完成后, 才回去触发click事件。 而移动端的click事件会延时300ms执行,所以触发底层元素。
解决:
首先,浏览器发送一个请求(request)给web服务器,支持一个压缩格式如(gzip),服务端会将原来的源码压缩之后,通过http响应(response)信息返回给web浏览器,浏览器接收之后,显示出来。
LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。
HTTP主要控制缓存的字段有
ETAG在HTTP协议中的定义是资源实体的标记(entity tag),强标识一个资源。是缓存过期的一种代替方案
客户端首次访问资源,服务器返回资源实体内容和在头区中返回ETAG值,客户端保存实体内容和ETAG值。
客户端再次访问资源的时候,在头域(header)中加入“If-match:etag值”指令。
服务器接受到请求后,检查资源的ETAG值是否与请求的If-match指定的etag值相同(强匹配),如果匹配则响应304 Not Modified。
ETag比Last-Modified和 Expires 的优势
Last-Modified和Expires都是时间作为判断资源是否改变的标志存在一些隐患。
HTTP缓存分为强制缓存和协商缓存
强制缓存内容放在memory cache
回流: 当渲染的一部分元素更改自己的宽高等,导致重新构建布局,就产生了回流。
重绘: 当一个元素自身的高度没改变,只改变了背景颜色的,发生重绘。
回流必定重绘,当重绘不一定回流。
减少reflow/repaint的措施:
域名收敛: 就是将静态资源放在一个域名下。减少DNS解析的开销。
域名发散:是将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。
域名发散是pc端为了利用浏览器的多线程并行下载能力。而域名收敛多用与移动端,提高性能,因为dns解析是是从后向前迭代解析,如果域名过多性能会下降,增加DNS的解析开销。
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
可能会给中间人攻击,解决方法可以用第三方安全证书来认证。
可以优化算法,比如RSA消耗很多时间,现在都在用ECC椭圆曲线加密了。
证书优化。
正常优化http都可以
双向认证:
一个是服务端证书,另一个或多个是客户端证书。
在证书验证成功的情况下即可完成请求响应。
双向认证一般企业应用对接。
单向认证:
客户端保存着服务端的证书并信任该证书即可。
https一般是单向认证,这样可以让绝大部分人都可以访问你的站点。
阿里云服务器就有。
HTTPDNS使用HTTP协议进行域名解析,代替现有基于UDP的DNS协议,域名解析请求直接发送到阿里云的HTTPDNS服务器,从而绕过运营商的Local DNS,能够避免Local DNS造成的域名劫持问题和调度不精准问题。
JsonP跨域可能造成的安全危害:
解决方法:利用了
XSS是指恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web页面中去。使别的用户访问都会执行相应的嵌入代码。源于过于信任客户端提交的数据,对提交的数据进行过滤,去掉script等关键字,或者拼接html时注意规则。
1、持续型XSS攻击:恶意脚本来源于网站的数据库。
攻击者通过评论表单提交将script>alert(‘aaa’) 网站后端对提交的评论数据不做任何操作,直接存储到数据库中。
其他用户访问正常访问网站,并且需要请求网站的评论数据。
网站后端会从数据库中取出数据,直接返回给用户
用户得到页面后,直接运行攻击者提交的代码,所有用户都会在网页中弹出aaa的弹窗
2,反射型XSS攻击:恶意脚本来源于受害者的请求url。
在一个反射型XSS攻击中,恶意文本属于受害者发送给网站的请求中的一部分。随后网站又把恶意文本包含进用于响应用户的返回页面中,发还给用户。
比如url中的参数带着script标签可能就执行
过滤可以采用字符转义
攻击盗用你的身份,发送恶意请求。
登陆可信网站A,在本地生成cookie
在不登出A的情况下,访问危险网站B
这时恶意站点 B的某个页面向站点A服务器发起请求,而这个请求会带上浏览器端所保存的站点A的cookie;因为同源政策所以会自动加上所请求的cookie
B可以有img标签去请求A的服务器
防御方法:
(1) 加入验证码
每次要提交数据时都要提交一组随机生成的验证码。
(2) token验证
1,用户验证成功登陆后,服务端会签发一个 Token,再把这个 Token 发送给客户端
2,客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
3,客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
4,服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
原理利用程序对用户输入的检查不足,轻则可以获取敏感信息,重则可以控制服务器。
最常见的是用户名和密码,在传给服务器时,它会拼接到sql语句中,并存入服务器。假如我们在密码中加入一个#号,#号在mysql中是注释的意思,这样后面的语句就不会执行了。
重定向中间人攻击
比如http百度,会重定向到https,但是中间人攻击数据劫持。
中间返回一个恶意的报文。
预防方法使用HSTS的方法,配合307,它会强制使用https
文件上传安全
文件上传用户上传的可执行脚本文件,获得执行服务端的功能。
解决:上传时就过滤文件。
cookie的字段:
name字段 :一个cookie的名称。
value字段 :一个cookie的值。
domain字段 :可以访问此cookie的域名
path字段:可以访问此cookie的页面路径。
expires:过期时间
Session分布式集群:
多台服务器的时候就会遇到Session共享的问题。
Session Sticky:
让负载均衡器能够根据每次的请求的会话标识来进行请求的转发,这样就能保证每次都能落到同一台服务器上面,这种方式称为Session Sticky方式。
原生ajax请求带cookie,设置
当我们手动在 ajax 请求中把 cookie 添加到头信息中的时候
xhr.withCredentials = true; //支持跨域发送cookies
然后呢,我们需要在我们的 Server 返回的时候加上一条头信息
response.setHeader("Access-Control-Allow-Credentials", true);
为什么用cookie存用户信息,而不用localStorage。
navigator.cookieEnabled判断支不支持cookie
cookie的编码,我认为应该是使用url编码吧,因为要编码分号,逗号和空格。
encodeURIComponent(URL)可把字符串作为 URI 组件进行编码。
decodeURIComponent(URL)函数可把字符串作为 URI 组件进行解码。
uel编码不可以用中文。
cookie使用中文
可以转为utf-8形式的字符串编码,读时再解码回来。
URLEncoder.encode(“虎嗅”,“utf-8”);
URLDecoder.decode(cookie.getValue(),“utf-8”);
1.前台编码
encodeURIComponent(str)
2.后台解码
原因是有关中文编码的问题,中文采用的是unicode编码,而英文采用的是ASCII编码
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";
defer和async都是在html解析(parase)的时候下载js文件,不同的是,async下载完后,会立即执行,而defer会等待html解析完成完后才执行。如果该脚本依赖于另一个脚本或被另一个脚本依赖,则使用defer。
1xx:指示信息–表示请求已接收.
2xx:成功-请求已接受并返回响应头
200:请求已成功,返回响应头。
3xx:重定向–要完成请求必须进行更进一步的操作。
301:请求的资源已被永久移到新位置。
302:临时重定向
304:缓存的内容并未改变。
4xx:客户端错误–请求有语法错误或请求无法实现。
400:请求无效,前端提交数据的字段名称或者是字段类型和后台的实体类不一致。
401:请求要身份验证。
404:找不到页面。
5xx:服务器端错误–服务器未能实现合法的请求。
500:服务器遇到未知状况,无法处理。
503:服务器过载或维护。
//301例如:http百度网页永久定位到https百度上
//302例如:用户中心的页面前端没认证,则跳到登陆页面,临时重定向。
样式兼容性(css),交互兼容性(javascript),浏览器 hack 三个方面。
样式兼容性(css)方面
交互兼容性(javascript)
浏览器 CSS hack
由于不同的浏览器对CSS的解析认识不完全一样,因此会导致生成的页面效果不一样。
我们就需要针对不同的浏览器去写不同的CSS,让它能够同时兼容不同的浏览器。
option测试服务器支持的请求类型。
cors跨域时对非简单请求时,发送一个option测一测是否允许跨域。
首先明白:物理像素:移动设备出厂时,不同设备自带的不同像素,也称硬件像素;
逻辑像素: 即css中记录的像素。
window.devicePixelRatio是代表物理像素与css逻辑像素的比值。
iPhone的 devicePixelRatio==2,所以你设置1px的边框,实际显示物理像素2px显示,在iphone中会比较粗。
解决方法:
用docment.getElementByTagName()获取标签名的元素集合,可以设置为*号,这样就可以获取全部。
javascript 有三部分构成,ECMAScript,DOM和BOM,根据宿主(浏览器)的不同,具体的表现形式也不尽相同,ie和其他的浏览器风格迥异。
BOM:是指浏览器对象模型,它使JavaScript可以和浏览器进行交互。包含:window。用来获取或设置浏览器的属性、行为,例如:新建窗口、获取屏幕分辨率、浏览器版本号等。
DOM是文档对象模型,用来获取或设置文档中标签的属性,例如获取或者设置input表单的value值。
js的数组:push(), pop(),unshift(),shift(),indexOf(),toString(),join(),concat(),reverse(),slice(),splice(),sort()
js的字符串: indexOf(),lastIndexOf(),slice(),split(),replace(),match()
基本数据类型存储在栈内存,引用数据类型存储在堆内存
NaN不是数字,和任何数都不相等。
typeof NaN == number。 //true
用isNaN()来判断
// 函数表达式(function expression)
var h = function() {
// h
}
// 函数声明(function declaration)
function h() {
// h
}
先说两者的显著区别:
第一种声明方式也就是var声明方式, 函数只有在var语句声明之后才能被调用
第二种生命方式也就是function声明方式, 函数可以在function声明之前被调用
第一种: for…in
1)、Object.keys(obj)
2)、Object.values(obj)
const obj = {
id:1,
name:'zhangsan',
age:18
}
console.log(Object.keys(obj))
console.log(Object.values(obj))
第三种:使用Object.getOwnPropertyNames(obj)
const obj = {
id:1,
name:'zhangsan',
age:18
}
Object.getOwnPropertyNames(obj).forEach(function(key){
console.log(key+ '---'+obj[key])
})
Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;
冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。
此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
console.log(navigator.appName) //浏览器名字
console.log(navigator.appVersion) //浏览器版本
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,如果还没有找到就会再的__proto__中查找,这样一层一层向上查找最后到object就会形成一个链式结构,我们称为原型链。
每一个构造函数都有一个prototype指针,他指向它的原型对象。
每一个原型对象都有一个constructor指针,它指向它的构造函数。
每一个对象都有一个__proto__指针,它指向它的原型对象。
原型对象的__proto__指针一般指向object,因为它是基础object的。
这样链式的连接集合就构成了原型链。
作用域链是保证对执行环境有权访问的所有变量和函数的有序访问。
变量对象:环境定义的所有变量和函数,对象的集合。
每一个函数都有一个scope属性。是所有父级变量对象的层级链。我是把它理解为父级的作用域链的。
比如一个全局函数,函数定义时,他的scope属性中只包含一个全局对象GO(global object)。
当执行这个函数时,它会创建唯一的执行上下文。 首先会创建一个它自己的活动对象【Activation Object】(即当前函数定义的变量和方法的集合)和当前执行环境下的作用域链,把这个函数的[scope]属性按顺序复制到作用域中,再在顶部加入这个执行环境的活动对象。这样就行成了作用域链。
函数执行完,执行环境就会销毁。
作用域链就是一个有序的栈,保证对执行环境有权访问的所有变量和函数的有序访问。
eval()参数是一个字符串,能像js一样执行语句。
with()是对原来的属速写,如果没有的话会声明为全局变量。
var obj = {
a: 1,
b: 2,
c: 3
};
with (obj) {
a = 3;
b = 4;
c = 5;
}
var cloneObj = function(arr) {
var obj = arr.constructor == Array ?[]:{};
for(var item in arr) {
if(typeof arr[item] === 'object') {
obj[item] = cloneObj(arr[item]);
}
else {
obj[item] = arr[item];
}
}
return obj;
}
用在连续点击div时,
是为了限制函数一段时间内只能执行一次,在延时的时间内,方法若被触发,则直接退出方法。
//节流
let throttle = (func, wait) => {
let timer;
return function() {
if(timer) {
return ;
}
var self = this;
var args = arguments;
timer = setTimeout(function() {
func.apply(self, args);
timer = null;
}, wait)
}
}
用在搜索输入时,
函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务未执行完,则 clear 掉定时任务,重新定时。
//防抖
let debounce = function(func,wait) {
let timer;
return function(){
let args = arguments;
var self = this;
clearTimeout(timer);
timer = setTimeout(function(){
func.apply(self,args);
}, wait);
}
}
Function.prototype.bind = function(obj){
var self = this;
var arg1 = Array.prototype.slice.call(arguments,1);
return function(){
self.apply(obj,arg1.concat(Array.prototype.slice.call(arguments)));
}
}
新生代垃圾回收:用于存储存活时间短的对象
由于新生代中的垃圾回收非常频繁,因此回收算法必须足够快。
新生代被划分为两个大小相等的子区:to区及from区。绝大多数对象的内存分配都发生在to区。当to区被填满后,交换to区和from区,现在所有对象都存储在from区,然后将活着的对象从from区拷贝到to区或者将它们晋升至老生代中,拷贝过后,释放from区的内存。
在新生代的垃圾回收周期中,始终有一半的内存时空的,由于新生代很小,因此浪费的空间并不大.
老生代垃圾回收:用于存储存活时间长的对象
大多数浏览器都是标记清除
当变量进入执行环境时,就标记为“进入环境”,当变量离开时标记“离开环境”。
垃圾回收器运行时,会在内存中标记所有变量,然后去除环境中的变量以及被环境中变量所引用的变量(闭包),之后标记的变量就是要清除的。
IE是用引用计数的方式清除
垃圾回收器是周期性运行的,按照固定的时间执行。
IE6是按照内存分配量运行的。
取消事件冒泡
w3c标准:
event.stopPropagation();
ie:
event.cancelBubble=true;
取消默认:
e.preventDefault();
ie:
window.event.returnValue = false;
即取消默认行为也不冒泡,就直接return false
blur、focus
监听窗口变化onorientationchange
DOM2事件模型
a. 事件捕获阶段
b. 事件处理阶段
c. 事件冒泡阶段
dom.addEventListener(‘click’,function(){},true)
第一个参数侦听的事件,第二个参数为触发事件执行的方法,第三个true捕获阶段执行,false冒泡阶段执行
IE事件流:叫做事件冒泡。从点击的元素向上传播。用ele.attachEvent(‘onclick’, function(){ }); 只支持冒泡阶段
DOM0级事件处理程序:
就是用onclick绑定事件
利用事件冒泡,子级触发冒泡,委托他们的父级代为处理事件。
常见的如ul中包含li
闭包指有权限访问另一个函数的变量的函数。
深入理解的话:
当函数执行时,会创建一个称为执行期上下文。
函数还会获得它所在作用域的作用域链,是存储函数能够访问的所有变量对象的集合,即这个函数中能够访问到的东西都是沿着作用域链向上查找直到全局作用域。
函数每次执行时对应的执行环境都是唯一的,当函数执行完毕,函数都会失去对这个作用域链的引用,JS的垃圾回收机制就回收。
但是,当闭包存在时,即内部函数保留了对外部变量的引用时,这个作用域链就不会被销毁,此时内部函数依旧可以访问其所在的外部函数的变量,这就是闭包。
优点:
1:防止函数执行完后,变量被销毁,使其保存在内存中。
2:通过闭包和立即执行函数来封装函数, 全局变量可能会造成命名冲突,使用闭包不用担心这个问题,因为它是私有化,加强了封装性,这样保护变量的安全。
缺点:
由于它是驻留在内存中,会增大内存使用量,使用不当很容易造成内存泄露。
内存泄漏:指一块被分配的内存既不使用,也不能回收
意外的全局变量引起的,在function中声明变量时未用var ,会变成全局变量不可以回收。
IE浏览器中引用计数的回收方式中,用循环引用,a对象中属性值为b,b对象中属性值为a。即a.pro=b;b.pro=a;
闭包所引起的内存泄漏。假设一个函数中要给一个元素绑定事件,改变当前元素的文字。使用到的dom除了绑定事件使用,剩下永远不会再使用,当闭包不可以删除内存。
function bindClick(){
var dom =document.getElementById('id');
dom.onclick = function() {
dom.innerText = '123';
}
}
主线程从“事件队列”中读取事件,这个过程是循环的,较事件循环。
worker是一个线程,通过构造函数Worker创建,参数是js文件路径;文件中的js文件会运行再主线程之外的worker线程。
worker线程不能访问window对象和dom对象,一般只用来计算数据。
通过PostMessage()发送消息,onMessage()监听事件接受消息。来进行交流
专用的 worker,只能被创建它的 JS 访问,创建它的页面关闭,它的生命周期就结束了。
共享的 worker,可以被同一域名下的 JS 访问,关联的页面都关闭时,它的生命周期就结束
ServiceWorker:ServiceWorker的主要能力集中在网络代理和离线缓存上。具体的实现上,可以理解为ServiceWorker是一个能在网页关闭时仍然运行的WebWorker。
说到Service worker不得不说一下PWA即渐进式WEB应用。就是让web网页更像原生手机app程序。
丰富的离线体验:通过Cache Api离线缓存,让用户能够完整地打开整个页面,比如页面的白屏时间过长,网络不稳定造成的加载中断导致页面不可用。 Https环境部署,可以把 App Icon 入口添加到桌面。
拦截网络请求,消息推送通知等功能。关于离线环境的支持我们就需要仰赖ServiceWorker。
同协议,域名,端口才能访问。
不同ip地址要跨域
CDN不同ip地址要跨域
为什么要同源:不同域下DOM任意操作,ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据。
要设置cookie:
cors简单请求:HEAD,GET,POST
简单请求:请求头只包含
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
创建一个新的空对象;
将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
执行构造函数中的代码(为这个新对象添加属性) ;
返回新对象。
var obj = new Base();
// 等价于
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
//随后通过Base.prototype.xxx = () => {}为Base添加方法时,obj也同时拥有此方法
(1)原型链继承:将父类实例作为子类的原型对象
缺点:所有子类的实例的原型都共享同一个父类实例的属性和方法。
function Parent () {
this.names = ['kevin', 'daisy'];
}
function Child () {
}
Child.prototype = new Parent();
var child1 = new Child();
child1.names.push('yayu');
console.log(child1.names); // ["kevin", "daisy", "yayu"]
var child2 = new Child();
console.log(child2.names); // ["kevin", "daisy", "yayu"]
(2)构造函数继承:在在函数中运行父级构造函数
缺点:父类函数没有共享,浪费内存。
无法继承原型链上的属性和方法
// 子类
function Sub(){
Super.call(this)
this.property = 'Sub Property'
}
(3)组合继承:原型继承和构造函数继承的组合。
缺点:父级构造函数被调用两次,子类实例也有两份,浪费内存
实际上子类上会拥有超类的两份属性,只是子类的属性覆盖了原型对象上的属性
// 子类
function Sub(){
Super.call(this)
this.property = 'Sub Property'
}
Sub.prototype = new Super()
// 注意这里new Super()生成的超类对象并没有constructor属性,故需添加上
Sub.prototype.constructor = Sub
(4)原型式继承:传入参数obj,生成一个继承obj对象的对象
缺点:不是类式继承,而是原型式基础,缺少了类的概念
function objectCreate(obj){
function F(){}
F.prototype = obj
return new F()
}
Object.create(obj)
(5)寄生式继承(和原型式继承差不多):原型式继承套个壳子,增加你要传入的参数。
缺点:依旧没有类的概念
function objectCreate(obj){
function F(){}
F.prototype = obj
return new F()
}
function createSubObj(superInstance){
var clone = objectCreate(superInstance)
clone.property = 'Sub Property'
return clone
}
(5)寄生组合式继承:在子构造函数中执行父级函数,并创建Object.create(父级的原型对象)复值给obj,再修改obj的constructor的指针指向子构造函数,最后obj作为子构造函数的原型对象。
function Sub(){
Super.call(this);
}
var proto = Object.create(Super.prototype);
proto.constructor = Sub;
Sub.prototype = proto ;
var obj = {
one:{
two:[1,2,3]
}
}
Object.assign(obj); //创建一个新对象,第一级式深拷贝,二级以下是浅拷贝
Object.create(obj); //创建一个新对象,新对象的__proto__指针指向obj
var arr = [1,2,3,4];
arr.splice(1,2); //截取从位置1开始后的两位返回[2,3],对原数组有影响
arr.slice(1,3); //返回从位置1到位置3-1的数组[2,3],对原数组无影响
var str = " 123 456";
str.split(' '); //会返回空格分开两边到数组上,[ '', '', '123', '456' ]
arr.join('+'); //返回数组用+连接起来
var x = JSON.stringify({abc:'123'});
console.log(x); //对象转化为json字符串{"abc":"123"}
var y = JSON.parse(x);
console.log(y); //将json字符串转化为对象{ abc: '123' }
JSON.stringify(xiaoming, ['name', 'skills'], ' ');
//第二个参数用于控制如何筛选对象的键值,如果我们只想输出指定的属性,可以传入Array:
this指向是包含它的函数作为方法被调用时所属的对象。
es6中箭头函数的this指针是定义函数时就绑定了的。就是this是继承自父执行上下文。
对象当转number时调用valueOf(),当转字符串时toString()
1,如果有一个操作数是布尔值,则比较相等之前将其转化为数值,true=1,false=0
2,如果有一个操作数是对象,调用valueOf方法,得到基本数据类型。
常见的比较:
console.log(null == undefined); //true
console.log([] == false); //true
console.log([] == ![]); //true
if([]) //true
使用"use strict"即可。
作用:
1,消除代码不安全之处
2,提高编译效率
(1)函数声明提前
(2)变量声明提前undefined
var reg = new RegExp('abc', 'g');
var reg2 = /abc/gim;
//i表示忽略大小写,g表示全局匹配,m表示多行匹配
//^为开头, $为结尾
reg.test('abcde'); //返回true
reg.exec('abc1abc2abc') //返回一个数组,一般循环调用
console.log('abc2abc'.match(reg)); //返回一个数组,匹配到的字符串[ 'abc', 'abc' ]
字母转换相关
stringObject.toLowerCase()//转小写
stringObject.toUpperCase()//转大写
var str1 = 'a';
str1.charCodeAt(); // 97
将数字化为千位一个逗号的样子
//金钱符号转换
function formatNumber(str) {
return str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
console.log(formatNumber("1234.5629")) // 1,234,567,890
var reg = /\B(?=(\d{3})+(?!\d))/g;
console.log('1234567'.replace(reg,','))
exec可以使reg对象的.lastIndex属性增加
腾讯前端团队alloyteam之前发布的手Q首屏渲染
第一,首屏数据拉取逻辑置于顶部,是为了数据能够第一时间返回,相比起将数据拉取逻辑放在外部资源会少了一个JS资源加载的往返时间。
第二,首屏渲染css及js逻辑优先内联HTML,这是为了当HTML文档返回时CSS和JS能够立即执行。
第三,次屏逻辑延后处理和执行,各种数据上报最好是延时上报,这样可以减少阻塞。
即时运行错误的捕获方式:
1、try…catch
2、window.onerror:只能捕获即时运行错误,不能捕获资源加载错误(原理:资源加载错误,并不会向上冒泡,object.onerror捕获后就会终止,所以window.onerror并不能捕获资源加载错误);
资源加载错误的捕获方式:
1、object.onerror:img标签、script标签都可以添加onerror事件,用来捕获资源加载错误;
看控制台报错
- 点我试试
其实就是target指向触发事件的元素
currentTarget指向的是绑定的标签元素
es5中在构造函数中var 一个变量。之后封装get和set函数来操作这个私有变量。
function Person(firstName,lastName){
var name=firstName+" "+lastName;
this.getFullName =function(){
return name;
}
es6中可以在constructor构造函数中var 变量,之后this.get方法来操作私有变量
class Example {
constructor() {
var _private = '';
_private = 'private';
this.getName = function() {return _private}
}
}
1、window.location.href(设置或获取整个 URL 为字符串)
var test = window.location.href;
alert(test);
返回:http://i.cnblogs.com/EditPosts.aspx?opt=1
2、window.location.protocol(设置或获取 URL 的协议部分)
var test = window.location.protocol;
alert(test);
返回:http:
3、window.location.host(设置或获取 URL 的主机部分)
var test = window.location.host;
alert(test);
返回:i.cnblogs.com
4、window.location.port(设置或获取与 URL 关联的端口号码)
var test = window.location.port;
alert(test);
返回:空字符(如果采用默认的80端口(update:即使添加了:80),那么返回值并不是默认的80而是空字符)
5、window.location.pathname(设置或获取与 URL 的路径部分(就是文件地址))
var test = window.location.pathname;
alert(test);
返回:/EditPosts.aspx
6、window.location.search(设置或获取 href 属性中跟在问号后面的部分)
var test = window.location.search;
alert(test);
返回:?opt=1
PS:获得查询(参数)部分,除了给动态语言赋值以外,我们同样可以给静态页面,并使用javascript来获得相信应的参数值。
7、window.location.hash(设置或获取 href 属性中在井号“#”后面的分段)
var test = window.location.hash;
alert(test);
返回:空字符(因为url中没有)
let:
1,不能重复声明一个变量
2,了他拥有块级作用域
3,let声明不提前
4,总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
const:
1,不能重复声明,要初始化
1,promise原理:
promise使用观察者模式,promise内部管理pending(等待),fulfilled(成功),rejected(失败)的状态改变,通过触发构造中的resolve()和reject()来改变状态,进而触发.then()中的回调函数,回调函数中返回promise对象,方便链式调用。
2,promise优点:异步编程,解决回调地狱。
3,promise缺点:promise一旦执行就不能停止
如果想停止1,不设置resolve()和reject()这样就一直是pending状态了
2,直接抛出错误catch接受
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
//全部成功才会执行回调函数
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
// Both resolve, but promise2 is faster
});
//返回先完成的那个
Promise.prototype.catch方法是.then(null,rejeaction)的别名,用于指定发生错误时的回调函数。
主要区别就是,如果在 then 的回调函数里抛出了异常,后面的 catch 能捕获到。
async function 声明用于定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。
返回一个promise对象。
async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待 Promise 的结果出来,然后恢复async函数的执行并返回解析值(resolved)。
注意:await只能在async中使用。
function f1() {
await f2();
}
则会回两次主线程,第一次是等待f2()返回promise,当promise返回后会执行并返回解析值(resolved)
这个时候执行resolved又要再等待,异步执行回调函数,这个时候又回主线程,
当主线程执行完就执行resolve触发的回调函数
以下代码就是经典await回两次主线程
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
var arr = [1,2,3];
arr.forEach(function(value, index) {
//要执行的操作
},this);
arr.filter(function(value,idnex){
return true;//要返回的过滤
},this);
arr.map(function(value,index){
return value;//要返回的数字
},this);
var b = arr.reduce(function(pre,current,index){
return pre + arr[index] + "+";
},'');
console.log(b); //1+2+3+
fetch 是全局量 window 的一个方法,它的主要特点有。
Promise.race()方法,它只会执行先完成的。
Promise.race([
fetch(URL),
new Promise(function(resolve,reject){
setTimeout(()=> reject(new Error('request timeout')),2000)
})])
.then((data)=>{
//请求成功
}).catch(()=>{
//请求失败
});
var xhr = new XMLHttpRequest();
xhr.open('post','url',true);
xhr.setRequestHeader("content-Type", "x-www-form-urlencoded");
xhr.onreadystatechange(function() {
if(xhr.readyState == 4&&xhr.status==200) {
//执行
}
})
xhr.send();
(1) link属于HTML标签,而@import是CSS提供的;
(2) 页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;
(3) import只在IE5以上才能识别,而link是HTML标签,无兼容问题;
(4) link方式的样式的权重 高于@import的权重.
从右往左解析
1,css画一个三角形
.triangle_border_up{
width:0;
height:0;
border-width: 30px; /*上右下左 上为0为上三角形*/
border-style:solid;
border-color:transparent transparent transparent #333;/*透明 透明 透明 灰*/
}
2,文本溢出时候省略号(css3)
text-overflow: ellipsis;
overflow: hidden;
white-space:nowrap;
多行文本溢出
overflow: hidden;
text-overflow:ellipsis;//文本溢出显示省略号
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
3,css五星好评思路
需要两张图片暗色的星星,亮色的星星。首先设置div宽度为5个星星宽度,用repeat-x平铺重复。
之后5个a标签分别占1/5的宽度,如果鼠标放上去,就触发伪类:hover,将left边定义到最左边,right定位到第i个位置,再background背景图片并repeat-x平铺i个亮的星星。
4,设置input中的两端对齐
span{
width: 100px;
text-align: justify;
float: left;
}
span:after{
content:'.';
width: 100%;
display: inline-block;
overflow: hidden;
height: 0;
}
text-align: justify;是多行两侧对齐。
span文本设置宽度和浮动和justify,之后设置伪类after且宽为100%,变为多行
伪类就是像hover
例如a标签一定要按顺寻link,visited,hover,active。
因为css伪类同级后面会覆盖前面。
比如把hover放在active后面,那么实际你在激活(active)链接的时候就触发了hover伪类,hover在后面覆盖了active的颜色,所以始终无法看到active的颜色
伏击设置after清除浮动
:after{display:block;clear:both;content:"";visibility:hidden;height:0}
visibility
text-align
line-height、color、font-family、font-size、font-style、font-weight、text-decoration、text-transform、direction。
querySelectorAll 返回的是一个 Static Node List即静态列表,而 getElementsBy 系列的返回的是一个 Live Node List即动态列表。
querySelectorAll 返回的是映射 改变其值不会改变document
当dom改变会动态nodelist会改变
而getElementsByClassName 改变它就会改变document
background-origin 属性规定 background-position 属性相对于什么位置来定位。
padding-box 背景图像相对于内边距框来定位。
border-box 背景图像相对于边框盒来定位。
content-box 背景图像相对于内容框来定位。
x% y% 使用百分比,左上角是0%0%,右下角是100%100%
这是因为有z-index的父盒子中的子盒子,其z-index值无论多大,都是相对父盒子而言的,所以只是决定它在这个父盒子区域的层级显示顺序而已~无法遮盖掉父盒子的兄弟盒子中z-index值较大的元素。
父级的z-index比兄弟盒子小的话,那子盒子中z-index设多大最后还是在父兄弟盒子的下面。
img标签的话,可以直接设置宽高,它会自动缩放,如果单设置一项的话,它只会按比例缩放。
如果是背景图片
width: 100%;
height: 200px;
background-size:100% 100%; //按照当前div的宽高进行自动缩放。
background-repeat: no-repeat;
background-image: url(); //背景图片
一张高清图片,怎么保证其在不同移动端设备上的显示效果?
可以用rem设置它的宽高
如果子div中有margin-top会外层div塌陷。
解决设置:父设置,overflow:hidden即可。
子margin为百分数,即以父级的宽度乘以百分比。
常用的块级元素:
address , center , div , dl , form , h1 , h2 , h3 , h4 , h5 , h6 , menu , ol , p , table , ul , li
常用内联的元素:
a , b , br , em , font , img , input , label , select , small , span , textarea
水平居中:
1,行内元素
text-align: center;
2,有宽度的块元素
margin: 0 auto;
垂直居中:
1,单行内容垂直居中:
line-height: height; //height父级高度
2,绝对定位
position: absolute;
top: 50%;
transform: translate(0, -50%);
3,flex布局
display: flex;
flex-direction: column;
justify-content: center;
width:100%;
height: 100%;
background-color: red;
position: absolute; /*设置个absolute就可以全屏了*/
visibility: hidden;
1、元素会被隐藏,但是不会消失,依然占据空间。
2、visibility: hidden会被子类继承,子类也可以通过显示的设置visibility: visible;来反隐藏。
3、visibility: hidden;不会触发该元素已经绑定的事件。
4、visibility: hidden;动态修改此属性会引起重绘。
display: none;
1、display: none;不占据空间(毕竟都不熏染啦),所以动态改变此属性时会引起重排。
2、display: none;不会被子类继承,但是···子类是不会显示的,毕竟都一起被kill啦。
3,不会触发绑定事件
opacity=0
1、opacity=0只是透明度为100%,元素隐藏,依然占据空间。
2、opacity=0会被子元素继承,且,子元素并不能通过opacity=1,进行反隐藏。不能。
3、opacity=0的元素依然能触发已经绑定的事件。
一个块格式化上下文(block formatting context) 是Web页面的可视化CSS渲染出的一部分。
BFC的创建方法
BFC特性
box-shadow
div
{
box-shadow: 10px 10px 5px #888888;//第一个水平阴影右为正
//第二个参数垂直阴影下为正,第三个是模糊程度,第四个是颜色
}
box-radius:50%//圆形
transform:变形,改变。
transition过度
div
{
width:100px;
height:100px;
background:blue;
transition:width 2s;
-moz-transition:width 2s; /* Firefox 4 */
-webkit-transition:width 2s; /* Safari and Chrome */
-o-transition:width 2s; /* Opera */
}
div:hover
{
width:300px;
}
//当鼠标放到div上宽度变为300
animation是指定keyframes的动画,具体是
.a {
animation:mymove 5s infinite;
-webkit-animation:mymove 5s infinite; /*Safari and Chrome*/
height: 100px;
background: red;
position: fixed;
width: 100px;
}
@keyframes mymove
{
from {left:0px;}
to {left:200px;}
}
//正方形向右平移的动画
尽量使用 transform 生成动画,避免使用 height,width,margin,padding 等。
使用 transform,浏览器只需要一次生成这个元素的位图,并在动画开始的时候将它提交给 GPU 去处理 。之后,浏览器不需要再做任何布局。transform 动画由GPU控制,支持硬件加速,并不需要软件方面的渲染。
尽可能少的使用box-shadows,它是性能杀手。
align-items:center;
//将子元素排成一行,并相对父级垂直居中
offsetWidth //返回元素的宽度(包括元素宽度、内边距和边框,不包括外边距)
clientWidth //返回元素的宽度(包括元素宽度、内边距,不包括边框和外边距),且要减去滑动块
scrollWidth //返回元素的宽度(包括元素宽度、内边距和溢出尺寸,不包括边框和外边距),无溢出的情况,与clientWidth相同
Vuex,vue-router,vue-cli,饿了么组件,
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 。
现在开发分解出很多组件进行开发,而 各个组件之间想必在逻辑上多少是有关系的。 那么组件之间的“通信”,就成了待解决问题。 在应用不断的扩展、变得越来越复杂。这当然不是我们想要的,我们希望应用的各个部分都易维护、可扩展。于是,状态管理模式冒了出来。
Vuex可以被看作项目中所有组件的数据中心,我们将所有组件中共享的State抽离出来,任何组件都可以访问和操作我们的数据中心.
Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
vueRouter原理:更新视图而不重新请求页面。
hash模式:
hash模式#后的字段对后端没有影响,不会加载。
原理:利用onhashchange事件来监听浏览器历历史记录记录。
history模式:
需要后台正确配置,如果url匹配不到资源,要返回一个index.html
原理:history模式利用了html5中的pushState()和replaceState() 方法。这两个方法应用于浏览器历史记录栈。
所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。
是在数据改变时计算的computed的值是一个缓存,只有它依赖中的数据发生改变,缓存才改变。
例子: return Date.now();它就永久不更新缓存,因为Date.now()不是vue中的响应式依赖。
销毁监听事件
vue采用数据劫持结合订阅发布者模式,来实现双向数据绑定。
通过Object.defineProperty()来劫持各个属性,当数据改变触发getter和setter,发布消息给订阅者。订阅者再施行相应操作更改视图。
Vue数据双向绑定是通过数据劫持结合订阅者-发布者模式模式方式来实现的.Object.defineProperty()给属性绑定getter和setter方法对数据进行劫持监听;然后创建一个订阅器来在getter里收集订阅者。watcher就是在自身实例化的时候触发getter往订阅器里添加自己,当数据改变时,就会触发setter
1,实现数据侦听听器observer,能对所有属性侦听。
2,实现指令解析器compiler,对每一个元素节点的指令进行解析,根据指令模板更新视图。
3,实现Watcher订阅者类,作为observer和compile桥梁,能够订阅并收到属性变动通知,发布者执行相应的回调函数更新视图。
4,Dep : 一个订阅者的列表类,依赖收集类,可以增加或删除订阅者,可以向订阅者发送消息
当指令解析时绑定监听者new一个Watcher,Watcher初始化将自己赋给Dep.target,再触发getter,将Dep.target加入Dep订阅者列表中,当数据变动时,setter就触发Dep的通知方法notify()通知订阅者,订阅者收到后,执行相应操作。
要给每一个监听的变量new Dep();通过闭包保存。
当执行object.defineProperty()时会执行一个new 一个dep的实类,当执行watcher时就会将自己赋给Dep.target这个全局属性,并触发getter,getter中将Dep.target加入dep的实例中,并为null空。当数据改变触发setter进而通知订阅者。
核心思想:通过创建一个拦截器来覆盖数组本身的原型对象Array.prototype。
首先用object.create()Array.prototype即数组原型对象作为参数去新建一个对象arrayMethods,
再遍历变异方法,比如push,pop,这些。并用Object.defineProperty给arrayMethods绑定这些变异方法。当调用这些方法时,首先用apply执行一下数组原型对象真正的push,pop方法。之后检查插入项,像push,shift这些。再监听插入项是不是数组,是的话再observeArray(insert)。之后触发.notify函数通知订阅者。返回arraymethods。
最后修改你要监听的数组的__proto__,即修改数组的原型对象为arraymethods,这样数组执行变异方法时就是执行arraymethods上的方法了。
Objct.defineProperty()无法监听数组
Proxy:
vue.nextTick:将延迟到下次Dom更新之后执行,dom更新是异步的。
可以用在created生命函数种,因为created时Dom还没编译。
因为vue中DOM更新是异步操作的,所以会写执行完执行栈中所有的同步任务即变量修改,Vue.nextTick()相当于在每次DOM更新后又手动添加了一个微任务,保证了该任务执行的时候DOM已经完全更新好了。
父向子传:
使用props
子向父:
父亲=组件加 @getMessage=“getVal”
子组件使用 $emit触发事件,父组件侦听事件
兄弟组件间传值:
先new Vue实例,在实例上用. e m i t ( ) 触 发 事 件 , 用 . emit()触发事件,用. emit()触发事件,用.on()侦听事件,并执行相应的函数。
vuex传值
ref=“myh3”
this.$refs.myh3
Vue.directive('demo', function (el, binding) {
el.innerText = '123'
})
this.$router.push({name:'Home',params:{userId: "1"}}); //传递参数
this.$router.params.userId;//获取参数数
用url传递参数
this.$router.query.userId;//获取参数
所有活动只有一个web页面,仅在初始化时加载HTML。更新视图而不重新请求页面。
因为是单页,在搜素引擎最佳化(seo)上的工作花功夫。
(1)真实Dom操作的代价高,dom虚拟只修改对象而已,所以操作代价小。
(2)Diff算法操作:
会对新旧两颗树进行深度遍历,每遍历一个节点就把该节点和新树对比,差异就记录到一个对象中,
最后将差异对象更新到视图上。
采取diff算法比较新旧节点的时候,比较只会在同层级进行, 不会跨层级比较。
当数据发生改变时,set方法会让调用 Dep.notify 通知所有订阅者Watcher,订阅者就会调用 patch 给真实的DOM打补丁,更新相应的视图。
patch函数接收两个参数 oldVnode 和 Vnode 分别代表新的节点和之前的旧节点。
首先sameVnode判断值不值得比较,如果不值得比较,将新节点直接把老节点整个替换了。
如果值得比较,会执行patchVnode函数进行节点比较:
节点比较有5种:
if (oldVnode === vnode),他们的引用一致,可以认为没有变化。
if(oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text),文本节点的比较,需要修改,则会调用Node.textContent = vnode.text。
if( oldCh && ch && oldCh !== ch ), 两个节点都有子节点,而且它们不一样,这样我们会调用updateChildren函数比较子节点,这是diff的核心,后边会讲到。
else if (ch),只有新的节点有子节点,调用createEle(vnode),vnode.el已经引用了老的dom节点,createEle函数会在老dom节点上添加子节点。
else if (oldCh),新节点没有子节点,老节点有子节点,直接删除老节点。
updateChildern函数:
oldVnode的childern有两个头尾指针,vnode的childern也要两个头尾指针。
然后两两比较,一共有4种比较方式。相等后指针往中间靠。
当其中两个能匹配上那么真实dom中的相应节点会移到Vnode相应的位置。
需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们。
当子组件模板只有一个没有属性的插槽时,父组件传入模板的整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。
1,
react一切都是javascript,xml和javascript的混合可以用jsx表达。
vue是html,css,js各自处理
2,
React:单项数据流,数据主要从父节点传递到子节点(通过props)。
Vue:双向数据绑定
3,
react会自顶向下整个diff一遍,而vue只会diff修改了数据绑定的组件
其实就是用到数据侦听,订阅发布者模式。
Node是基于事件驱动和无阻塞的,所以非常适合处理并发请求,
不适合CPU密集型应用,因为javascript单线程,导致CPU不能释放。
中间件就是一种功能的封装方式,就是封装在程序中处理http请求的功能,中间件是在管道中执行。
调用 next(),以将控制权传递给下一个中间件。如果要结束respone即可。
app.use()使用中间件
app.use(url,func)第一个参数是客户端请求的路径,第二个参数就是中间件
Node.js采用ChormeV8作为js的解析引擎,而I/O处理方面使用了自己设计的libuv,libuv是一个基于事件驱动的跨平台抽象层.
如果是浏览器的事件循环,单主线程的同步任务执行完,才会执行微任务中的事件比如promise,
等微任务队列全部执行完毕,才会去执行宏任务队列。
node.js的事件循环,有6个阶段,只有当切换阶段才会去执行微任务的事件,等微任务队列全部执行完,才会去执行宏任务。
事件循环分为 6 个阶段,它们会按照顺序反复运行。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段。
例子:
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
}, 0)
服务端渲染的目的是:性能优势。 在服务端生成对应的 HTML 字符串,客户端接收到对应的 HTML 字符串,能立即渲染 DOM ,最高效的首屏耗时。此外,由于服务端直接生成了对应的 HTML 字符串,对 SEO 也非常友好;
服务端渲染只是把当前的前端框架中的一部分js代码放到服务器上渲染,加载到浏览器上的html就不是一个空页面,这样可以减少首页的白屏时间,同时提高被搜索引擎检索的机会。
应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
应用层:为用户的应用程序如邮件提供网络服务。
表示层:确保一个系统的应用层发送的消息可以被另一个系统的应用层读取,管理数据加密,解密。
会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路,主要在你的系统之间发起会话或者接受会话请求
传输层:定义一些传输数据的协议和端口。进行流量控制等
网络层:传送ip分组报文
数据链路层:传输有Mac地址的帧
物理层:传输二进制
一.通用头部
1、Cache-Control
no-cache:如果是客户端的话,说明客户端不会接收缓存过的响应,要请求最新的内容。
max-age:该参数后方会被赋值上相应的秒数,缓存的秒数。
2,Request URL字段,Request Method请求方法get/post,Status Code:200状态码。
Content-type:请求类型。
3,Connection
Connection:keep-alive就是保持持久连接的意思。也可以理解为长连接,建立一次TCP连接,再复用发送。Connection:close就是关闭
二,请求头
1,Accept
请求报文可通过一个“Accept”报文头属性告诉服务端 客户端接受什么类型的响应。
2,Cookie 客户端的Cookie就是通过这个报文头属性传给服务端的哦
3,If-None-Match:一般是带上etag,然后与服务端比较。
4,Referer:后面带一个url 就是发起请求的url地址当前页面url
5,Origin 标识跨域资源请求
三,响应头:
1,etag:是服务器当前请求的服务器资源(图片,HTML页面等)所对应的一个的字符串标识。
2,Location:Location字段一般与重定向结合着使用。
3,Set-Cookie:响应报文中会使用到该字段。
TCP:面向连接,可靠传输,用于传输大量数据(流模式)。
UDP:非面向连接,不可靠传输,用于传输少量数据,速度快(数据报模式)。
TCP:(HTTP,FTP)一般用于文件传输。
UDP:(DHCP自举协议,用于获取IP地址)一般用于即使通信,qq聊天,对丢包率要求较低。
UDP若想完成可靠传输。传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
实现确认机制、重传机制、窗口确认机制。
RUDP 提供一组数据服务质量增强机制。
确认机制、重传机制、滑动窗口。
短连接:所谓短连接,及连接只保持在数据传输过程,请求发起,连接建立,数据返回,连接关闭。它适用于一些实时数据请求。
长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
实例:请求频繁的场景(直播,流媒体)。
短轮询:短轮询指的是在循环周期内,不断发起请求,每一次请求都立即返回结果,根据新旧数据对比决定是否使用这个结果。
长轮询: 客户端向服务器发送Ajax请求,服务器接到请求后将请求挂起,直到有新消息才返回响应信息并关闭连接(或到了设定的超时时间关闭连接),客户端处理完响应信息后再向服务器发送新的请求。
实例:WebQQ、Hi网页版、Facebook IM。
Websocket:WebSocket是Html5定义的一个新协议,与传统的http协议不同,该协议可以实现服务器与客户端之间全双工通信。简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。
websocket借用了HTTP的握手,请求头加了。
Upgrade: websocket
Connection: Upgrade
这就是websocket的核心,告诉服务器这是websocket请求,而不是http请求。
之后就可以进行全双工通信了。
socket:也叫嵌套字 ,是一组实现TCP/UDP通信的接口API,也就是说无论TCP还是UDP,通过对scoket的编程,都可以实现TCP/UCP通信。
作为一个通信链的句柄,它包含网络通信必备的5种信息:
连接使用的协议
本地主机的IP地址
本地进程的协议端口
远地主机的IP地址
远地进程的协议端口
可见,socket包含了通信本方和对方的ip和端口以及连接使用的协议(TCP/UDP)。通信双方中的一方(暂称:客户端)通过scoket(嵌套字)对另一方(暂称:服务端)发起连接请求,服务端在网络上监听请求,当收到客户端发来的请求之后,根据socket里携带的信息,定位到客户端,就相应请求,把socket描述发给客户端,双方确认之后连接就建立了。
TCP报文头20字节
UDP报文头8字节
慢开始的思路:当主机刚开始发送数据时,由于并不清楚网络的负荷情况,如果立即发送大量数据可能引起网络拥塞,所以从小到大逐渐发,一般是2的指数倍增大。
当到达慢开始门限时,它会执行拥塞避免算法,使拥塞窗口cwnd的值缓慢增大,其实就是”加法增大“。
当拥塞窗口值到达极限,网络就出现超时,重新进行慢开始,而且慢开始门限变为拥塞窗口极限值的一半。
快重传:可以尽早知道发生了个别报文段丢失。比如发送方接收到了3个确认M2的重复确认报文,则立即重发M2报文。
快恢复:当知道只是丢失个别报文段时,执行快重传,门限 = 拥塞控制值/2,并开始拥塞控制算法。
当滑动窗口为1时,发送方只有接收到ack确认报文才能发下一个序号。
首先,发送方有发送窗口,接收方有接收窗口,发送窗口内的序号号表示可以直接发送的序号。
当接收窗口的前n个序号都收到时,就会发送ack确认报文给发送方,同时接收窗口向右移n位。
发送窗口只有收到发送窗口内字节的ACK确认,才会向右移动发送窗口的左边界。
流量控制:是让发送方的速率不要太快,要让接收方来的及接收。
接收方会告诉发送方它的接收窗口是多少。
客户端最后一次发送ack确认报文会进入time wait状态。如果在2MSL时间内没收到重发的FIN报文就会结束。
当进入time wait,你发的ack报文可能丢失,这样服务端就会重发FIN断开连接报文,所以客户端要维持这条连接。重发ACK报文后2MSL时间内不收到FIN报文就会断开。
一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
确保数据能够完成传输。
但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
close()发起主动关闭的一方,在发送最后一个ACK之后会进入time_wait的状态,也就说该发送方会保持2MSL时间之后才会回到初始状态。 在这段时间内若ack报文没到,服务端会再发送FIN报文,客户端若收到就重新发确认,否则时间到断开连接。
可以看出当失败时服务器并不会重传ack报文,而是直接发送RTS报文段,进入CLOSED状态。这样做的目的是为了防止SYN洪泛攻击。
以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的.
因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节.
而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的.
又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节.
这个1472字节就是我们可以使用的字节数。
当我们发送的UDP数据大于1472的时候会怎样呢?
这也就是说IP数据报大于1500字节,大于MTU.这个时候发送方IP层就需要分片(fragmentation).
把数据报分成若干片,使每一片都小于MTU.而接收方IP层则需要进行数据报的重组.
这样就会多做许多事情,而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方便
无法重组数据报.将导致丢弃整个UDP数据报。
交换机发生在网络的第二层数据链路层,而路由器发生在第三层网络层。
路由器可以根据IP地址寻找下一个设备,可以处理TCPIP协议,而交换机是根据MAC地址寻址的。
记住口诀:
不稳定排序:“快选堆希”
首先利用二叉树的性质,按序存储到数组中,如果要从小到大排序,会先形成最大堆,再将最大的数字与倒数第一个数互换,接着再忽略最后一个元素进行最大堆,再将最大的元素放到倒数第二的位置,其实就是选择排序,每次选最大的并放到相应位置。
快速排序时间复杂度:平均O(nlogn),
最坏情况:O(n的平方),如果像冒泡的话就会O(n的平方)如果递归n次加上比较基准元素要O(n)的时间复杂度。
空间复杂度:主要在递归的层数,一般O(logn),最坏为O(n)
快排除了递归实现,还可以用栈来实现非递归实现排序,先推start和end,进去后调用partition函数返回位置p,将p+1,到end入栈,start到p-1入栈
依稀记得是插排和快排,数组长度小于等于 22 的用插入排序 InsertionSort,比22大的数组则使用快速排序
哈希表(Hash Table)是一种根据关键字直接访问内存存储位置的数据结构。
1,除留余数法
哈希表允许的地址数为m,去一个小于或等于m的质数作为除数。
则关键码转化为散列地址为hash(key) = key%p;
处理冲突的闭散列方法:1.线性探查法
当遇到同一个散列地址时,往下一个散列位置移。
它的左子树小于根节点,右子树大于根节点。
中序遍历会从小到大输出。
平衡二叉搜索树左右两个子树的高度之差不能大于1,
红黑树,非红即黑,根节点为黑。红黑树从根节点到叶子,没有一条路径会比其它路径长出两倍。
如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL树,
如果搜索,插入删除次数几乎差不多,应选择红黑树。
进程:进程是系统进行资源分配和管理资源的基本单位。指计算机运行的程序。
线程:是进程的一个执行单元,是进程中执行运算调度的最小单位。
1,分页存储
分成多张页面,建立页表,映射到物理空间
2,分段存储
有段表,段表有起始地址,和占用空间大小。
3,段页式存储
进程上下文就是表示进程信息的一系列东西,包括各种变量、寄存器以及进程的运行的环境。这样,当进程被切换后,下次再切换回来继续执行,能够知道原来的状态。
不同浏览器通信负载均衡时用socket
就绪:进程已处于准备好运行的状态,即进程已分配到除CPU外的所有必要资源后,只要再获得CPU,便可立即执行。
执行:进程已经获得CPU,程序正在执行状态。
阻塞:正在执行的进程由于发生某事件(如I/O请求、申请缓冲区失败等)暂时无法继续执行的状态。
1 当用户请求资源时将所有资源都请求完。
2.有序资源分配法
资源按某种规则统一编号,申请时必须按顺寻申请。
比如B进程申请2,1则改为申请顺序为1,2.
核心态:核心态是操作系统内核的运行状态,在这种状态下,处理机具有较高的特权,能执行一切指令,可以访问所有的寄存器和存储区。
用户态(User Mode):运行用户程序
用户态具有较低特权的执行状态,在这种状态下,处理机只能执行规定的指令,访问指定的寄存器和存储区,用户程序通常只能在这一级别执行。
MySQL的索引类型:
比如1w个电话号码数据,查找一个电话号码,它要遍历1w个数据,如果建立索引,就能快速查找到了。
我认为索引应该是就像hashmap一样,一个值对应着它的存储地址。
在程序装入时,可以将程序的一部分装入内存,而将其余部分留在外存,就可以启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换出到外存上,从而腾出空间存放将要调入内存的信息。这样,系统好像为用户提供了一个比实际内存大得多的存储器,称为虚拟存储器。
MVC即Model-View-Controller模型-视图-控制器。
MVC优缺点:
优点:实现了功能模块和显示模块的分离,同时它还提高了应用系统的可维护性、可扩展性。
缺点:不适合小型项目,增加系统结构和实现的复杂性
MVVM :即Model-view-viewmodel模型-视图-视图模型
视图(view)和模型(model)通过视图模型(viewmodel)交互。所以视图改变,数据源改变。数据源改变,视图改变。
webpack是一个对资源模块化和打包的工具。资源文件包括js,css,图片等。
loader 它就是一个转化器,将文件A转化为B。
对于plugin,它就是一个扩展器,也就是一个插件。
在webpack.config.js文件下配置打包
module.exports = {
entry:_dirname + '/app/main.js', //入口文件
output:{
path:_dirname + "/public",//打包后输出路径
filename: "bundle.js" //文件名
},
module:{
rules:[
{
test: /(\jsx|\js)$/, //所要处理的文件
use: {loader: "babel-loader"}, //要使用loader
exclude:/node-moduled/ //不要处理的文件
}
]
},
plugins:{
//插件
}
}
webpack打包产物是将css和js等文件打包成一个bundle.js的文件。
使用babel或css-loader就可以将es6转换为es5,和打包css了。
现在的前端网页功能丰富,JavaScript的复杂度增加和需要一大堆依赖包,所以打包后前端页面后,减少了页面的请求。
解析:将代码字符串解析成抽象语法树。
变换:将抽象语法树。
根据变换后的抽象语法树再生成代码字符串
主要是封装一个类型的功能,方便其他文件调用。
commonJs和es6模块的区别
requireJs:是一种在线“编译”模块的方案,相当于在浏览器页面上加载一个CommonJS/AMD模块格式解释器。这样浏览器就认识了define, exports,module这些东西,也就实现了模块化。
define([‘jquery’], function(jquery){})
webpack:是一个预编译模块打包的方案,你在本地直接写JS,不管是AMD/CMD/ES6风格的模块化,它都能认识,并且编译成浏览器认识的JS。webpack还可以加载图片
开发–》测试—》部署
单例模式分为饿汉式和懒汉式。
饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。
懒汉式单例模式:在类加载时不初始化。
饿汉模式:
优点:只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。
缺点:这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
懒汉模式:
即用到时,再去创建实例,当多个线程时可能不安全。
/**
*饿汉式单例模式(线程安全)
**/
public class Singleton{
private static Singleton instance = new Singleton();
//私有构造方法
private Singleton(){}
//静态方法为调用者提供单利对象
public static Singleton getInstance(){
return instance;
}
}
/**
* 懒汉式(线程不安全)
*/
public class Singleton
{
private static Singleton instance;
private Singleton(){}
private static Singleton getInstnce()
{
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
js的工厂模式是用来创建对象的,通常一个函数传入参数,函数中new一个Object对象,并将参数加入到新对象中,最后返回这个对象。
java
简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
抽象类,具体实现类,工厂类,客户端。
普通工厂模式:
首先定义工厂接口:之后实现工厂类。在客户端加多了代码。普通工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行,调用工厂类的create去创造产品。
面向对象编程,这种编程是把问题看作由对象的属性与对象所进行的行为组成。基于对象的概念,以类作为对象的模板,以对象为中心,来思考并解决问题。
封装性:含有私有数据和公有数据,封装性能使数据更加安全依赖的就是类的特性。
继承性:子类继承父类的共有数据和函数等
多态性:不同对象执行同意函数会形成多种行为。
函数式编程,顾名思义,这种编程是以函数思维做为核心,在这种思维的角度去思考问题。这种编程最重要的基础是λ演算,接受函数当作输入和输出。
函数是一等公民:函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数。
数据不可变:因为所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
闭包和惰性求值:惰性求值需要时再求值
函数柯里化:
简单的说就是柯里化是把一个多参数函数转换为一个嵌套的一元函数的过程
const addCurried = function(x){
return function(y){
return x + y;
}
}
// 使用方法
addCurried(4)(4)
// 8
能够进行延迟计算,就像add(1)(2)一样,1比2先传入,2就会被延迟计算,在特定的场景里,有一定的应用意义。
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
重载就是在一个类里面,方法名字相同,而参数不同。
重写就是子类重写父类的方法
Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。汉字可能要2-3个字节。
UTF-8就是使用变长字节表示,顾名思义,就是使用的字节数可变,这个变化是根据 Unicode 编号的大小有关,编号小的使用的字节就少,编号大的使用的字节就多。
同步异步:浏览器首先发送第一个请求,等待服务器回复后,再发送第二个请求,依次类推,直到所有请求完成。浏览器发送第一个请求,可以不用等待服务器返回,可以继续发送第二个请求。
并发并行:并发是两个cpu做两件事,并行就是一个cpu做两件事,吃饭电话来了,停止吃饭去接电话。其实只是各执行一段cpu时间。
阻塞非阻塞:例如进行需要read数据,阻塞方式操作流程是:如果没有数据,则read会一直等着数据到来,才能进行后续的动作;而非阻塞则是read没有到数据后,则可以进行后续的动作,当有数据的时候再回来读取。
不同点: express出现的早,koa是express原班人马打造的更小便捷的框架。
Koa 摒弃了‘被人诟病’的回调,采用 generator 的方式。Koa支持es6语法
它使用脏检查。Angular,当 watcher 越来越多时会变得越来越慢,因为作用域内的每一次变化,所有 watcher 都要重新计算。
单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,状态变化通过手动调用通知,源头易追溯,没有“暗箱操作”。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性。
缺点就是由于都是“暗箱操作”,我们无法追踪局部状态的变化(虽然大部分情况下我们并不关心),潜在的行为太多也增加了出错时 debug 的难度。同时由于组件数据变化来源入口变得可能不止一个.
可用jwt来生成,根据你的规则生成相应的token
seo优化其实就是让搜索引擎更快的找到你的网址
根据浏览器搜索规则,来写相应的关键词,利于搜索引擎搜索。
seo站内优化:301永久重定向,404页面,要求登录、强制使用cookies,域名好记短
seo外优化包含:论坛博客发外链
Prerender.io
git add:把要提交的所有修改放到暂存区(Stage)
git commit:一次性把暂存区的所有修改提交到分支
"name": "studylogin",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "zehongfan ",
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.18.0"
灰度测试,先在小范围测试
Typescript是微软推出的javascript的超集,
TypeScript 中的数据要求带有明确的类型,JavaScript不要求。
TypeScript 通过类型注解提供编译时的静态类型检查。代码运行前识别某些类型的问题。
web与Native两者掺杂,即Hybrid渲染。混合模式应用。
双线程通信方式
为什么要双线程 ? -> 为了管控安全,避免操作DOM。
小程序的渲染层和逻辑层分别由 2 个线程管理:渲染层的界面使用了 WebView 进行渲染,逻辑层采用 JsCore 线程运行 JS 脚本。
受控组件有两个特点:1. 设置value值,value由state控制,2. value值一般在onChange事件中通过setState进行修改
就是不受state的状态值改变而改变,只是具有一个类似于defaultValue这样的初始值来设置状态。
适配物理像素。
dpr是设备物理像素与逻辑像素的比值,告诉浏览器应该使用多少个屏幕的实际像素来绘制单个 CSS 像素。
当dpr为2时,设置viewport的scale为0.5,实现真正的1px逻辑像素代表1px物理像素。
它会动态设置scale
计稿对应的html的font-size。拿淘宝来说的,他们用的设计稿是750的,所以html的font-size就是75px,那1rem为75px,当屏幕宽度变化,他的html的font-size也变化。(屏幕宽度/750px)*75