一些概念(私人笔记)

私人笔记

私人笔记,仅做方便手机阅读使用javascript

私人笔记,仅做方便手机阅读使用css

私人笔记,仅做方便手机阅读使用html

markdown 给文字添加颜色

  1. <font color=red>我是红色</font> => 我是红色前端

  2. $\color{red}{我是红色}$ => \color{red}{我是红色}vue

  3. 使用带!的引用(预览时可能看不到效果)java

    这是一段带红色感叹号的引用 node

http请求头 Content-Type 类型

(待补充···)react

HTTP请求行、请求头、请求体详解webpack

Http中Content-Type的详解ios

常见的媒体格式类型以下:

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的内容格式。

es6新特性

经常使用的有:

默认参数

let、const

模板文字(${})

多行字符串(``)

for···of

解构赋值

箭头函数

Promises

延展操做符(...)

import 和 export

ES6新特性概览

ES6经常使用新特性总结

*ES6这些就够了

数据类型

最新的 ECMAScript 标准定义了 7 种数据类型:

  • 6 种原始类型:

    • Boolean
    • Null
    • Undefined
    • Number
    • String
    • Symbol (ECMAScript 6 新定义)
  • Object

判断数据类型 (typeof vs instanceof)

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
复制代码

值类型 vs 引用类型

除了原始类型,ES 还有引用类型,typeof 识别出来的类型中,只有objectfunction是引用类型,其余都是值类型。

根据 JavaScript 中的变量类型传递方式,又分为值类型和引用类型,值类型变量包括 BooleanStringNumberUndefinedNull引用类型包括了 Object 类的全部,如 DateArrayFunction 等。在参数传递方式上,值类型是按值传递,引用类型是按共享传递。

Ramda 经常使用函数

Ramda文档

函数 做用 示例
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}

react 高阶组件

定义:高阶组件就是一个函数,且该函数接受一个组件做为参数,并返回一个新的组件。

分类:代理方式、继承方式

做用:

  1. 代码复用
  2. 更改 props
  3. 经过 refs 获取组件实例
  4. 抽象 state
  5. 包装组件

高阶组件示例 :

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;
复制代码

react生命周期

生命周期(官方文档)

componentWillMount()componentWillUpdata()componentWillReceiveProps() 在 v16.3 版本后不推荐使用,在 v17 版本计划删除。

componentWillMount()static getDerivedStateFromProps() 取代;componentWillUpdata()getSnapshotBeforeUpdate()取代;componentWillReceiveProps() 应避免使用

来谈谈Reactv16.3新生命周期知识点及遇到的问题

Markdown

安装阶段

在建立组件的实例并将其插入DOM时,将按如下顺序调用这些方法:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

更新

state的更改可能致使更新。从新渲染组件时,将按如下顺序调用这些方法:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

卸载

从DOM中删除组件时调用此方法:

  • componentWillUnmount()

setState()会触发哪些生命周期

static getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()


web前端开发,如何提升页面性能优化?

优化原则和方向

性能优化的原则是以更好的用户体验为标准,具体就是实现下面的 目标

  • 多使用内存、缓存或者其余方法
  • 减小 CPU 和GPU 计算,更快展示

优化的 方向 有两个:

  • 减小页面体积,提高网络加载
  • 优化页面渲染

减小页面体积,提高网络加载

  • 静态资源的压缩合并(JS 代码压缩合并、CSS 代码压缩合并、雪碧图)
  • 静态资源缓存(资源名称加 MD5 戳,好处
  • 使用 CDN 让资源加载更快

优化页面渲染

  • CSS 放前面,JS 放后面
  • 懒加载(图片懒加载、下拉加载更多)
  • 减小DOM 查询,对 DOM 查询作缓存
  • 减小DOM 操做,多个操做尽可能合并在一块儿执行(DocumentFragment)
  • 事件节流
  • 尽早执行操做(DOMContentLoaded)
  • 使用 SSR(server side render,服务端渲染) 后端渲染,数据直接输出到 HTML 中,减小浏览器使用 JS 模板渲染页面 HTML 的时间(适合首屏渲染,利于SEO)

性能优化怎么作

上面提到的都是性能优化的单个点,性能优化项目具体实施起来,应该按照下面步骤推动:

  1. 创建性能数据收集平台,摸底当前性能数据,经过性能打点,将上述整个页面打开过程消耗时间记录下来
  2. 分析耗时较长时间段缘由,寻找优化点,肯定优化目标
  3. 开始优化
  4. 经过数据收集平台记录优化效果
  5. 不断调整优化点和预期目标,循环2~4步骤

性能优化是个长期的事情,不是一蹴而就的,应该本着 先摸底、再分析、后优化 的原则逐步来作。

前端性能优化最佳实践

内容方面:

  1. 减小 HTTP 请求 (Make Fewer HTTP Requests)

  2. 减小 DOM 元素数量 (Reduce the Number of DOM Elements)

  3. 使得 Ajax 可缓存 (Make Ajax Cacheable)

针对CSS:

  1. 把 CSS 放到代码页上端 (Put Stylesheets at the Top)

  2. 从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)

  3. 精简 JavaScript 与 CSS (Minify JavaScript and CSS)

  4. 避免 CSS 表达式 (Avoid CSS Expressions)

针对JavaScript :

  1. 脚本放到 HTML 代码页底部 (Put Scripts at the Bottom)

  2. 从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)

  3. 精简 JavaScript 与 CSS (Minify JavaScript and CSS)

  4. 移除重复脚本 (Remove Duplicate Scripts)

面向图片(Image):

  1. 优化图片

  2. 不要在 HTML 中使用缩放图片

  3. 使用恰当的图片格式

  4. 使用 CSS Sprites 技巧对图片优化

http状态码?

关于http部分状态码

1xx:1开头的是信息状态码

2xx:2开头的是请求成功

3xx:3开头的是重定向

4xx:4开头的是客户端错误

5xx:5开头的是服务器错误


为何使用React与Redux的,它们有什么优点

React(优点、劣势)

优点

  1. React速度很快 :它并不直接对DOM进行操做,引入了一个叫作虚拟DOM的概念,安插在javascript逻辑和实际的DOM之间,性能好。

  2. 跨浏览器兼容 :虚拟DOM帮助咱们解决了跨浏览器问题,它为咱们提供了标准化的API,甚至在IE8中都是没问题的。

  3. 一切都是component :代码更加模块化,重用代码更容易,可维护性高。

  4. 单向数据流 :Flux是一个用于在JavaScript应用中建立单向数据层的架构,它随着React视图库的开发而被Facebook概念化。

  5. 同构、纯粹的javascript :由于搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。

  6. 兼容性好 :好比使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务再也不让人望而生畏。

缺点

  • React自己只是一个V(视图库)而已,因此若是是大型项目想要一套完整的框架的话,也许还须要引入Redux和route相关的东西。

虚拟DOM

虚拟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插入慢。

浅谈React的最大亮点——虚拟DOM

Redux(优点)

Redux、Reflux、Mobx 对比

Redux VS Reflux

  1. Reflux没有reducer的概念。取而代之,和action作互动的的是store。Reflux的功能流程以下:
    Markdown

组件就是用户界面,actions就是组件的动做,store用于执行actions的命令,并返回一个state对象给组件。组件经过state来更新界面。

而Redux的功能流程以下:

Markdown

state就是数据,组件就是数据的呈现形式,action是动做,action是经过reducer来更新state的。

  1. Reflux能够直接调用action的方法,而Redux必须将方法绑定在组件的props上,或者使用props的dispatch方法来执行actions的方法。

Redux VS Mobx

  1. store 与多 store。在 Redux 中,你将全部的 state 都放在一个全局的 store。这个 store对象就是你的单一数据源。另外一方面,多个 reducers 容许你修改不可变的 stateMobx 则相反,它使用多 stores
  2. Redux偏向于函数式编程,️而Mobx偏向于面向对象编程和响应式编程。Redux偏向于函数式编程,因此用的是纯函数,State 不可变。而MobxState 则能够改变。

React 与 Vue 对比

Vue特色

  1. 轻量级的框架

  2. 双向数据绑定 -- 好比你改变一个输入框 Input 标签的值,会自动同步更新到页面上其余绑定该输入框的组件的值

  3. 组件化 -- 页面上小到一个按钮均可以是一个单独的文件.vue,这些小组件直接能够像乐高积木同样经过互相引用而组装起来

  4. 单向响应的数据流

  5. 指令

  6. 插件化

React特色

  1. 声明式设计 -- React采用声明范式,能够轻松描述应用。

  2. 高效 -- React经过对DOM的模拟,最大限度地减小与DOM的交互。

  3. 灵活 --  −React能够与已知的库或框架很好地配合。

  4. JSX -- JSX 是 JavaScript 语法的扩展。React 开发不必定使用 JSX ,但咱们建议使用它。

  5. 组件 -- 经过 React 构建组件,使得代码更加容易获得复用,可以很好的应用在大项目的开发中。

  6. 单向响应的数据流 -- React 实现了单向响应的数据流,从而减小了重复代码,这也是它为何比传统数据绑定更简单。

React 和 Vue 有许多类似之处

  1. 使用 Virtual DOM(虚拟 DOM经过 diff 比对,找到变动节点,从新渲染)

  2. 提供了响应式(Reactive)和组件化(Composable)的视图组件。

  3. 将注意力集中保持在核心库,而将其余功能如路由和全局状态管理交给相关的库。

  4. 使用Prop传递数据,prop 是单向绑定的,当父组件的属性变化时,将传导给子组件,可是不会反过来。子组件不该该直接改变prop的值。

  5. 都提供了路由、状态管理器(React对应的Redux,Vue对应Vuex)等。

  6. 都提供合理的钩子函数,可让开发者定制化地去处理需求。

  7. 在组件开发中都支持mixins的特性。

Vue 与 React 差别

性能上

  1. React 和 Vue 在大部分常见场景下都能提供近似的性能。一般 Vue 会有少许优点,由于 Vue 的 Virtual DOM 实现相对更为轻量一些。

  2. 在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,从新渲染整个组件子树。如要避免没必要要的子组件的重渲染,有相应的处理机制PureComponent(React.Component 与 React.PureComponent)。在 Vue 应用中,组件的依赖是在渲染过程当中自动追踪的,因此系统能精确知晓哪一个组件确实须要被重渲染。

  3. 用 Vue 和 React 开发大多数应用的速度都是足够快的。假如你要开发一个对性能要求比较高的数据可视化或者动画的应用时,你须要了解到下面这点:在开发中,Vue 每秒最高处理 10 帧,而 React 每秒最高处理不到 1 帧。

HTML & CSS

  1. 在 React 中,一切都是 JavaScript。HTML 能够用 JSX 来表达。Vue 的总体思想是拥抱经典的 Web 技术(采用template方式,好比v-on的各类修饰符,在 JSX 中实现对应的功能会须要多得多的代码),事实上 Vue 也提供了render渲染函数,甚至支持 JSX。

  2. 在 React 中,如今的潮流也愈来愈多地将 CSS 也归入到 JavaScript 中来处理(有其优越性,具体不详说),经过依赖引入css模块,而 Vue 可让你在每一个单文件组件中彻底访问 CSS,方便的规定css做用域,也可引入css模块。

其余

  1. 二者另外一个重要差别是,Vue 的路由库和状态管理库都是由官方维护支持且与核心库同步更新的。React 则是选择把这些问题交给社区维护,所以建立了一个更分散的生态系统。但相对的,React 的生态系统相比 Vue 更加繁荣。

  2. 从二者提供的路由、状态管理器等向上扩展来看,Vue、React作得都比较完善,从向下扩展来看,Vue就相似于 jQuery。你只要把以下标签放到页面就能够运行:

    <script src="https://unpkg.com/vue/dist/vue.js"></script>

  3. 本地渲染。ReactNative 能使你用相同的组件模型编写有本地渲染能力的 APP(iOS 和 Android)。能同时跨多平台开发,对开发者是很是棒的。相应地,Vue 和Weex会进行官方合做,Weex 是阿里的跨平台用户界面开发框架,Weex 的 JavaScript 框架运行时用的就是 Vue。这意味着在 Weex 的帮助下,你使用 Vue 语法开发的组件不只仅能够运行在浏览器端,还能被用于开发 iOS 和 Android 上的原生应用。固然在如今,Weex 还在积极发展,成熟度也不能和 ReactNative 相抗衡。

  4. Vue.js在模板中提供了指令,过滤器等,能够很是方便,快捷地操做DOM。


axios

axios 是目前最经常使用的 http 请求库,能够用于浏览器和 node.js

Axios 的主要特性包括:

基于 Promise

  • 支持浏览器和 node.js
  • 可添加拦截器从而转换请求和响应数据
  • 请求能够取消(axios.CancelToken)
  • 自动转换 JSON 数据
  • 客户端支持防范 XSRF (跨站请求伪造)
  • 支持各主流浏览器及 IE8+

手写 XMLHttpRequest 不借助任何库

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 这几种,比较经常使用的有如下几种:

  • 200 正常
  • 3xx
    • 301 永久重定向。如http://xxx.com这个 GET 请求(最后没有/),就会被301到http://xxx.com/(最后是/)
    • 302 临时重定向。临时的,不是永久的
    • 304 资源找到可是不符合请求条件,不会返回任何主体。如发送 GET 请求时,head 中有If-Modified-Since: xxx(要求返回更新时间是xxx时间以后的资源),若是此时服务器 端资源未更新,则会返回304,即不符合要求
  • 404 找不到资源
  • 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>
复制代码

后端添加白名单


React建立组件的三种方式及其区别


token 与 session

为何要有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) :我发给你一张加密的身份证,之后你只要出示这张卡片,我就知道你必定是本身人。


转Boolean

在条件判断时,除了 undefinednullfalseNaN''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
复制代码

关于[]{}须要注意的点:

  1. 空数组[]和空对象{}都是object类型,所以直接用于if判断条件时就会被转化为 true
  2. 任意值与布尔值比较,都会将两边的值转化为Number
  3. 若是将空数组[]与布尔值false比较,false转化为0,而空数组[]也转化为0,所以[] == false的判断获得true
  4. 若是将空对象{}与布尔值false比较,false转化为0,而空对象{}转化为NaN,因为NaN与任何数都不相等,所以{} == false的判断获得false
  5. 引用类型之间的比较是内存地址的比较,不须要进行隐式转换,因此 [] == [] //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
复制代码

可是该方法也是有局限性的:

  • 会忽略 undefined
  • 不能序列化函数
  • 不能解决循环引用的对象

$.extend([deep],target,Object1,Object2)

deep为布尔值,默认不传为false(若是传值只能传true),为true时表示深拷贝

或者借助Ramda函数库,Ramda.clone()


关于Promise

Promise

Promise 是异步编程的一种解决方案,用于抽象异步处理对象。能够避免回调地狱。

Promise 对象有如下两个特色:

  • 对象的状态不受外界影响。Promise对象表明一个异步操做,有三种状态:pending(进行中)fulfilled(已成功)rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态,任何其余操做都没法改变这个状态。

  • 一旦状态改变,就不会再变,任什么时候候均可以获得这个结果。Promise对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。只要这两种状况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。若是改变已经发生了,你再对 Promise 对象添加回调函数,也会当即获得这个结果。

有了Promise对象,就能够将异步操做以同步操做的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操做更加容易。

await & async

使用注意点:

  1. 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);
      });
    }
    复制代码
  2. 多个await命令后面的异步操做,若是不存在继发关系,最好让它们同时触发。

    let foo = await getFoo();
    let bar = await getBar();
    复制代码

    上面代码中,getFoogetBar是两个独立的异步操做(即互不依赖),被写成继发关系。这样比较耗时,由于只有getFoo完成之后,才会执行getBar,彻底可让它们同时触发。

    // 写法一
    let [foo, bar] = await Promise.all([getFoo(), getBar()]);
    
    // 写法二
    let fooPromise = getFoo();
    let barPromise = getBar();
    let foo = await fooPromise;
    let bar = await barPromise;
    
    复制代码

上面两种写法,getFoogetBar 都是同时触发,这样就会缩短程序的执行时间。

  1. await命令只能用在async函数之中,若是用在普通函数,就会报错。

JS异步机制

主线程、执行栈、任务队列:

  • 主线程不断读取执行栈中的同步事件,直到执行栈空
  • 异步任务结束放入任务队列,执行栈空时主线程读取任务队列
  • 任务队列读取完毕,回到步骤1

带你完全弄懂Event Loop

event loop是一个执行模型,在不一样的地方有不一样的实现。

  • js是单线程的

  • js进程分为宏任务与微任务,优先执行微任务,后执行宏任务(其实不许确,页面加载先执行一个<script>标签,属于宏任务),若微任务执行过程当中产生了新的微任务,则将改新产生的微任务置于队列底部(例如队列为A -> B -> C,执行A过程当中产生了微任务A1,则将A1置于C后面A -> B -> C -> A1)

  • 常见的微任务:promiseprocess.nextTick (Node独有,以及其余不太常见的本次不讨论)

  • 常见的宏任务:setTimeoutsetIntervalsetImmediate (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>)
  }
复制代码

缓存

在介绍缓存的时候,咱们习惯将缓存分为 强缓存协商缓存 两种。二者的主要区别是使用本地缓存的时候,是否须要向服务器验证本地缓存是否依旧有效。

前端缓存最佳实践


三次握手四次挥手

以女友为例讲解 TCP/IP 三次握手与四次挥手


实现超出整数存储范围的两个大整数相加function add(a,b)。注意a和b以及函数的返回值都是字符串。

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('');
    
}

复制代码

节流与防抖

JavaScript函数节流和函数防抖之间的区别

↑上文中节流防抖效果demo

函数节流 是指 必定时间内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


浏览器从加载页面到渲染页面的过程(输入URL后发生了什么)

加载过程

要点以下:

  • 浏览器根据 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
  • DOMCSSOM 整合造成 RenderTree
  • 根据 RenderTree 开始渲染和展现
  • 遇到<script>时,会执行并阻塞渲染

重绘与回流

当元素的样式发生变化时,浏览器须要触发更新,从新绘制元素。这个过程当中,有两种类型的操做,即重绘与回流。

  • 重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时因为只须要UI层面的从新像素绘制,所以 损耗较少

  • 回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会从新渲染页面,称为回流。此时,浏览器须要从新通过计算,计算后还须要从新页面布局,所以是较重的操做。会触发回流的操做:

    • 页面初次渲染

    • 浏览器窗口大小改变

    • 元素尺寸、位置、内容发生改变

    • 元素字体大小变化

    • 添加或者删除可见的 dom 元素

    • 激活 CSS 伪类(例如::hover)

    • 查询某些属性或调用某些方法:

      • clientWidth、clientHeight、clientTop、clientLeft
      • offsetWidth、offsetHeight、offsetTop、offsetLeft
      • scrollWidth、scrollHeight、scrollTop、scrollLeft
      • getComputedStyle()
      • getBoundingClientRect()
      • scrollTo()

回流一定触发重绘,重绘不必定触发回流。重绘的开销较小,回流的代价较高


Web 安全

题目:前端常见的安全问题有哪些?

Web 前端的安全问题,能回答出下文的两个问题,这个题目就能基本过关了。开始以前,先说一个最简单的攻击方式 —— SQL 注入。

上学的时候就知道有一个「SQL注入」的攻击方式。例如作一个系统的登陆界面,输入用户名和密码,提交以后,后端直接拿到数据就拼接 SQL 语句去查询数据库。若是在输入时进行了恶意的 SQL 拼装,那么最后生成的 SQL 就会有问题。可是如今稍微大型一点的系统,都不会这么作,从提交登陆信息到最后拿到受权,要通过层层的验证。所以,SQL 注入都只出如今比较低端小型的系统上。

XSS(Cross Site Scripting,跨站脚本攻击)

这是前端最多见的攻击方式,不少大型网站(如 Facebook)都被 XSS 攻击过。

举一个例子,我在一个博客网站正常发表一篇文章,输入汉字、英文和图片,彻底没有问题。可是若是我写的是恶意的 JS 脚本,例如获取到document.cookie而后传输到本身的服务器上,那我这篇博客的每一次浏览都会执行这个脚本,都会把访客 cookie 中的信息偷偷传递到个人服务器上来。

其实原理上就是黑客经过某种方式(发布文章、发布评论等)将一段特定的 JS 代码隐蔽地输入进去。而后别人再看这篇文章或者评论时,以前注入的这段 JS 代码就执行了。JS 代码一旦执行,那可就不受控制了,由于它跟网页原有的 JS 有一样的权限,例如能够获取 server 端数据、能够获取 cookie 等。因而,攻击就这样发生了。

XSS的危害

XSS 的危害至关大,若是页面能够随意执行别人不安全的 JS 代码,轻则会让页面错乱、功能缺失,重则会形成用户的信息泄露。

好比早些年社交网站常常爆出 XSS 蠕虫,经过发布的文章内插入 JS,用户访问了感染不安全 JS 注入的文章,会自动从新发布新的文章,这样的文章会经过推荐系统进入到每一个用户的文章列表面前,很快就会形成大规模的感染。

还有利用获取 cookie 的方式,将 cookie 传入入侵者的服务器上,入侵者就能够模拟 cookie 登陆网站,对用户的信息进行篡改。

XSS的预防

那么如何预防 XSS 攻击呢?—— 最根本的方式,就是对用户输入的内容进行验证和替换,须要替换的字符有:

& 替换为:&amp;
< 替换为:&lt;
> 替换为:&gt;
” 替换为:&quot;
‘ 替换为:&#x27;
/ 替换为:&#x2f;
复制代码

替换了这些字符以后,黑客输入的攻击代码就会失效,XSS 攻击将不会轻易发生。

除此以外,还能够经过对 cookie 进行较强的控制,好比对敏感的 cookie 增长http-only限制,让 JS 获取不到 cookie 的内容。

CSRF(Cross-site request forgery,跨站请求伪造)

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 原理示意图:

Markdown

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.comcookie ,这是发生 CSRF 攻击的理论基础

预防 CSRF 就是加入各个层级的权限验证,例如如今的购物网站,只要涉及现金交易,确定要输入密码或者指纹才行。除此以外,敏感的接口使用POST请求而不是GET 也是很重要的。


何为构建工具

“构建”也可理解为“编译”,就是将开发环境的代码转换成运行环境代码的过程。开发环境的代码是为了更好地阅读,而运行环境的代码是为了更快地执行,二者目的不同,所以代码形式也不同。例如,开发环境写的 JS 代码,要经过混淆压缩以后才能放在线上运行,由于这样代码体积更小,并且对代码执行不会有任何影响。总结一下须要构建工具处理的几种状况:

  • 处理模块化:CSS 和 JS 的模块化语法,目前都没法被浏览器兼容。所以,开发环境可使用既定的模块化语法,可是须要构建工具将模块化语法编译为浏览器可识别形式。例如,使用 webpack、Rollup 等处理 JS 模块化。
  • 编译语法:编写 CSS 时使用 Less、Sass,编写 JS 时使用 ES六、TypeScript 等。这些标准目前也都没法被浏览器兼容,所以须要构建工具编译,例如使用 Babel 编译 ES6 语法。
  • 代码压缩:将 CSS、JS 代码混淆压缩,为了让代码体积更小,加载更快。

进程与线程区别?JS 单线程带来的好处?

本质上来讲,两个名词都是 CPU工做时间片的一个描述

进程 描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来讲就表明了一个程序。线程 是进程中的更小单位,描述了执行一段指令所需的时间。

把这些概念拿到浏览器中来讲,当你打开一个 Tab 页时,其实就是建立了一个进程,一个进程中能够有多个线程,好比渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是建立了一个线程,当请求结束后,该线程可能就会被销毁。

若是在 JS 执行的时候 UI 线程还在工做,就可能致使不能安全的渲染 UI。这其实也是一个单线程的好处,得益于 JS 是单线程运行的,能够达到节省内存,节约上下文切换时间,没有锁的问题的好处。

执行栈

能够把执行栈认为是一个 存储函数调用的栈结构 ,遵循先进后出的原则。

instanceof 的原理是什么?

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 === 构造函数
复制代码

关系图以下:

Markdown

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

代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。

在MDN上对于 Proxy 的解释是:

Proxy 对象用于定义基本操做的自定义行为(如属性查找,赋值,枚举,函数调用等)

简单来讲: Proxy 对象就是可让你去对JavaScript中的一切合法对象的基本操做进行自定义。而后用你自定义的操做去覆盖其对象的基本操做。也就是当一个对象去执行一个基本操做时,其执行的过程和结果是你自定义的,而不是对象的。

Proxy的做用 :

对于代理模式 Proxy 的做用主要体如今三个方面:

  1. 拦截和监视外部对对象的访问

  2. 下降函数或类的复杂度

  3. 在复杂操做前对操做进行校验或对所需资源进行管理

路由原理

前端路由原理?两种实现方式有什么区别?

前端路由实现起来其实很简单,本质就是 监听 URL 的变化,而后匹配路由规则,显示相应的页面,而且无须刷新页面 。目前前端使用的路由就只有两种实现方式:

  • Hash 模式
  • History 模式

两种模式对比

  • Hash 模式只能够更改 # 后面的内容,History 模式能够经过 API 设置任意的同源 URL
  • History 模式能够经过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
  • Hash 模式无需后端配置,而且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端须要配置 index.html 页面用于匹配不到静态资源的时候

Babel

Babel 是一个 JavaScript 编译器

Babel 是一个工具链,主要用于在旧的浏览器或环境中将 ECMAScript 2015+ 代码转换为向后兼容版本的 JavaScript 代码:

  • 转换语法
  • Polyfill 实现目标环境中缺乏的功能 (经过 @babel/polyfill)
  • 源代码转换 (codemods)

特性

  • Babel 能够转换 JSX 语法
  • Babel 能够删除类型注释!请注意 Babel 不会进行类型检查;你仍然能够安装使用 Flow 或者 TypeScript 来进行类型检查。
  • 可插拔。Babel 是用插件构建的。你可使用现有插件编写本身的转换管道或编写本身的插件。经过使用或建立 preset 轻松使用一组插件。
  • 可调试。支持 Source map ,所以你能够轻松调试编译过的代码。
  • 压缩性。Babel 尝试使用尽量少的代码而不依赖于庞大的运行时环境。

受控组件与非受控组件

受控组件

组件状态由state控制。假设咱们如今有一个表单,表单中有一个input标签,input的value值必须是咱们设置在constructor构造函数的state中的值,而后,经过onChange触发事件来改变state中保存的value值,这样造成一个循环的回路影响。也能够说是React负责渲染表单的组件仍然控制用户后续输入时所发生的变化。

非受控组件

组件状态不禁state控制。其值能够经过refs获取。常见的有input(不添加valuedefaultvalueonChange()等)

非受控组件(codepen)

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内核

js快速排序

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 版本中被删除)

盒模型包括marginborderpaddingcontent

区别:

content-box 计算时content 不包含 borderpadding,而 border-boxcontent则包含 borderpadding

示例:

.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;

BFC

块格式化上下文(Block Formatting Context,BFC) 是Web页面的可视化CSS渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其余元素交互的区域。

常见建立 BFC 方法:

  • 浮动元素(元素的 float 不是 none)
  • 绝对定位元素(元素的 position 为 absolute 或 fixed)
  • 行内块元素(元素的 display 为 inline-block)
  • overflow 值不为 visible 的块元素
  • display 值为 flow-root 的元素
  • 弹性元素(display为 flex 或 inline-flex元素的直接子元素)
  • 网格元素(display为 grid 或 inline-grid 元素的直接子元素

做用:

  • 让浮动内容和周围的内容等高
  • 防止外边距塌陷

选择器优先级

!important > 行内样式 > #id > .class > tag > * > 继承 > 默认

去除浮动影响,防止父级高度塌陷

  1. 经过增长尾元素清除浮动:after / <br> / clear: both
  2. 建立父级 BFC
  3. 父级设置高度

link 与 @import 的区别

  • link功能较多,能够定义 RSS,定义 Rel 等做用,而@import只能用于加载 css
  • 当解析到link时,页面会同步加载所引的 css,而@import所引用的 css 会等到页面加载完才被加载
  • @import须要 IE5 以上才能使用 link可使用 js 动态引入,@import不行
相关文章
相关标签/搜索