私人笔记,仅做方便手机阅读使用javascript
私人笔记,仅做方便手机阅读使用css
私人笔记,仅做方便手机阅读使用html
<font color=red>我是红色</font>
=> 我是红色前端
$\color{red}{我是红色}$
=> vue
使用带!的引用(预览时可能看不到效果)java
这是一段带红色感叹号的引用 node
(待补充···)react
HTTP请求行、请求头、请求体详解webpack
常见的媒体格式类型以下:
text/html : HTML格式
text/plain :纯文本格式
text/xml : XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式
以application开头的媒体格式类型:
application/xhtml+xml :XHTML格式
application/xml : XML数据格式
application/atom+xml :Atom XML聚合格式
application/json : JSON数据格式
application/pdf :pdf格式
application/msword : Word文档格式
application/octet-stream : 二进制流数据(如常见的文件下载)
application/x-www-form-urlencoded : <form encType=''>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
复制代码
另一种常见的媒体格式是上传文件之时使用的:
multipart/form-data : 须要在表单中进行文件上传时,就须要使用该格式
复制代码
以上就是咱们在平常的开发中,常常会用到的若干content-type的内容格式。
经常使用的有:
默认参数
let、const
模板文字(${})
多行字符串(``)
for···of
解构赋值
箭头函数
Promises
延展操做符(...)
import 和 export
最新的 ECMAScript 标准定义了 7 种数据类型:
6 种原始类型:
Boolean
Null
Undefined
Number
String
Symbol
(ECMAScript 6 新定义)和 Object
typeof 是否能正确判断类型?instanceof 能正确判断对象的原理是什么?
typeof
typeof
对于原始类型来讲,除了 null
均可以显示正确的类型
typeof 1 // 'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'
// typeof 不能判断null
typeof null // 'object'
复制代码
typeof
对于对象来讲,除了函数都会显示 object
(typeof console.log // 'function'
),因此说 typeof
并不能准确判断变量究竟是什么类型
instanceof
若是咱们想判断一个对象的正确类型,这时候能够考虑使用 instanceof
,由于内部机制是 经过原型链来判断 的。
对于原始类型来讲,你想直接经过 instanceof 来判断类型是不行的。
const Person = function() {}
const p1 = new Person()
p1 instanceof Person // true
var str = 'hello world'
str instanceof String // false
var str1 = new String('hello world')
str1 instanceof String // true
复制代码
除了原始类型,ES 还有引用类型,typeof
识别出来的类型中,只有object
和 function
是引用类型,其余都是值类型。
根据 JavaScript
中的变量类型传递方式,又分为值类型和引用类型,值类型变量包括 Boolean
、String
、Number
、Undefined
、Null
,引用类型包括了 Object
类的全部,如 Date
、Array
、Function
等。在参数传递方式上,值类型是按值传递,引用类型是按共享传递。
函数 | 做用 | 示例 |
---|---|---|
R.clone() | 克隆一个对象/数组 | const a = R.clone(b) |
R.defaultTo() | 设置默认值(若参数为undefined或null则取默认值) | const defaultTo42 = R.defaultTo(42); |
R.difference() | 找出前一个数组中不包含于后一个数组中的元素 | R.difference([7,6,5,4,3], [1,2,3,4]); //=> [7,6,5] |
R.without() | 找出后一个数组中不包含于前一个数组中的元素 | R.without([7,6,5,4,3], [1,2,3,4]); //=> [1,2] |
R.dissoc() | 返回不包含prop属性的新对象。(R.omit()能够传一个数组 | R.dissoc('b', {a: 1, b: 2, c: 3}); //=> {a: 1, c: 3} |
R.omit() | 返回省略指定键的对象的部分副本。(R.dissoc()只能传一个字符串参数) | R.omit(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, c: 3} |
R.pick() | 返回仅包含指定键的对象的部分副本。不存在则返回空对象{} | R.pick(['a', 'd'], {a: 1, b: 2, c: 3, d: 4}); //=> {a: 1, d: 4} |
R.pickBy() | 返回仅包含知足所提供谓词的键的对象的部分副本。 | const isUpperCase = (val, key) => key.toUpperCase() === key; R.pickBy(isUpperCase, {a: 1, b: 2, A: 3, B: 4}); //=> {A: 3, B: 4} |
R.equals() | 判断是否相等 | const isEquals = R.equals({info:[1,{2}]},{info:[1,{3}]}); //=>false |
R.flatten() | 扁平化数组 | const fla = R.flatten([1,[2,3,[4,5,[6,[7,[8],[9],[10]]]]]]); //=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
R.fromPairs() | 从列表键值对建立新对象。若是一个键出如今多个对中,则最右边的对包含在对象中。 | R.fromPairs([['a', 1], ['b', 2], ['c', 3]]); //=> {a: 1, b: 2, c: 3} |
R.isEmpty() | 判断目标是否为空(数组/对象/字符串) | const isEmptyObj = R.isEmpty([]);//=> true |
R.isNil() | 判断目标是否为null或者undefined | const isNil = R.isNil(null);//=> true |
R.keys() | 返回一个列表,其中包含所提供对象的全部可枚举属性的名称。 | R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c'] |
R.values() | 返回所提供对象的全部可枚举自身属性的列表。 | R.values({a: 1, b: 2, c: 3}); //=> [1, 2, 3] |
R.uniq() | 返回一个新列表,其中只包含原始列表中每一个元素的一个副本。([...new Set()] 去重没法处理数组和对象) |
R.uniq([1,2,1,1,'1',3,2,5,[6],[6],{c:'c'},{c:'c'},{c:'d'}]); //=> [1, 2, "1", 3, 5, [6],{c:'c'},{c:'d'}] |
R.zipObj() | 从键列表和值列表中建立新对象。键/值配对被截断为两个列表中较短者的长度。 | R.zipObj(['a', 'b', 'c', 'd'], [1, 2, 3]); //=> {a: 1, b: 2, c: 3} |
定义:高阶组件就是一个函数,且该函数接受一个组件做为参数,并返回一个新的组件。
分类:代理方式、继承方式
做用:
const MyContainer = (WrappedComponent) => {
return class extends WrappedComponent {
render() {
const elementsTree = super.render();
let newProps = {};
if (elementsTree && elementsTree.type === 'input') {
newProps = {value: 'may the force be with you'};
}
const props = Object.assign({}, elementsTree.props, newProps);
const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children);
return newElementsTree;
}
}
}
export default MyContainer;
复制代码
componentWillMount()
、componentWillUpdata()
、componentWillReceiveProps()
在 v16.3 版本后不推荐使用,在 v17 版本计划删除。
componentWillMount()
被static getDerivedStateFromProps()
取代;componentWillUpdata()
被getSnapshotBeforeUpdate()
取代;componentWillReceiveProps()
应避免使用
安装阶段
在建立组件的实例并将其插入DOM时,将按如下顺序调用这些方法:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
更新
state的更改可能致使更新。从新渲染组件时,将按如下顺序调用这些方法:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸载
从DOM中删除组件时调用此方法:
componentWillUnmount()
static getDerivedStateFromProps()
→ shouldComponentUpdate()
→ render()
→ getSnapshotBeforeUpdate()
→ componentDidUpdate()
性能优化的原则是以更好的用户体验为标准,具体就是实现下面的 目标:
优化的 方向 有两个:
减小页面体积,提高网络加载
优化页面渲染
性能优化怎么作
上面提到的都是性能优化的单个点,性能优化项目具体实施起来,应该按照下面步骤推动:
性能优化是个长期的事情,不是一蹴而就的,应该本着 先摸底、再分析、后优化 的原则逐步来作。
内容方面:
减小 HTTP 请求 (Make Fewer HTTP Requests)
减小 DOM 元素数量 (Reduce the Number of DOM Elements)
使得 Ajax 可缓存 (Make Ajax Cacheable)
针对CSS:
把 CSS 放到代码页上端 (Put Stylesheets at the Top)
从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)
精简 JavaScript 与 CSS (Minify JavaScript and CSS)
避免 CSS 表达式 (Avoid CSS Expressions)
针对JavaScript :
脚本放到 HTML 代码页底部 (Put Scripts at the Bottom)
从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)
精简 JavaScript 与 CSS (Minify JavaScript and CSS)
移除重复脚本 (Remove Duplicate Scripts)
面向图片(Image):
优化图片
不要在 HTML 中使用缩放图片
使用恰当的图片格式
使用 CSS Sprites 技巧对图片优化
1xx:1开头的是信息状态码
2xx:2开头的是请求成功
3xx:3开头的是重定向
4xx:4开头的是客户端错误
5xx:5开头的是服务器错误
优点:
React速度很快 :它并不直接对DOM进行操做,引入了一个叫作虚拟DOM的概念,安插在javascript逻辑和实际的DOM之间,性能好。
跨浏览器兼容 :虚拟DOM帮助咱们解决了跨浏览器问题,它为咱们提供了标准化的API,甚至在IE8中都是没问题的。
一切都是component :代码更加模块化,重用代码更容易,可维护性高。
单向数据流 :Flux是一个用于在JavaScript应用中建立单向数据层的架构,它随着React视图库的开发而被Facebook概念化。
同构、纯粹的javascript :由于搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。
兼容性好 :好比使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务再也不让人望而生畏。
缺点:
虚拟DOM是在DOM的基础上创建了一个抽象层,对数据和状态所作的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中
在React中,render执行的结果获得的并非真正的DOM节点,而仅仅是JavaScript对象,称之为 虚拟DOM。
innerHTML:render html字符串 + 从新建立全部 DOM 元素
虚拟DOM:render 虚拟DOM + diff + 更新必要的 DOM 元素
调用this.setState
会致使re-render
(从新渲染),但不会影响到整个页面,而只会影响组件自己及其children
组件。父母和兄弟姐妹都不会受到影响。当咱们有一个层级很深的组件链时,这会让状态更新变得很是方便,由于咱们只须要重绘(redraw
)它的一部分。
虚拟DOM的优势:
最终表如今DOM上的修改只是变动的部分,能够保证很是高效的渲染。
虚拟DOM的缺点:
首次渲染大量DOM时,因为多了一层虚拟DOM的计算,会比innerHTML插入慢。
Redux VS Reflux:
组件就是用户界面,actions就是组件的动做,store用于执行actions的命令,并返回一个state对象给组件。组件经过state来更新界面。
而Redux的功能流程以下:
state就是数据,组件就是数据的呈现形式,action是动做,action是经过reducer来更新state的。
Redux VS Mobx
store
与多 store
。在 Redux
中,你将全部的 state
都放在一个全局的 store
。这个 store
对象就是你的单一数据源。另外一方面,多个 reducers
容许你修改不可变的 state
。 Mobx
则相反,它使用多 stores
。State
不可变。而Mobx
的 State
则能够改变。轻量级的框架
双向数据绑定 -- 好比你改变一个输入框 Input 标签的值,会自动同步更新到页面上其余绑定该输入框的组件的值
组件化 -- 页面上小到一个按钮均可以是一个单独的文件.vue,这些小组件直接能够像乐高积木同样经过互相引用而组装起来
单向响应的数据流
指令
插件化
声明式设计 -- React采用声明范式,能够轻松描述应用。
高效 -- React经过对DOM的模拟,最大限度地减小与DOM的交互。
灵活 -- −React能够与已知的库或框架很好地配合。
JSX -- JSX 是 JavaScript 语法的扩展。React 开发不必定使用 JSX ,但咱们建议使用它。
组件 -- 经过 React 构建组件,使得代码更加容易获得复用,可以很好的应用在大项目的开发中。
单向响应的数据流 -- React 实现了单向响应的数据流,从而减小了重复代码,这也是它为何比传统数据绑定更简单。
使用 Virtual DOM(虚拟 DOM经过 diff 比对,找到变动节点,从新渲染)
提供了响应式(Reactive)和组件化(Composable)的视图组件。
将注意力集中保持在核心库,而将其余功能如路由和全局状态管理交给相关的库。
使用Prop传递数据,prop 是单向绑定的,当父组件的属性变化时,将传导给子组件,可是不会反过来。子组件不该该直接改变prop的值。
都提供了路由、状态管理器(React对应的Redux,Vue对应Vuex)等。
都提供合理的钩子函数,可让开发者定制化地去处理需求。
在组件开发中都支持mixins的特性。
性能上
React 和 Vue 在大部分常见场景下都能提供近似的性能。一般 Vue 会有少许优点,由于 Vue 的 Virtual DOM 实现相对更为轻量一些。
在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,从新渲染整个组件子树。如要避免没必要要的子组件的重渲染,有相应的处理机制PureComponent(React.Component 与 React.PureComponent)。在 Vue 应用中,组件的依赖是在渲染过程当中自动追踪的,因此系统能精确知晓哪一个组件确实须要被重渲染。
用 Vue 和 React 开发大多数应用的速度都是足够快的。假如你要开发一个对性能要求比较高的数据可视化或者动画的应用时,你须要了解到下面这点:在开发中,Vue 每秒最高处理 10 帧,而 React 每秒最高处理不到 1 帧。
HTML & CSS
在 React 中,一切都是 JavaScript。HTML 能够用 JSX 来表达。Vue 的总体思想是拥抱经典的 Web 技术(采用template方式,好比v-on的各类修饰符,在 JSX 中实现对应的功能会须要多得多的代码),事实上 Vue 也提供了render渲染函数,甚至支持 JSX。
在 React 中,如今的潮流也愈来愈多地将 CSS 也归入到 JavaScript 中来处理(有其优越性,具体不详说),经过依赖引入css模块,而 Vue 可让你在每一个单文件组件中彻底访问 CSS,方便的规定css做用域,也可引入css模块。
其余
二者另外一个重要差别是,Vue 的路由库和状态管理库都是由官方维护支持且与核心库同步更新的。React 则是选择把这些问题交给社区维护,所以建立了一个更分散的生态系统。但相对的,React 的生态系统相比 Vue 更加繁荣。
从二者提供的路由、状态管理器等向上扩展来看,Vue、React作得都比较完善,从向下扩展来看,Vue就相似于 jQuery。你只要把以下标签放到页面就能够运行:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
本地渲染。ReactNative 能使你用相同的组件模型编写有本地渲染能力的 APP(iOS 和 Android)。能同时跨多平台开发,对开发者是很是棒的。相应地,Vue 和Weex会进行官方合做,Weex 是阿里的跨平台用户界面开发框架,Weex 的 JavaScript 框架运行时用的就是 Vue。这意味着在 Weex 的帮助下,你使用 Vue 语法开发的组件不只仅能够运行在浏览器端,还能被用于开发 iOS 和 Android 上的原生应用。固然在如今,Weex 还在积极发展,成熟度也不能和 ReactNative 相抗衡。
Vue.js在模板中提供了指令,过滤器等,能够很是方便,快捷地操做DOM。
axios 是目前最经常使用的 http 请求库,能够用于浏览器和 node.js
Axios 的主要特性包括:
基于 Promise
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
// 这里的函数异步执行,可参考以前 JS 基础中的异步模块
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert(xhr.responseText)
}
}
}
xhr.open("GET", "/api", false)
xhr.send(null)
复制代码
上述代码中,有两处状态码须要说明。xhr.readyState
是浏览器判断请求过程当中各个阶段的,xhr.status
是 HTTP 协议中规定的不一样结果的返回状态说明。
xhr.readyState
的状态码说明:
0 -代理被建立,但还没有调用 open()
方法。
1 -open()
方法已经被调用。
2 -send()
方法已经被调用,而且头部和状态已经可得到。
3 -下载中, responseText
属性已经包含部分数据。
4 -下载操做已完成
xhr.status
即 HTTP 状态码,有 2xx 3xx 4xx 5xx 这几种,比较经常使用的有如下几种:
同源策略:同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能。所谓同源指的是协议、域名、端口均相同。
跨域:浏览器从一个域名的网页去请求另外一个域名的资源时,协议、域名、端口有一个不一样(违反同源策略),即视为跨域。
Jsonp
利用 src
属性不受同源策略影响实现跨域。例如<script/>
、<img/>
、<iframe/>
标签。
注意src中带上回调函数callback
。
<script type="text/javascript" src="http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998&callback=flightHandler"></script>
<script type="text/javascript" src=''>
// 获得航班信息查询结果后的回调函数
var flightHandler = function(data){
alert('你查询的航班结果是:票价 ' + data.price + ' 元,' + '余票 ' + data.tickets + ' 张。');
};
</script>
复制代码
iframe跨子域
基于iframe实现的跨域要求两个域具备aa.xx.com,bb.xx.com 这种特色, 也就是两个页面必须属于一个基础域(例如都是xxx.com),使用同一协议和同一端口,这样在两个页面中同时添加document.domain,就能够实现父页面调用子页面的函数。
eg:a.study.cn/a.html 请求 b.study.cn/b.html
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
document.domain = 'study.cn';
function test() {
alert(document.getElementById('a').contentWindow);
}
</script>
</head>
<body>
<iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
复制代码
Ajax
JQuery已经将跨域封装到了Ajax中
<script type="text/javascript">
$(document).ready(function(){
var name = 'chenshishuo';
var sex = 'man';
var address = 'shenzhen';
var looks = 'handsome ';
$.ajax({
type : 'get',
url:'http://192.168.31.137/train/test/testjsonp',
data : {
name : name,
sex : sex,
address : address,
looks : looks,
},
cache :false,
jsonp: "callback",
jsonpCallback:"success",
dataType : 'jsonp',
success:function(data){
alert(data);
},
error:function(data){
alert('error');
}
});
});
</script>
复制代码
后端添加白名单
为何要有token与session?——身份验证,用以判断两次请求是否予以经过
session生成方式?
浏览器第一次访问服务器,服务器会建立一个session,而后同时为该session生成一个惟一的会话的key,也就是sessionid。而后服务器再把sessionid,以cookie的形式发送给客户端。这样浏览器下次再访问时,会直接带着cookie中的sessionid。而后服务器根据sessionid找到对应的session进行匹配;
还有一种是浏览器禁用了cookie或不支持cookie,这种能够经过URL重写的方式发到服务器;
简单来说,用户访问的时候说他本身是张三,他骗你怎么办? 那就在服务器端保存张三的信息,给他一个id,让他下次用id访问。
token的生成方式?
浏览器第一次访问服务器,根据传过来的惟一标识userId,服务端会经过一些算法,如经常使用的HMAC-SHA256算法,而后加一个密钥,生成一个token,而后经过BASE64编码一下以后将这个token发送给客户端;客户端将token保存起来,下次请求时,带着token,服务器收到请求后,而后会用相同的算法和密钥去验证token,若是经过,执行业务操做,不经过,返回不经过信息;
token和session的区别?
token和session其实都是为了身份验证,session通常翻译为会话,而token更多的时候是翻译为令牌; session服务器会保存一份,可能保存到缓存,文件,数据库;一样,session和token都是有过时时间一说,都须要去管理过时时间;
虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不须要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑以外,整个信息是自包含的。这才是真正的无状态。 而 sessionid ,通常都是一段随机字符串,须要到后端去检索 id 的有效性。万一服务器重启致使内存里的 session 没了呢?万一 redis 服务器挂了呢?
方案 A(session) :我发给你一张身份证,但只是一张写着身份证号码的纸片。你每次来办事,我去后台查一下你的 id 是否是有效。
方案 B(token) :我发给你一张加密的身份证,之后你只要出示这张卡片,我就知道你必定是本身人。
在条件判断时,除了 undefined
, null
, false
, NaN
, ''
, 0
, -0
,其余全部值都转为 true
,包括全部对象。
[]
与{}
都会转为Number
,[]
会转为0
,{}
会转为NaN
if ([] == false) console.log(1);
if ([]) console.log(2);
if ({} == false ) console.log(3);
if ({}) console.log(4);
if ([1] == [1]) console.log(5);
// => 1 2 4
复制代码
关于[]
和{}
须要注意的点:
[]
和空对象{}
都是object
类型,所以直接用于if
判断条件时就会被转化为 true
。Number
。[]
与布尔值false
比较,false
转化为0
,而空数组[]
也转化为0
,所以[] == false
的判断获得true
。{}
与布尔值false
比较,false
转化为0
,而空对象{}
转化为NaN
,因为NaN
与任何数都不相等,所以{} == false
的判断获得false
。[] == [] //false
地址不同console.log(([0]) ? true : false); // true
console.log(([0] == false) ? true : false); // true
console.log(({x:0} == false) ? true : false); // false
复制代码
[0]
直接用于if
判断条件时会被转化为true
。 与布尔值比较,都会将两边的值转化为Number
,[0]
转换为0
,{x:0}
转换为NaN
。
首先能够经过 Object.assign
来解决这个问题。固然咱们也能够经过展开运算符 …
来解决
let a = {
age: 1
}
let b = Object.assign({}, a)
let c = {...a}
a.age = 2
console.log(b.age) // 1
console.log(c.age) // 1
复制代码
一般浅拷贝就能解决大部分问题了,可是当咱们遇到以下状况就须要使用到深拷贝了
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = {...a}
a.jobs.first = 'native'
console.log(b.jobs.first) // native
复制代码
浅拷贝只解决了第一层的问题,若是接下去的值中还有对象的话,那么就又回到刚开始的话题了,二者享有相同的引用。要解决这个问题,咱们须要引入深拷贝。
这个问题 一般 能够经过 JSON.parse(JSON.stringify(object))
来解决。
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
复制代码
可是该方法也是有局限性的:
$.extend([deep],target,Object1,Object2)
deep
为布尔值,默认不传为false(若是传值只能传true),为true时表示深拷贝
或者借助Ramda
函数库,Ramda.clone()
Promise 是异步编程的一种解决方案,用于抽象异步处理对象。能够避免回调地狱。
Promise 对象有如下两个特色:
对象的状态不受外界影响。Promise对象表明一个异步操做,有三种状态:pending(进行中)
、fulfilled(已成功)
和 rejected(已失败)
。只有异步操做的结果,能够决定当前是哪种状态,任何其余操做都没法改变这个状态。
一旦状态改变,就不会再变,任什么时候候均可以获得这个结果。Promise对象的状态改变,只有两种可能:从 pending
变为 fulfilled
和从 pending
变为 rejected
。只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved
(已定型)。若是改变已经发生了,你再对 Promise
对象添加回调函数,也会当即获得这个结果。
有了Promise对象,就能够将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操做更加容易。
使用注意点:
await
命令后面的 Promise
对象,运行结果多是rejected
,因此最好把await
命令放在try...catch
代码块中。
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
// 另外一种写法
async function myFunction() {
await somethingThatReturnsAPromise()
.catch(function (err) {
console.log(err);
});
}
复制代码
多个await
命令后面的异步操做,若是不存在继发关系,最好让它们同时触发。
let foo = await getFoo();
let bar = await getBar();
复制代码
上面代码中,getFoo
和getBar
是两个独立的异步操做(即互不依赖),被写成继发关系。这样比较耗时,由于只有getFoo
完成之后,才会执行getBar
,彻底可让它们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
复制代码
上面两种写法,getFoo
和 getBar
都是同时触发,这样就会缩短程序的执行时间。
await
命令只能用在async
函数之中,若是用在普通函数,就会报错。主线程、执行栈、任务队列:
event loop是一个执行模型,在不一样的地方有不一样的实现。
js是单线程的
js进程分为宏任务与微任务,优先执行微任务,后执行宏任务(其实不许确,页面加载先执行一个<script>
标签,属于宏任务),若微任务执行过程当中产生了新的微任务,则将改新产生的微任务置于队列底部(例如队列为A -> B -> C,执行A过程当中产生了微任务A1,则将A1置于C后面A -> B -> C -> A1)
常见的微任务:promise
,process.nextTick
(Node独有,以及其余不太常见的本次不讨论)
常见的宏任务:setTimeout
,setInterval
,setImmediate
(Node独有,以及其余不太常见的本次不讨论)
关于process.nextTic
,优先级 promise
> process.nextTick
> promise.then
,例如:
new Promise(function(resolve) {
console.log('1');
resolve();
}).then(function() {
console.log('2')
})
process.nextTick(function() {
console.log('3');
})
输出结果:1,3,2
复制代码
setImmediate(function () {
console.log(1);
}, 0);
setTimeout(function () {
console.log(2);
}, 0);
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
});
console.log(6);
process.nextTick(function () {
console.log(7);
});
console.log(8);
// 3 4 6 8 7 5 1 2
复制代码
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');
输出结果:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
复制代码
最简单的清空和截短数组的方法就是改变 length
属性:
const arr = [11, 22, 33, 44, 55, 66];
// 截取
arr.length = 3;
console.log(arr); //=> [11, 22, 33];
// 清空
arr.length = 0;
console.log(arr); //=> []
console.log(arr[2]); //=> undefined
复制代码
使用扩展运算符能够快速扁平化数组(或直接使用ES6的flat()
):
const arr = [11, [22, 33], [44, 55], 66];
const flatArr = [].concat(...arr); // => [11, 22, 33, 44, 55, 66]
const flatArr2 = arr.flat(); // => [11, 22, 33, 44, 55, 66]
复制代码
不幸的是,上面的技巧只能适用 二维数组 ,可是使用递归,咱们能够扁平化任意纬度数组:
function flattenArray(arr) {
const flattened = [].concat(...arr);
return flattened.some(item => Array.isArray(item)) ?
flattenArray(flattened) : flattened;
}
const arr = [11, [22, 33], [44, [55, 66, [77, [88]], 99]]];
const flatArr = flattenArray(arr); //=> [11, 22, 33, 44, 55, 66, 77, 88, 99]
复制代码
或者使用ramda
函数,Ramda.flatten()
let arr = [1,2,[4,{name:'jack'},[5,{name:'tom',info:{add:'wuhan'}}]]]
R.flatten(arr) //=> [1,2,4,{name:'jack'},5,{name:'tom',info:{add:'wuhan'}}]
复制代码
// 生成六位随机数(数字字母组成)
// 先生成随机数而后转成36进制字符串(36进制 0-9a-z),而后在小数位取六位
Math.random().toString(36).slice(2,8)
(~~(Math.random()*(1<<30))).toString(36)
复制代码
例如:1234567890 => 1,234,567,890
最优实现:
正则表达式的零宽断言 /\d{1,3}(?=(\d{3})+$)/g
let a = 12345678900;
let b = a.toString(); // b => '1234567890'
let c = b.replace(/\d{1,3}(?=(\d{3})+$)/g, res => res + ','); // c => '1,234,567,890'
复制代码
或者(不用正则)函数实现:
const qianfen = number => {
let earr = [];
let arr = number.toString().split('').reverse();
arr.forEach((i,idx) => earr.push((idx != arr.length - 1 && idx % 3 == 2) ? ',' + i : i));
return earr.reverse().join('');
}
console.log(qianfen(123456789)); // => 123,456,789
console.log(qianfen(1234567890)); // => 1,234,567,890
复制代码
思路:先将特定关键字取出改变,而后再渲染。此时用到
dangerouslySetInnerHTML
属性
render() {
let words = '杭州杭城科技有限公司';
let keys = '杭';
let reg = new RegExp(keys, 'ig'); // reg => /杭/gi
const showw = w => {
//let ss = w.replace(/杭/ig,"<b style='color: green;'>$&</b>");
let ss = w.replace(reg,"<b style='color: green;'>$&</b>");
return ss;
}
let kk = showw(words);
return (<div className="card">
<div className='show' style={{color:'red'}} dangerouslySetInnerHTML={{__html:kk}}></div>
</div>)
}
复制代码
在介绍缓存的时候,咱们习惯将缓存分为 强缓存 和 协商缓存 两种。二者的主要区别是使用本地缓存的时候,是否须要向服务器验证本地缓存是否依旧有效。
function add (a, b) {
let lenA = a.length,
lenB = b.length,
len = lenA > lenB ? lenA : lenB;
// 先补齐位数一致
if(lenA > lenB) {
for(let i = 0; i < lenA - lenB; i++) {
b = '0' + b;
}
} else {
for(let i = 0; i < lenB - lenA; i++) {
a = '0' + a;
}
}
// arr 存储最终结果的数组,carryAdd 逐位相加时产生的进位
let arrA = a.split('').reverse(),
arrB = b.split('').reverse(),
arr = [],
carryAdd = 0;
// 逐位相加。若产生进位则carryAdd 为 1,不然carryAdd 为 0
// carryAdd 逐位存储两数逐位相加产生的个位
for(let i = 0; i < len; i++) {
let temp = Number(arrA[i]) + Number(arrB[i]) + carryAdd;
arr[i] = temp > 9 ? temp - 10 : temp;
carryAdd = temp >= 10 ? 1 : 0;
}
// 最后判断一次首位是否产生进位
if(carryAdd === 1) {
arr[len] = 1;
}
return arr.reverse().join('');
}
复制代码
函数节流
是指 必定时间内js方法只跑一次。好比人的眨眼睛,就是必定时间内眨一次。这是函数节流最形象的解释。
函数防抖
是指频繁触发的状况下,只有足够的空闲时间,才执行代码一次。好比生活中的坐公交,就是必定时间内,若是有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。
// 函数节流
let flag = false;
const throttling = (func, ms = 500) => {
if (flag) return;
flag = true;
setTimeout(() => {
func();
flag = false;
}, ms);
};
// 思路:定义一个flag,若是当前是空闲的则执行函数,若是当前非空闲,直接return出去。
// setTimeout()用于在固定时间后将状态设置为空闲。也就是固定时间(ms)只能执行一次js。
// 存在的问题:若是执行函数耗时超过了ms 那么仍是会出现上一次没执行完又执行下一次请求的状况
复制代码
// 函数防抖
let timer = 0;
const debounce = (func, ms = 500) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func();
}, ms);
};
// 思路:设置一个定时器,延迟处理请求函数;若是等待时间await以后没人执行请求,则执行函数
// 若是用户调用该函数的间隔小于wait的状况下,上一次的时间还未到就被清除了,并不会执行函数
复制代码
大小 = 分辨率 * 位深/8 (/8计算的是字节数。)
分辨率 = 宽 * 高(如:1024 * 768,640 * 480)
位深:如24位,16位,8位
例如: 一幅图像分辨率:1024*768,24位,则其大小计算以下:
大小 = 1024 * 768 * 24 / 8 = 2359296byte = 2304KB
要点以下:
DNS
服务器获得域名的 IP
地址 (中间有三次握手)IP
的机器发送 HTTP
请求HTTP
请求例如在浏览器输入https://juejin.im/timeline,而后通过 DNS 解析,juejin.im对应的 IP 是36.248.217.149(不一样时间、地点对应的 IP 可能会不一样)。而后浏览器向该 IP 发送 HTTP 请求。server 端接收到 HTTP 请求,而后通过计算(向不一样的用户推送不一样的内容),返回 HTTP 请求,返回一堆 HMTL 格式的字符串,由于只有 HTML格式浏览器才能正确解析。接下来就是浏览器的渲染过程。
要点以下:
HTML
结构生成 DOM
树CSS
生成 CSSOM
DOM
和 CSSOM
整合造成 RenderTree
RenderTree
开始渲染和展现<script>
时,会执行并阻塞渲染当元素的样式发生变化时,浏览器须要触发更新,从新绘制元素。这个过程当中,有两种类型的操做,即重绘与回流。
重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时因为只须要UI层面的从新像素绘制,所以 损耗较少
回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会从新渲染页面,称为回流。此时,浏览器须要从新通过计算,计算后还须要从新页面布局,所以是较重的操做。会触发回流的操做:
页面初次渲染
浏览器窗口大小改变
元素尺寸、位置、内容发生改变
元素字体大小变化
添加或者删除可见的 dom 元素
激活 CSS 伪类(例如::hover)
查询某些属性或调用某些方法:
回流一定触发重绘,重绘不必定触发回流。重绘的开销较小,回流的代价较高。
题目:前端常见的安全问题有哪些?
Web 前端的安全问题,能回答出下文的两个问题,这个题目就能基本过关了。开始以前,先说一个最简单的攻击方式 —— SQL 注入。
上学的时候就知道有一个「SQL注入」的攻击方式。例如作一个系统的登陆界面,输入用户名和密码,提交以后,后端直接拿到数据就拼接 SQL 语句去查询数据库。若是在输入时进行了恶意的 SQL 拼装,那么最后生成的 SQL 就会有问题。可是如今稍微大型一点的系统,都不会这么作,从提交登陆信息到最后拿到受权,要通过层层的验证。所以,SQL 注入都只出如今比较低端小型的系统上。
这是前端最多见的攻击方式,不少大型网站(如 Facebook)都被 XSS 攻击过。
举一个例子,我在一个博客网站正常发表一篇文章,输入汉字、英文和图片,彻底没有问题。可是若是我写的是恶意的 JS 脚本,例如获取到document.cookie而后传输到本身的服务器上,那我这篇博客的每一次浏览都会执行这个脚本,都会把访客 cookie 中的信息偷偷传递到个人服务器上来。
其实原理上就是黑客经过某种方式(发布文章、发布评论等)将一段特定的 JS 代码隐蔽地输入进去。而后别人再看这篇文章或者评论时,以前注入的这段 JS 代码就执行了。JS 代码一旦执行,那可就不受控制了,由于它跟网页原有的 JS 有一样的权限,例如能够获取 server 端数据、能够获取 cookie 等。因而,攻击就这样发生了。
XSS的危害
XSS 的危害至关大,若是页面能够随意执行别人不安全的 JS 代码,轻则会让页面错乱、功能缺失,重则会形成用户的信息泄露。
好比早些年社交网站常常爆出 XSS 蠕虫,经过发布的文章内插入 JS,用户访问了感染不安全 JS 注入的文章,会自动从新发布新的文章,这样的文章会经过推荐系统进入到每一个用户的文章列表面前,很快就会形成大规模的感染。
还有利用获取 cookie 的方式,将 cookie 传入入侵者的服务器上,入侵者就能够模拟 cookie 登陆网站,对用户的信息进行篡改。
XSS的预防
那么如何预防 XSS 攻击呢?—— 最根本的方式,就是对用户输入的内容进行验证和替换,须要替换的字符有:
& 替换为:&
< 替换为:<
> 替换为:>
” 替换为:"
‘ 替换为:'
/ 替换为:/
复制代码
替换了这些字符以后,黑客输入的攻击代码就会失效,XSS 攻击将不会轻易发生。
除此以外,还能够经过对 cookie 进行较强的控制,好比对敏感的 cookie 增长http-only限制,让 JS 获取不到 cookie 的内容。
CSRF 是借用了当前操做者的权限来偷偷地完成某个操做,而不是拿到用户的信息。
例如,一个支付类网站,给他人转帐的接口是https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf
,而这个接口在使用时没有任何密码或者 token
的验证,只要打开访问就直接给他人转帐。一个用户已经登陆了http://buy.com
,在选择商品时,忽然收到一封邮件,而这封邮件正文有这么一行代码<img src="https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf"/>
,他访问了邮件以后,其实就已经完成了购买。
CSRF 原理示意图:
CSRF 的发生实际上是借助了一个 cookie
的特性。咱们知道,登陆了http://buy.com
以后,cookie
就会有登陆过的标记了,此时请求https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf
是会带着 cookie
的,所以 server
端就知道已经登陆了。而若是在 http://buy.com
去请求其余域名的 API 例如http://abc.com/api
时,是不会带 cookie
的,这是浏览器的同源策略的限制。可是 —— 此时在其余域名的页面中,请求https://user-gold-cdn.xitu.io/2019/2/17/168f9547e3ae02cf
,会带着buy.com
的 cookie
,这是发生 CSRF 攻击的理论基础。
预防 CSRF 就是加入各个层级的权限验证,例如如今的购物网站,只要涉及现金交易,确定要输入密码或者指纹才行。除此以外,敏感的接口使用POST请求而不是GET 也是很重要的。
“构建”也可理解为“编译”,就是将开发环境的代码转换成运行环境代码的过程。开发环境的代码是为了更好地阅读,而运行环境的代码是为了更快地执行,二者目的不同,所以代码形式也不同。例如,开发环境写的 JS 代码,要经过混淆压缩以后才能放在线上运行,由于这样代码体积更小,并且对代码执行不会有任何影响。总结一下须要构建工具处理的几种状况:
本质上来讲,两个名词都是 CPU工做时间片的一个描述。
进程 描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来讲就表明了一个程序。线程 是进程中的更小单位,描述了执行一段指令所需的时间。
把这些概念拿到浏览器中来讲,当你打开一个 Tab 页时,其实就是建立了一个进程,一个进程中能够有多个线程,好比渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是建立了一个线程,当请求结束后,该线程可能就会被销毁。
若是在 JS 执行的时候 UI 线程还在工做,就可能致使不能安全的渲染 UI。这其实也是一个单线程的好处,得益于 JS 是单线程运行的,能够达到节省内存,节约上下文切换时间,没有锁的问题的好处。
能够把执行栈认为是一个 存储函数调用的栈结构 ,遵循先进后出的原则。
instanceof 能够正确的判断对象的类型,由于内部机制是 经过判断对象的原型链中是否是能找到类型的 prototype
。
原型(prototype): 一个简单的对象,用于实现对象的 属性继承。能够简单的理解成对象的爹。在 Firefox 和 Chrome 中,每一个JavaScript对象中都包含一个__proto__ (非标准)的属性指向它爹(该对象的原型),可obj.__proto__进行访问。
构造函数: 能够经过new来 新建一个对象 的函数。
实例: 经过构造函数和new建立出来的对象,即是实例。 实例经过__proto__指向原型,经过constructor指向构造函数。
举个栗子,以 Object
为例,咱们经常使用的 Object
即是一个构造函数,所以咱们能够经过它构建实例。
// 实例
const instance = new Object()
复制代码
则此时, 实例为 instance
,构造函数为 Object
,咱们知道,构造函数拥有一个 prototype
的属性指向原型,所以原型为:
// 原型
const prototype = Object.prototype
复制代码
这里咱们能够来看出三者的关系:
实例.__proto__ === 原型
原型.constructor === 构造函数
构造函数.prototype === 原型
// 这条线实际上是是基于原型进行获取的,能够理解成一条基于原型的映射线
// 例如:
// const o = new Object()
// o.constructor === Object --> true
// o.__proto__ = null;
// o.constructor === Object --> false
实例.constructor === 构造函数
复制代码
关系图以下:
let shili = new Object();
let yuanxin = Object.prototype;
实例: shili
构造函数: Object 或 shili.constructor 或 yuanxin.constructor
原型:yuanxin (Object.prototype) 或 shili.__proto__
复制代码
其实每一个 JS 对象都有 __proto__
属性,这个属性指向了原型。
原型 也是一个对象,而且这个对象中包含了不少函数,咱们能够得出一个结论:对于 obj 来讲,能够经过 __proto__
找到一个原型对象,在该对象中定义了不少函数让咱们来使用。
原型的
constructor
属性指向构造函数,构造函数又经过prototype
属性指回原型,可是并非全部函数都具备这个属性,Function.prototype.bind()
就没有这个属性。
其实 原型链 就是多个对象经过 __proto__
的方式链接了起来。
- Object 是全部对象的爸爸,全部对象均可以经过 proto 找到它
- Function 是全部函数的爸爸,全部函数均可以经过 proto 找到它
- 函数的 prototype 是一个对象
- 对象的 proto 属性指向原型, proto 将对象和原型链接起来组成了原型链
模块化就是 将文件按照功能分离,根据需求引入不一样的文件中 。源于服务器端。
使用模块化能够给咱们带来如下好处:
代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。
在MDN上对于 Proxy 的解释是:
Proxy 对象用于定义基本操做的自定义行为(如属性查找,赋值,枚举,函数调用等)。
简单来讲: Proxy 对象就是可让你去对JavaScript中的一切合法对象的基本操做进行自定义。而后用你自定义的操做去覆盖其对象的基本操做。也就是当一个对象去执行一个基本操做时,其执行的过程和结果是你自定义的,而不是对象的。
Proxy的做用 :
对于代理模式 Proxy 的做用主要体如今三个方面:
拦截和监视外部对对象的访问
下降函数或类的复杂度
在复杂操做前对操做进行校验或对所需资源进行管理
前端路由原理?两种实现方式有什么区别?
前端路由实现起来其实很简单,本质就是 监听 URL 的变化,而后匹配路由规则,显示相应的页面,而且无须刷新页面 。目前前端使用的路由就只有两种实现方式:
Babel 是一个 JavaScript 编译器
Babel 是一个工具链,主要用于在旧的浏览器或环境中将 ECMAScript 2015+ 代码转换为向后兼容版本的 JavaScript 代码:
特性 :
组件状态由state控制。假设咱们如今有一个表单,表单中有一个input标签,input的value值必须是咱们设置在constructor构造函数的state中的值,而后,经过onChange触发事件来改变state中保存的value值,这样造成一个循环的回路影响。也能够说是React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。
组件状态不禁
state
控制。其值能够经过refs
获取。常见的有input(不添加value
、defaultvalue
、onChange()
等)
handleSubmit = event => {
const val = this.refs.inputRef.value;
alert('A name was submitted: ' + val);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref='inputRef'/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
复制代码
函数 | 做用 |
---|---|
Trident | IE内核 |
Gecko | Firefox浏览器内核 |
Webkit | Safari浏览器内核 |
Presto | Opera浏览器内核,最初是本身的Presto内核,后来是Webkit,如今是Blink内核 |
Chromium | 统称为Chromium内核或Chrome内核,之前是Webkit内核,如今是Blink内核 |
const b = [1,4,6,3,7,4,6,3,2,9];
const quickSort = arr => {
const len = arr.length;
if (len <= 1) return arr;
const s = Math.floor(len / 2);
const temp = arr.splice(s, 1);
let left=[],
right=[];
arr.forEach(i => i < temp ? left.push(i) : right.push(i));
return quickSort(left).concat(temp, quickSort(right));
}
console.log(quickSort(b)); // => [1, 2, 3, 3, 4, 4, 6, 6, 7, 9]
复制代码
页面渲染时,
dom
元素所采用的 布局模型。可经过box-sizing
进行设置。根据计算宽高的区域可分为:
content-box
(W3C
标准盒模型)border-box
(IE
盒模型)padding-box
(仅Firefox
曾实现,且已在Firefox 50
版本中被删除)盒模型包括
margin
、border
、padding
、content
区别:
content-box
计算时content
不包含 border
与 padding
,而 border-box
的content
则包含 border
与 padding
。
示例:
.box {
width: 200px;
height:100px;
margin:10px;
padding:5px;
border:1px;
box-sizing: ···
}
复制代码
若设置 box-sizing: content-box;
,则box
宽度为: 200 + (10 + 5 + 1) * 2 = 232px;
,content
部分宽度为 200px
;
若设置 box-sizing: border-box;
,则box
宽度为: 200 + 10 * 2 = 220px;
,content
部分宽度为 200 - (10 + 1) * 2 = 178px
;
块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其余元素交互的区域。
常见建立 BFC
方法:
做用:
!important
> 行内样式 > #id
> .class
> tag
> *
> 继承 > 默认
:after
/ <br>
/ clear: both