赞
踩
<header></header> 头标记 <nav></nav> 导航标记,表示页面中导航链接部分 <!--main标记在一个网页中只能有一个,主要内容区域要区别--> <main></main> 主要内容标记 <section></section> 块标记,类似于div,可混合使用 <article></article> 文章标记,与上下文不想关的独立内容,一般作用于文章或报纸里的一篇文章 <!--表示文档主体内容中的一个独立单元,一个figure里只能有一个figcaption,类似于自定义列表dl--> <figure> 图文标记 <figcaption></figcaption> figure的标题 </figure> <aside></aside> 侧边栏标记 <footer></footer> 页脚标记 <address></address> 地址标记,文字会变成斜体 <canvas></canvas> 画布标记,必须配合js使用,属于内联块 <mark></mark> 高亮标记,属于内联标记 <time></time> 时间标记,定义日期和时间,属于内联标记 <video></video> 视频标记 <audio></audio> 音频标记,属于内联块 <embed></embed> 插件标记,视频和音频都可以插入
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1" />
目的 是为了在移动端不让用户缩放页面使用的
解释每个单词的含义
with=device-width 将布局视窗(layout viewport)的宽度设置为设备屏幕分辨率的宽度
initial-scale=1 页面初始缩放比例为屏幕分辨率的宽度
maximum-scale=1 指定用户能够放大的最大比例
minimum-scale=1 指定用户能够缩小的最大比例
首先:CSS 规范规定,每个元素都有 display 属性,确定该元素的类型,每个元素都有默认的 display 值,如 div 的 display 默认值为“block”,则为“块级”元素;span 默认 display 属性值为“inline”,是“行内”元素。
<div>、<p>、<h1>...<h6>、<ol>、<ul>、<dl>、<table>、<address>、<blockquote> 、<form>
<a>、<span>、<br>、<i>、<em>、<strong>、<label>、<q>、<var>、<cite>、<code>
<img>、<input>
<br/> <hr/> <img/> <input/> <link/> <meta/>
块格式化上下文, 特性:
方案一:
方案二:已知高度
方案三:未知高度
方案四:弹性布局
权重可以使用四位数表示:
浮动元素碰到包含它的边框或者浮动元素的边框停留。由于浮动元素不在文档流中,所以文档流的块框表现得就像浮动框不存在一样。浮动元素会漂浮在文档流的块框上。
浮动带来的问题:
清除浮动的方式:
.clear{
/*zoom在平时可以起到缩放效果,不过宽高不会有变化*/
zoom: 1; 兼容IE地板的浏览器的,可以触发IE浏览器的机制让其支持clear属性
}
.clear::after{
content: "";
display: block;
clear: both;
visibility: hidden; 为了防止content里有内容
height: 0; 可以不加,不过IE低版本下有问题,不写高的时候会默认有大概18px的高
}
1、利用了边框,块级元素在默认没有宽和高的情况下,设置四个边框,会以三角的形式呈现出来;
2、由于块级元素默认情况下是独占一行的,即使不设置宽度的情况下,默认也会跟随容器的宽度,所以
需要给width:0;
3、transparent 属性表示的是透明,无论背景颜色是什么样的,它都不会显示
例子:
div{
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 50px solid yellow;
border-bottom: 50px solid transparent;
}
display:none; 隐藏,理解为消失,没有位置。
visibility:hidden; 占位隐藏,元素消失,但位置还在。理解为隐身,且元素的功能不存在。
opacity:0; 隐藏,占位置的,实际上就是透明为0,让你眼睛看不见它了,但元素的功能存在。
溢出隐藏方式: overflow:hidden;
共同点:
1.改变行内元素的呈现方式,display 被置为 block;
2.让元素脱离普通流,不占据空间;
3.默认会覆盖到非定位元素上
不同点:
absolute 的”根元素“是可以设置的,而 fixed 的”根元素“固定为浏览器窗口。当你滚动网页,fixed 元素与浏览器窗口之间的距离是不变的。
canvas 标签的 width 和 height 是画布实际宽度和高度,绘制的图形都是在这个上面。而 style 的 width 和 height 是 canvas 在浏览器中被渲染的高度和宽度。如果 canvas 的 width 和 height 没指定或值不正确,就被设置成默认值。
因为浏览器的兼容问题,不同浏览器对有些标签的默认值是不同的,如果没对 CSS 初始化往往会出现浏览器之间的页面显示差异。
将一个页面涉及到的所有图片都包含到一张大图中去,然后利用 CSS 的 background-image,background- repeat,background-position 的组合进行背景定位。
利用 CSS Sprites 能很好地减少网页的 http 请求,从而大大的提高页面的性能;CSS Sprites 能减少图片的字节。
单冒号(:)用于 CSS3 伪类,双冒号(::)用于 CSS3 伪元素。
::before 就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于 dom 之中,只存在在页面之中。
:before 和 :after 这两个伪元素,是在 CSS2.1 里新出现的。起初,伪元素的前缀使用的是单冒号语法,但随着 Web 的进化,在 CSS3 的规范里,伪元素的语法被修改成使用双冒号,成为::before ::after
<div style="width: 100%;height: 1px;"></div>
<hr size="1">
JavaScript 有 简单数据类型:Undefined、Null、Boolean、Number、String、Bigint 和 复杂数据类型 Object、Function、Array ,以及es6语法新增的Symbol数据类型
判断方法有:typeof、instanceof、constructor、Object.prototype.toString
闭包是指有权访问另外一个函数作用域中的局部变量的函数。声明在一个函数中的函数,叫做闭包函数。而且内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
jsonp是一个能够跨域的方法,由于ajax受到同源策略的影响,不能进行跨域,而script标签中的src属性中的链接能够访问跨域的js脚本,利用这个特性返回一段调用某个函数的js代码,在src中进行了调用,从而实现跨域
优点:可以跨越同源策略、可以在老版本浏览器中运行、请求完毕后可以通过callback的方式回传结果
缺点:只支持get请求、只支持跨域http请求、安全性差
回调函数
事件监听
promise
ajax
async
setTimeout
Generator
Promise 是异步编程的一种解决方案:
从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变化。创造promise实例后,它会立即执行。
处理问题:
回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
promise可以支持多个并发的请求,获取并发请求中的数据
这个promise可以解决异步的问题,本身不能说promise是异步的
Promise是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:
resolve :异步操作执行成功后的回调函数
reject:异步操作执行失败后的回调函数
then:传递状态的方式来使得回调函数能够及时被调用
catch:指定reject的回调,或者在执行resolve时,如果抛出异常,并不会报错卡死js,而是会进到这个catch方法中
all:谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都会返回Promise对象。提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
race:谁跑的快,以谁为准执行回调
json优点:json数据更小、读取速度更快、更容易被解析(因为与js格式类似,可读性更好)
forEach():
map()
filter()
实现 原生AJAX
// 创建核心对象 const xhr = new XMLHttpRequest() // 建立连接 xhr.open(method, url, async) // 发送请求 // 如果为 post 需要在 send 前调用 setRequestHeader() 方法设置请求头 // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') xhr.send(params) // 处理响应 xhr.onreadystatechange = function() { // 当 readyState 改变时,执行该回调函数 // readyState 属性存有 XMLHttpRequest 的状态信息。 // 可以理解为请求到达哪个阶段了。 // 状态值可取 0-4 的值,4表示请求处理完毕,响应就绪 if(xhr.readyState === 4) { // status 表示的是 HTTP 状态码 // 200 表示 OK (成功) if(xhr.status === 200) { // 从响应中获取返回的数据 let data = xhr.responseText } } } // Promise 封装 ajax const myAjax = function(url) { return Promise((resolve, reject) => { const xhr = new XMLHttpRequest() xhr.open('GET', url) xhr.send(params) xhr.onreadystatechange = function() { if(xhr.readyState === 4) { if(xhr.status === 200){ let data = JSON.prase(xhr.responseText) resolve(data) } else { reject('error') } } } }) }
随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,this永远指向的是最后调用它的对象。
this指向的形式 (4种)
当 this 碰到 return
如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例。
注意:虽然null也是对象,但是在这里 this 指向那个函数的实例,因为null 比较特殊。
改变this指向的方式
1.变量提升只会提升变量名的声明,而不会提升变量的赋值初始化。
2.函数提升的优先级大于变量提升的优先级,即函数提升在变量提升之上。
例子:
var a = 10;
function a(){}
console.log(typeof a)
A."number"
B."object"
C."function"
D."undefined"
答案:A
代码等价于:
function a(){}
var a;
a = 10;
console.log(typeof a)
原型(prototype): 一个简单的对象,用于实现对象的 属性继承。可以简单的理解成对象的爹。在 Firefox 和 Chrome 中,每个 JavaScript 对象中都包含一个__proto__
(非标准)的属性指向它爹(该对象的原型),可 obj.__proto__
进行访问。
构造函数: 可以通过 new 来 新建一个对象 的函数。
实例: 通过构造函数和 new 创建出来的对象,便是实例。 实例通过__proto__
指向原型,通过 constructor 指向构造函数
三者的关系:
实例.__proto__
=== 原型
原型.constructor
=== 构造函数
构造函数.prototype
=== 原型
原型链是由原型对象组成,每个对象都有__proto__
属性,指向了创建该对象的构造函数的原型,__proto__
将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链。
当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的 __proto__
隐式原型上查找,即它的构造函数的prototype
,如果还没有找到就会再在构造函数的 prototype
的 __proto__
中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
属性查找机制: 当查找对象的属性时,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,找到时则输出,不存在时,则继续沿着原型链往上一级查找,直至最顶级的原型对象 Object.prototype
,如还是没找到,则输出undefined
;
属性修改机制: 只会修改实例对象本身的属性,如果不存在,则进行添加该属性,如果需要修改原型的属性时,则可以用: b.prototype.x = 2;
但是这样会造成所有继承于该对象的实例的属性发生改变。
浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,修改时原对象也会受到影响
深拷贝: 它在栈内存中仅仅存储了一个引用,而真正的数据存储在堆内存中。完全拷贝一个新对象,修改时原对象不再受到任何影响
1.新生成一个对象
2.链接到原型: obj.proto = Con.prototype
3.绑定 this: apply
4.返回新对象(如果构造函数有自己 retrun 时,则返回该值)
当你发现任何代码开始写第二遍时,就要开始考虑如何复用。一般有以下的方式:
函数封装
继承
复制 extend
混入 mixin
借用 apply/cal
模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。
分类:
1.babylon 将 ES6/ES7 代码解析成 AST
2.babel-traverse 对 AST 进行遍历转译,得到新的 AST
3.新 AST 通过 babel-generator 转换成 ES5
AST:抽象语法树 (Abstract Syntax Tree),是将代码逐字母解析成 树状对象 的形式。这是语言之间的转换、代码语法检查,代码风格检查,代码格式化,代码高亮,代码错误提示,代码自动补全等等的基础。
在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 预置通用参数,供多次重复调用。
const add = function add(x) {
return function (y) {
return x + y
}
}
const add1 = add(1)
add1(2) === 3
add1(20) === 21
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是 mouseout
mouseenter:当鼠标移入元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是 mouseleave
运行在客户端浏览器上;
不用预编译,直接解析执行代码;
是弱类型语言,较为灵活;
与操作系统无关,跨平台的语言;
脚本语言、解释性语言
跨域的原理:指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript 实施的安全限制,那么只要协议、域名、端口有任何一个不同,都被当作是不同的域。跨域原理,即是通过各种方式,避开浏览器的安全限制。
JSONP : 通 过 动 态 创 建 script , 再 请 求 一 个 带 参 网 址 实 现 跨 域 通 信 。
document.domain + iframe 跨域:两个页面都通过 js 强制设置 document.domain为基础主域,就实现了同域。
location.hash + iframe 跨域:a 欲与 b 跨域相互通信,通过中间页 c 来实现。三个页面,不同域之间利用 iframe 的 location.hash 传值,相同域之间直接 js访问来通信。
window.name + iframe 跨域:通过 iframe 的 src 属性由外域转向本地域,跨域数据即由 iframe 的window.name 从外域传递到本地域。
postMessage 跨域:可以跨域操作的 window 属性之一。
CORS:服务端设置 Access-Control-Allow-Origin 即可,前端无须设置,若要带cookie 请求,前后端都需要设置。
代理跨域:启一个代理服务器,实现数据的转发
法一:indexOf 循环去重
法二:ES6 Set 去重;Array.from(new Set(array))
法三:Object 键值对去重;把数组的值存成 Object 的 key 值,比如
Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。
在代码块内,使用 let、const 命令声明变量之前,该变量都是不可用的。在语法上,称为“暂时性死区”
webpack 是 一 个 现 代 JavaScript 应 用 程 序 的 静 态 模 块 打 包 器 (module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
优点:组件非常全面,样式效果也都比较不错。
缺点:框架自定义程度低,默认 UI 风格修改困难
// 第一个方法:用 let 块级作用域
for(let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i + 1)
}, 1000 * i)
}
// 第二个方法:闭包
for(var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function(){
console.log(i + 1)
}, 1000 * i)
})(i)
}
Symbol 是 ES6 的新增属性,代表用给定名称作为唯一标识,这种类型的值可以这样创建,let id = symbol(“id”)
Symbl 确保唯一,即使采用相同的名称,也会产生不同的值,我们创建一个字段,仅为知道对应 symbol 的人能访问,使用 symbol 很有用,symbol 并不是 100%隐藏,有内置方法 Object.getOwnPropertySymbols(obj)可以获得所有的 symbol。也有一个方法 Reflect.ownKeys(obj)返回对象所有的键,包括 symbol。所以并不是真正隐藏,但大多数库内置方法和语法结构遵循通用约定他们是隐藏的。
js变量存储有栈存储和堆存储,基本数据类型的变量存储在栈中,引⽤数据类型的变量存储在堆中,引⽤类型数据的地址也存在栈中
当访问基础类型变量时,直接从栈中取值。当访问引⽤类型变量时,先从栈中读取地址,在根据地址到堆中取出数据
Call 和 apply 的作用是一模一样的,只是传参的形式有区别而已
1、改变 this 的指向
2、借用别的对象的方法
3、调用函数,因为 apply,call 方法会使函数立即执行
除了正常模式运行外,ECMAscript 5 添加了第二种运行模式:“严格模式”。
作用:
全称:JavaScript Object Notation
JSON 中对象通过“{}”来标识,一个“{}”代表一个对象,如{“AreaId”:”123”},对象的值是键值对的形式(key:value)。JSON 是 JS 的一个严格的子集,一种轻量级的数据交换格式,类似于 xml。数据格式简单,易于读写,占用带宽小。
两个函数:
同步的概念在操作系统中:不同进程协同完成某项工作而先后次序调整(通过阻塞、唤醒等方式),同步强调的是顺序性,谁先谁后。异步不存在顺序性。
同步:浏览器访问服务器,用户看到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容之后进行下一步操作。
异步:浏览器访问服务器请求,用户正常操作,浏览器在后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容。
渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进,达到更好的用户体验。
优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容
先给出结论
CSS 不会阻塞 DOM 解析,但会阻塞 DOM 渲染。CSS 会阻塞 JS 执行,并不会阻塞 JS 文件下载
先讲一讲 CSSOM 作用:
第一个是提供给 JavaScript 操作样式表的能力;
第二个是为布局树的合成提供基础的样式信息。
这个 CSSOM 体现在 DOM 中就是 document.styleSheets。
由之前讲过的浏览器渲染流程我们可以看出:
DOM 和 CSSOM 通常是并行构建的,所以「CSS 加载不会阻塞 DOM 的解析」。然而由于 Render Tree 是依赖 DOM Tree 和 CSSOM Tree 的,所以它必须等到两者都加载完毕后,完成相应的构建,才开始渲染,因此,「CSS 加载会阻塞 DOM渲染」。
由于 JavaScript 是可操纵 DOM 和 css 样式 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器设置 「GUI 渲染线程与JavaScript 引擎为互斥」的关系。
有个需要注意的点就是:
「有时候 JS 需要等到 CSS 的下载,这是为什么呢?」
仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等 CSS 控制的属性,浏览器是需要计算的,也就是依赖于 CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行 JS。JS 文件下载和 CSS 文件下载是并行的,有时候 CSS 文件很大,所以 JS 需要等待。因此,样式表会在后面的 js 执行前先加载执行完毕,所以「css 会阻塞后面 js的执行」。
JS 阻塞 DOM 解析,也就会阻塞页面
这也是为什么说 JS 文件放在最下面的原因,那为什么会阻塞 DOM 解析呢
你可以这样子理解:
由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置 「GUI 渲染线程与JavaScript 引擎为互斥」的关系。
当 JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。
当浏览器在执行 JavaScript 程序的时候,GUI 渲染线程会被保存在一个队列中, 直到 JS 程序执行完成,才会接着执行。因此如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
另外 ,如 果 JavaScript 文件 中没 有操 作 DOM 相关 代码 ,就 可以将 该JavaScript 脚本设置为异步加载,通过 async 或 defer 来标记代码。
eval(arr.join(“+”))
同步:同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。
异步:异步任务是指不进入主线程,而进入任务队列的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程。
const withinErrorMargin = (left, right) => {
return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
console.log(withinErrorMargin(0.1 + 0.2, 0.3))
// 第二种方法
console.log(parseFloat((0.1 + 0.2).toFixed(10)) === 0.3)
作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现。
作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。
2xx (3种)
3xx (5种)
4xx (4种)
5xx (2种)
共同点:都是保存在浏览器端,并且是同源的
cookie 可以在浏览器和服务器间来回传递,存储容量小,只有大约4K左右。
作用:1、保存用户登录状态;2、跟踪用户行为。
localStorage ⻓期存储数据,浏览器关闭后数据不会丢失;
sessionStorage 数据在浏览器关闭后⾃动删除。
sessionStorage和localStorage优势:存储空间更⼤、有更多丰富易⽤的接⼝、各⾃独⽴的存储空间
分为 4 个步骤:
(1)浏览器根据请求的 URL 交给 DNS 域名解析,找到真实 IP,向服务器发起请求;
(2)浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。
(3)一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态表示一个正确的响应。
(4)此时,Web 服务器提供资源服务,客户端开始下载资源。请求返回后,载入解析到的资源文件,渲染页面,便进入了我们关注的前端模块
简述版:
当元素的样式发生变化时,浏览器需要触发更新,重新绘制元素。这个过程中,有两种类型的操作,即重绘与回流。
重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此 损耗较少
回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。会触发回流的操作:
回流必定触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。
最佳实践:
css:
javascript
阻止事件传播(冒泡): e.stopPropagation()
阻止默认行为: e.preventDefault()
虚拟 DOM 可提升性能, 无须整体重新渲染, 而是局部刷新.
JS 对象, diff 算法
事件捕获阶段: 从 dom 树节点往下找到目标节点, 不会触发函数
事件目标处理函数: 到达目标节点
事件冒泡: 最后从目标节点往顶层元素传递, 通常函数在此阶段执行. addEventListener 第三个参数默认false(冒泡阶段执行), true(捕获阶段执行).
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源
防止 XSS、CSFR 等攻击, 协议+域名+端口不同
jsonp; 跨域资源共享(CORS); (Access control); 服务器正向代理等
预检请求: 需预检的请求要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
TCP(Transmission Control Protocol:传输控制协议;面向连接,可靠传输
UDP(User Datagram Protocol):用户数据报协议;面向无连接,不可靠传输
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
一个程序至少一个进程,一个进程至少一个线程。
浏览器内核可以分成两部分: 渲染引擎(layout engineer 或者 Rendering Engine)和 JS 引擎。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
渲染引擎:
负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。
JS 引擎
解析 Javascript 语言,执行 javascript 语言来实现网页的动态效果。最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。
DOM Tree 和 CSS RULE Tree 将 html 和 css 解析成树形数据结构,然后 Dom 和 css 合并后生成Render Tree,用 layout 来确定节点的位置以及关系,通过 painting 按照规则来画到屏幕上,由 display 搭建最终看到效果。
Cross-site script,跨站脚本攻击,当其它用户浏览该网站时候,该段 HTML 代码会自动执行,从而达到攻击的目的,如盗取用户的 Cookie,破坏页面结构,重定向到其它网站等。
XSS 类型:
一般可以分为: 持久型 XSS 和非持久性 XSS
持久型 XSS: 就是对客户端攻击的脚本植入到服务器上,从而导致每个正常访问到的用户都会遭到这段 XSS 脚本的攻击。
非持久型 XSS: 是对一个页面的 URL 中的某个参数做文章,把精心构造好的恶意脚本包装在 URL 参数重,再将这个 URL 发布到网上,骗取用户访问,从而进行攻击。
CSRF(Cross-site request forgery), 中文名称:跨站请求伪造
CSRF 可以简单理解为:攻击者盗用了你的身份,以你的名义发送恶意请求,容易造成个人隐私泄露以及财产安全。
防范:
post 请求
使用 token
验证码
利用目标系统网络服务功能缺陷或者直接消耗其系统资源,使得该目标系统无法提供正常的服务。
DDoS 攻击通过大量合法的请求占用大量网络资源,以达到瘫痪网络的目的。
具体有几种形式:
URI 是统一资源标识符,相当于一个人身份证号码
Web 上可用的每种资源如 HTML 文档、图像、视频片段、程序等都是一个来 URI来定位的
URI 一般由三部组成
①访问资源的命名机制
②存放资源的主机名
③资源自身的名称,由路径表示,着重强调于资源。
URL 是统一资源定位符,相当于一个人的家庭住址
URL 是 Internet 上用来描述信息资源的字符串,主要用在各种 WWW 客户程序和服务器程序上,特别是著名的 Mosaic。采用 URL 可以用一种统一的格式来描述各种信息资源,包括文件、服务器的地址和目录等。
URL 一般由三部组成
①协议(或称为服务方式)
②存有该资源的主机 IP 地址(有时也包括端口号)
③主机资源的具体地址。如目录和文件名等。
SYN表示建立连接,ACK表示响应
建立连接前,客户端和服务端需要通过握手来确认对方:
客户端发送 syn(同步序列编号) 请求,进入 syn_send 状态,等待确认
服务端接收并确认 syn 包后发送 syn+ack 包,进入 syn_recv 状态
客户端接收 syn+ack 包后,发送 ack 包,双方进入 established 状态
SYN表示建立连接,FIN表示关闭连接,ACK表示响应
客户端 – FIN --> 服务端, FIN—WAIT
服务端 – ACK --> 客户端, CLOSE-WAIT
服务端 – ACK,FIN --> 客户端, LAST-ACK
客户端 – ACK
有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值。例如空格的编码值是” “。
如果不使用转义字符,这些编码就会当URL中定义的特殊字符处理。
答:数据驱动、组件系统
.prevent
: 提交事件不再重载页面;.stop
: 阻止单击事件冒泡;.self
: 当事件发生在该元素本身而不是子元素的时候会触发;.capture
: 事件侦听,事件发生的时候会调用答:当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key 的作用主要是为了高效的更新虚拟DOM。
父组件与子组件传值:
非父子组件间的数据传递,兄弟组件传值:
Vue.js 2.0 采用数据劫持(Proxy 模式)结合发布者-订阅者模式(PubSub 模式)的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
例如现在需要监控data中, obj.a 的变化。Vue中监控对象属性的变化你可以这样:
watch: {
'obj.a': {
handler (newName, oldName) {
console.log('obj.a changed')
}
}
}
另一种方法,利用计算属性(computed) 的特性来实现,当依赖改变时,便会重新计算一个新值。
computed: {
a1 () {
return this.obj.a
}
}
总结:虚拟dom可以很好的跟踪当前dom状态,因为他会根据当前数据生成一个描述当前dom结构的虚拟dom,然后数据发生变化时,又会生成一个新的虚拟dom,而这两个虚拟dom恰恰保存了变化前后的状态。然后通过diff算法,计算出两个前后两个虚拟dom之间的差异,得出一个更新的最优方法(哪些发生改变,就更新哪些)。可以很明显的提升渲染效率以及用户体验
作用在表单元素上 v-model = "message"
等同于 v-bind:value = "message" v-on:input = "message=event.target.value"
作用在组件上,本质是一个父子组件通信的语法糖,通过 prop 和 .emit 实现, 等同于:value = "message" @input = "$emit('input', $event.target.value)"
JS中的对象是引用类型的数据,当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。
而在Vue中,我们更多的是想要复用组件,那就需要每个组件都有自己的数据,这样组件之间才不会相互干扰。
所以组件的数据不能写成对象的形式,而是要写成函数的形式。数据以函数返回值的形式定义,这样当我们每次复用组件的时候,就会返回一个新的data,也就是说每个组件都有自己的私有数据空间,它们各自维护自己的数据,不会干扰其他组件的正常运行。
介绍:每一个vue实例从创建到销毁的过程,就是这个vue实例的生命周期。在这个过程中,他经历了从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程。
beforecreate (初始化界面前)
created (初始化界面后)
beforemount (渲染界面前)
mounted (渲染界面后)
beforeUpdate (更新数据前)
updated (更新数据后)
beforedestory (卸载组件前)
destroyed (卸载组件后)
< keep-alive >是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
< keep-alive > 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
生命周期钩子:actived:激活调用; deactived:停用时调用
属性:include:名称(正则 or 字符串)匹配时调用; exclude:名称不匹配时调用; max:最多缓存数量
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便实现组件数据之间的共享;
Vuex集中管理共享的数据,易于开发和后期维护;
能够高效的实现组件之间的数据共享,提高开发效率;
存储在 Vuex 的数据是响应式的,能够实时保持页面和数据的同步;
Vuex重要核心属性包括:state, mutations, action, getters, modules.
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
getters
类似vue的计算属性,主要用来过滤一些数据。
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。前端最流行的 ajax 请求库,react/vue 官方都推荐使用 axios 发 ajax 请求
特点:
常用语法:
1 动态路由传值。例如:path:“/home/:id/name”; 接受的时候通过 this.$route.params
2 query 传值。因为在 url 中 ?后面的参数不会被解析,因此我们可以通过 query 进行传值。接受的时候通过 this.$route.query
3 路由解耦。在配置路由的时候添加 props 属性为 true,在需要接受参数的组件页面通过 props
进行接受
4 编程式导航 this.$router.push({path:"/home",query:{}});
$route
和 $router
的区别$route
是 “路由信息对象”,包括 path,params,hash,query,fullPath, matched,name 等路由信息参数。
$router
是“路由实例”对象包括了路由的跳转方法,钩子函数等。
动态路由的创建,主要是使用 path 属性过程中,使用动态路径参数,以冒号开头,如下:
{
path: '/details/:id'
name: 'Details'
components: Details
}
访问 details 目录下的所有文件,如果 details/a,details/b 等,都会映射到 Details 组件上。当匹配到 /details 下的路由时,参数值会被设置到 this.$route.params
下,所以通过这个属性可以获取动态参数 this.$route.params.id
路由守卫为:
全局守卫:beforeEach
后置守卫:afterEach
全局解析守卫:beforeResolve
路由独享守卫:beforeEnter
bubbleSort = arr => {
for (let i = 0, l = arr.length; i < l - 1; i++) {
for (let j = 0; j < l - 1 - i; j++) {
if(arr[j] > arr[j+1]) {
let temp = arr[j+1]
arr[j+1] = arr[j]
arr[j] = temp
}
}
}
return arr
}
console.log('结果:', bubbleSort([5,3,2,4,8]))
selectSort = arr => {
for (let i = 0,l = arr.length; i < l - 1; i++) {
for (let j = i+1; j < l; j++) {
if(arr[i] > arr[j]) {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
}
}
return arr
}
console.log('结果:', selectSort([5,3,2,4]))
insertSort = arr => {
let l = arr.length
for(let i = 1; i < l; i++){
let temp = arr[i]
let j = i - 1
for(; j >= 0 && arr[j] > temp; j--) {
arr[j+1] = arr[j]
}
arr[j+1] = temp
}
return arr
}
console.log(insertSort([3,44,58,2,9,1]))
quickSort = arr => { let l = arr.length let leftArr = [] let rightArr = [] let a = arr[0] if( l <= 1) { return arr } for(let i = 1; i < l; i++) { if(arr[i] > a) { rightArr.push(arr[i]); } else{ leftArr.push(arr[i]); } } return [].concat(quickSort(leftArr),[a],quickSort(rightArr)) } console.log('结果:', quickSort([5,1,6,7]))
func = arr => { let len = arr.length let res = [] // 所有排列结果 /** * 【全排列算法】 * 说明:arrange用来对arr中的元素进行排列组合,将排列好的各个结果存在新数组中 * @param tempArr:排列好的元素 * @param leftArr:待排列元素 */ let arrange = (tempArr, leftArr) => { if (tempArr.length === len) { // 这里就是递归结束的地方 res.push(tempArr.join('')) // 得到全排列的每个元素都是字符串 } else { leftArr.forEach((item, index) => { let temp = [].concat(leftArr) temp.splice(index, 1) // 此时,第一个参数是当前分离出的元素所在数组;第二个参数temp是传入的leftArr去掉第一个后的结果 arrange(tempArr.concat(item), temp) // 这里使用了递归 }) } } arrange([], arr) return res } console.log('结果:', func(['A', 'B', 'C', 'D']))
/** 队列思想(FIFO) 先进先出 * enqueue(element):向队列尾部添加一个(或多个)新的项; * dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素; * front():返回队列中的第一个元素——最先被添加,也将是最先被移除的元素。 队列不做任何变动(不移除元素,只返回元素信息与Stack类的peek方法非常类似); * isEmpty():如果队列中不包含任何元素,返回true,否则返回false; * size():返回队列包含的元素个数,与数组的length属性类似; * toString():将队列中的内容,转成字符串形式 */ function Queue() { this.items = [] // enqueue():将元素加入到队列中 Queue.prototype.enqueue = element => { this.items.push(element) } // dequeue():从队列中删除前端元素 Queue.prototype.dequeue = () => { return this.items.shift() } // front():查看前端的元素 Queue.prototype.front = () => { return this.items[0] } // isEmpty:查看队列是否为空 Queue.prototype.isEmpty = () => { return this.items.length === 0 } // size():查看队列中元素的个数 Queue.prototype.size = () => { return this.items.length } // toString():将队列中元素以字符串形式输出 Queue.prototype.toString = () => { let resultString = '' for (let i of this.items){ resultString += i + ' ' } return resultString } } // 创建队列 let queue = new Queue() // 加入队列 queue.enqueue('a') queue.enqueue('b') queue.enqueue('c') console.log(queue) // 从队列中删除 queue.dequeue() console.log(queue) // 查看前端元素 console.log(queue.front()) // 判断是否为空 console.log(queue.isEmpty()) // 查看 size console.log(queue.size()) // 字符串形式输出 console.log(queue.toString())
/** 栈的思想: 先进后出,后进先出 * enStack(element):添加一个新元素到栈顶位置; * deStack():移除栈顶的元素,同时返回被移除的元素; * peek():返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它); * isEmpty():如果栈里没有任何元素就返回true,否则返回false; * size():返回栈里的元素个数。这个方法和数组的length属性类似; * toString():将栈结构的内容以字符串的形式返回。 * */ function Stack() { this.items = [] // 压栈 Stack.prototype.enStack = element => { this.items.push(element) } // 出栈 Stack.prototype.deStack = () => { return this.items.pop() } // 查看栈顶 Stack.prototype.peek = () => { return this.items[this.items.length-1] } // 判断是否为空 Stack.prototype.isEmpty = () => { return this.items.length === 0 } // 查看栈中元素个数 Stack.prototype.size = () => { return this.items.length } // 以字符串的形式输出 Stack.prototype.toString = () => { let resultString = '' for(let i of this.items) { resultString += i + ' ' } return resultString } } // 创建栈 let stack = new Stack() // 压栈 stack.enStack('a') stack.enStack('b') stack.enStack(3) console.log(stack) // 出栈 console.log(stack.deStack()) // 查看栈顶 console.log(stack.peek()) // 判断是否为空 console.log(stack.isEmpty()) // 查看栈中元素个数 console.log(stack.size()) // 以字符串的形式输出 console.log(stack.toString())
// 创建节点 class Node { constructor(key, left = null, right = null) { this.key = key this.left = left this.right = right } } // 方法 class BinarySearchTree { constructor(node) { this.root = node } // 插入节点 insert(newNode, node = this.root) { if (!this.root) { this.root = newNode } else { if (newNode.key < node.key) { if (node.left === null) { node.left = newNode } else { this.insert(newNode, node.left) } } else { if (node.right === null) { node.right = newNode } else { this.insert(newNode, node.right) } } } } // 前序遍历 preOrderTraverse(curNode = this.root) { //可以从指定结点进行 if (!curNode) { return } let arr = []; const preOrderTraverseNode = node => { if (!node) { return } arr.push(node.key) preOrderTraverseNode(node.left) preOrderTraverseNode(node.right) } preOrderTraverseNode(curNode) return arr } // 中序遍历 inOrderTraverse(curNode = this.root) { if (!curNode) { return } let arr = []; const inOrderTraverseNode = node => { if (!node) { return } inOrderTraverseNode(node.left) arr.push(node.key) inOrderTraverseNode(node.right) } inOrderTraverseNode(curNode) return arr } // 后序遍历 postOrderTraverse(curNode = this.root) { if (!curNode) { return } let arr = []; const postOrderTraverseNode = node => { if (!node) { return } postOrderTraverseNode(node.left) postOrderTraverseNode(node.right) arr.push(node.key) } postOrderTraverseNode(curNode) return arr } // 最小节点 minNode(node = this.root) { if (!node.left) { return node.key } return this.minNode(node.left) } // 最大节点 maxNode(node = this.root) { if (!node.right) { return node.key } return this.maxNode(node.right) } // 查找节点 search(key,curNode = this.root) { if(!curNode) { return false } if(key === curNode.key) { return curNode } return this.search(key, key < curNode.key ? curNode.left : curNode.right) } } const tree = new BinarySearchTree(new Node(11)) tree.insert(new Node(15)) tree.insert(new Node(7)) tree.insert(new Node(5)) tree.insert(new Node(3)) tree.insert(new Node(9)) tree.insert(new Node(8)) tree.insert(new Node(10)) tree.insert(new Node(13)) tree.insert(new Node(12)) tree.insert(new Node(14)) tree.insert(new Node(20)) tree.insert(new Node(18)) tree.insert(new Node(25)) console.log(tree.preOrderTraverse()) console.log(tree.inOrderTraverse()) console.log(tree.postOrderTraverse()) console.log(tree.minNode()) console.log(tree.maxNode()) console.log(tree.search(9))
function getType(obj) {
if (obj === null) {
return String(obj)
}
const toType = (obj) => {
return Object.prototype.toString.call(obj).replace('[object ', '').replace(']', '').toLowerCase()
}
return typeof obj === 'object' ? toType(obj) : typeof obj
}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> img { display: block; margin-bottom: 50px; width: 400px; height: 400px; background-color: red; } </style> </head> <body> <div id="div"> <img src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt=""> <img src="http://ww1.sinaimg.cn/large/006y8mN6gw1fa7kaed2hpj30sg0l9q54.jpg" alt=""> <img src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" alt=""> </div> <script> function lazyload() { //监听页面滚动事件 var documentHeight = document.documentElement.offsetHeight; //文档总高度 var seeHeight = document.documentElement.clientHeight; //可见区域高度 var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度 if (documentHeight - seeHeight - scrollTop < 30) { var imgElement = document.createElement('img'); imgElement.setAttribute('src', 'http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg'); document.querySelector('#div').appendChild(imgElement); } } // 简单的节流函数 //fun 要执行的函数 //delay 延迟 //time 在time时间内必须执行一次 function throttle(fun, delay, time) { var timeout, startTime = new Date(); return function () { var context = this, args = arguments, curTime = new Date(); clearTimeout(timeout); // 如果达到了规定的触发时间间隔,触发 handler if (curTime - startTime >= time) { fun.apply(context, args); startTime = curTime; // 没达到触发间隔,重新设定定时器 } else { timeout = setTimeout(function () { fun.apply(context, args); }, delay); } }; }; // 采用了节流函数 window.addEventListener('scroll', throttle(lazyload, 500, 1000)); </script> </body> </html>
// 累加 reduce addFunc = arr => { return arr.reduce((x, y) => { return x + y }, 0) } console.log(addFunc([8,5,2,1])) // 累加 eval addFunc = arr => { return eval(arr.join('+')) } console.log(addFunc([62,4,3,7])) // 累加 for循环 addFuncFor = arr => { let l = arr.length let sum = 0 for(let i = 0; i < l ; i++) { sum += arr[i] } return sum } console.log(addFuncFor([8,3,47,5]))
// 阶乘 递归 mulFunc = n => { if(n ===0) { return 1 } else { return n * mulFunc(n-1) } } console.log(mulFunc(4)) // 阶乘 for循环 mulFuncFor = n => { for(let i = n-1; i > 0; i--) { n *= i } return n } console.log(mulFuncFor(5))
func = str => {
const arr = []
const group = str.split('|')
group.forEach((item, index) => {
const h = item.split(',')[0]
const v = item.split(',')[1]
let obj = {
h,v
}
arr.push(obj)
})
return arr
}
console.log(func('10,100|20,200|30,300|40,400'))
根据公司不同,面试的情况也会有所不同,有的公司可能偏向基础,有的公司可能偏向算法(可以到力扣上刷刷题)。
最主要还是面试时要表达清晰,不要太紧张,放平心态,关注前沿技术以及一些底层代码,因为面试官会根据你说的话而延伸话题,答题时要懂得灵活应变。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。