共计 13786 个字符,预计需要花费 35 分钟才能阅读完成。
Call, bind, apply 实现
// call
Function.prototype.myCall = function (context) {context = context ? Object(context) : window
context.fn = this;
let args = [...arguments].slice(1);
const result = context.fn(...args);
delete context.fn;
return result;
}
// apply
Function.prototype.myApply = function (context) {context = context ? Object(context) : window;
context.fn = this;
let args = [...arguments][1];
let result;
if (args.length === 0) {result = context.fn();
} else {result = context.fn(args);
}
delete context.fn;
return result;
}
// bind
Function.prototype.myBind = function (context) {
let self = this;
let args = [...arguments].slice(1);
return function() {let newArgs = [...arguments];
return self.apply(context, args.concat(newArgs));
}
}
原型与原型链
每一个 JavaScript 对象 (null 除外) 在创立的时候会关联另一个对象,这个对象就是原型,每一个对象都会从原型 ” 继承 ” 属性。
每一个 JavaScript 对象 (除了 null) 都具备的一个属性,叫__proto__
,这个属性会指向该对象的原型。
实例对象和构造函数都能够指向原型, 原型能够指向构造函数,不能指向实例(因为能够有多个实例)。
原型有两个属性,constructor 和 __proto__
。
JavaScript 中所有的对象都是由它的原型对象继承而来。而原型对象本身也是一个对象,它也有本人的原型对象,这样层层上溯,就造成了一个相似链表的构造,这就是 原型链。
function Person() {}
var person = new Person();
// 实例原型 === 构造函数原型
person.__proto__ === Person.prototype // true
// 原型构造函数 === 构造函数
Person.prototype.constructor === Person // true
react diff
- React 通过制订大胆的 diff 策略,将 O(n3) 复杂度的问题转换成 O(n) 复杂度的问题;
- React 通过 分层求异 的策略,对 tree diff 进行算法优化;
对树进行分层比拟,两棵树只会对同一档次的节点进行比拟。
-
React 通过 雷同类生成类似树形构造,不同类生成不同树形构造 的策略,对 component diff 进行算法优化;
- 如果是同一类型的组件,依照原策略持续比拟 virtual DOM tree。
- 如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
- 对于同一类型的组件,有可能其 Virtual DOM 没有任何变动,如果可能确切的晓得这点那能够节俭大量的 diff 运算工夫,因而 React 容许用户通过 shouldComponentUpdate() 来判断该组件是否须要进行 diff。
- React 通过 设置惟一 key的策略,对 element diff 进行算法优化;
- 倡议,在开发组件时,保持稳定的 DOM 构造会有助于性能的晋升;
对象办法
对象遍历办法总结:
for...in
:遍历对象本身,蕴含继承,可枚举,不含 Symbol 的属性。Object.keys(obj)
:遍历对象本身, 不含继承,可枚举,不含 Symbol 的属性。【values,entries】Object.getOwnPropertyNames(obj)
:遍历对象本身, 不含继承, 不含 Symbol 的属性, 不论是否可枚举Object.getOwnPropertySymbols(obj)
: 遍历对象本身, 不含继承, 所有 Symbol 的属性, 不论是否可枚举Reflect.ownKeys(obj)
: 遍历对象本身,不含继承,所有键名,不论是否 Symbol 和可枚举。对象其余办法:
JSON.stringify()
:只串行化对象本身,不含继承,可枚举,不含 Symbol 属性。【function,undefined, Symbol 会失落,set、map 会解决成空对象】Object.assign()
:只拷贝对象本身,不含继承,可枚举属性, 不论是否是 Symbol。【全副数据类型属性值】
办法 | 本身属性 | 继承属性 | 可枚举属性 | Symbol 属性 |
---|---|---|---|---|
for...in.. |
是 | 是 | 必须 | 否 |
Object.keys() |
是 | 否 | 必须 | 否 |
Object.getOwnPropertyNames(obj) |
是 | 否 | 非必须 | 否 |
Object.getOwnPropertySymbols(obj) |
是 | 否 | 非必须 | 是 |
Reflect.ownKeys(obj) |
是 | 否 | 非必须 | 非必须 |
JSON.stringify() |
是 | 否 | 必须 | 否 |
Object.assign() |
是 | 否 | 必须 | 非必须 |
加载脚本 <script>
默认状况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到 <script>
标签就会停下来,等到执行完脚本,再持续向下渲染。如果是内部脚本,还必须退出脚本下载的工夫。
异步加载脚本办法:defer
与async
。
defer
与 async
的区别是:defer
要等到整个页面在内存中失常渲染完结(DOM 构造齐全生成,以及其余脚本执行实现),才会执行;async
一旦下载完,渲染引擎就会中断渲染,执行这个脚本当前,再持续渲染。一句话,defer
是“渲染完再执行”,async
是“下载完就执行”。另外,如果有多个 defer
脚本,会依照它们在页面呈现的程序加载,而多个 async
脚本是不能保障加载程序的。
浏览器对于带有 type="module"
的<script>
,都是异步加载,不会造成梗塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于关上了 <script>
标签的 defer
属性。
ES6 模块与 CommonJS 模块的差别
- CommonJS 模块输入的是一个值的拷贝,ES6 模块输入的是值的援用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输入接口。
因为 CommonJS 加载的是一个对象(即
module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种动态定义,在代码动态解析阶段就会生成。 - CommonJS 模块的
require()
是同步加载模块,ES6 模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。
回流 Reflow 与重绘 Repaint
回流:元素的大小或者地位产生了变动,触发了从新布局,导致渲染树从新计算布局和渲染。页面第一次加载的时候,至多产生一次回流。
- 增加或删除可见的 DOM 元素;
- 元素的地位发生变化;
- 元素的尺寸发生变化;
- 内容发生变化(比方文本变动或图片被另一个不同尺寸的图片所代替);
- 页面一开始渲染的时候(这个无奈防止);
- 浏览器的窗口尺寸变动,因为回流是依据视口的大小来计算元素的地位和大小的;
重绘:元素的外观,格调扭转,而不会影响布局(不蕴含宽高、大小、地位等不变)
- 如:outline, visibility, color, background-color…… 等
Reflow 的老本比 Repaint 高得多的多。DOM Tree 里的每个结点都会有 reflow 办法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。。回流肯定会触发重绘,而重绘不肯定会回流
缩小重绘与回流
-
CSS
- 应用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发回流 - 防止应用
table
布局,可能很小的一个小改变会造成整个table
的从新布局。 - 防止设置多层内联款式 ,CSS 选择符 从右往左 匹配查找,防止节点层级过多。
- 将动画成果利用到
position
属性为absolute
或fixed
的元素上,防止影响其余元素的布局,这样只是一个重绘,而不是回流,同时,管制动画速度能够抉择requestAnimationFrame
- 防止应用
CSS
表达式,可能会引发回流。 - 将频繁重绘或者回流的节点设置为图层 ,图层可能阻止该节点的渲染行为影响别的节点,例如
will-change
、video
、iframe
等标签,浏览器会主动将该节点变为图层。 - CSS3 硬件加速(GPU 减速),应用 css3 硬件加速,能够让
transform
、opacity
、filters
这些动画不会引起回流重绘。然而对于动画的其它属性,比方background-color
这些,还是会引起回流重绘的,不过它还是能够晋升这些动画的性能。
- 应用
-
JavaScript
- 防止频繁操作款式 ,最好一次性重写
style
属性,或者将款式列表定义为class
并一次性更改class
属性。 - 防止频繁操作
DOM
,创立一个documentFragment
,在它下面利用所有DOM 操作
,最初再把它增加到文档中。 - 防止频繁读取会引发回流 / 重绘的属性,如果的确须要屡次应用,就用一个变量缓存起来。
- 防止频繁操作款式 ,最好一次性重写
CSS3 硬件加速(GPU 减速)
CSS3 硬件加速又叫做 GPU 减速,是利用 GPU 进行渲染,缩小 CPU 操作的一种优化计划。
render tree -> 渲染元素 -> 图层 -> GPU 渲染 -> 浏览器复合图层 -> 生成最终的屏幕图像。
浏览器在获取 render tree 后,渲染树中蕴含了大量的渲染元素,每一个渲染元素会被分到一个个图层中,每个图层又会被加载到 GPU 造成渲染纹理。GPU 中 transform 是不会触发 repaint,最终这些应用 transform 的图层都会由独立的合成器过程进行解决。
CSS3 触发硬件加速的属性:
- transform
- opacity
- filter
- will-change
http 申请办法
HTTP1.0 定义了三种申请办法:GET, POST 和 HEAD 办法。
HTTP1.1 新增了五种申请办法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 办法
- OPTIONS:即预检申请,可用于检测服务器容许的 http 办法。当发动跨域申请时,因为平安起因,触发肯定条件时浏览器会在正式申请之前 主动 先发动 OPTIONS 申请,即CORS 预检申请,服务器若承受该跨域申请,浏览器才持续发动正式申请。
- HEAD: 向服务器索与 GET 申请相一致的响应,只不过响应体将不会被返回,用于获取报头。
- GET:向特定的资源发出请求。留神:GET 办法不该当被用于产生“副作用”的操作中
- POST:向指定资源提交数据进行解决申请(例如提交表单或者上传文件)。数据被蕴含在申请体中。POST 申请可能会导致新的资源的建设和 / 或已有资源的批改。
- PUT:向指定资源地位上传其最新内容
- DELETE:申请服务器删除 Request-URL 所标识的资源
- TRACE:回显服务器收到的申请,次要用于测试或诊断
- CONNECT:HTTP/1.1 协定中预留给可能将连贯改为管道形式的代理服务器
js 判断数据类型
- typeof 操作符
- 对于根本类型,除 null 以外,均能够返回正确的后果。
- 对于援用类型,除 function 以外,一律返回 object 类型。
- 对于 null,返回 object 类型。
- 对于 function 返回 function 类型。
- instanceof:用来判断 A 是否为 B 的实例,检测的是原型。instanceof 只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型。
instanceof 次要的实现原理就是只有左边变量的 prototype 在右边变量的原型链上即可。
- constructor
- null 和 undefined 是有效的对象,不会有 constructor 存在的
- 函数的 constructor 是不稳固的,这个次要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 援用会失落,constructor 会默认为 Object。为了标准开发,在重写对象原型时个别都须要从新给 constructor 赋值。
为什么变成了 Object?
因为 prototype 被从新赋值的是一个 {},{} 是 new Object() 的字面量,因而 new Object() 会将 Object 原型上的 constructor 传递给 {},也就是 Object 自身。
- toString
toString() 是 Object 的原型办法,调用该办法,默认返回以后对象的 [[Class]]。这是一个外部属性,其格局为 [object Xxx],其中 Xxx 就是对象的类型。
浏览器事件模型
DOM 事件流(event flow)存在三个阶段:事件捕捉阶段、处于指标阶段、事件冒泡阶段。
// useCapture:true, 即采纳事件捕捉形式
window.addEventListener("click", function(e){console.log("window 捕捉");
}, true);
// useCapture:false【默认为 false】,即采纳事件冒泡形式
window.addEventListener("click", function(e){console.log("window 冒泡");
}, false);
指标元素(被点击的元素)绑定的事件都会产生在指标阶段,在绑定捕捉代码之前写了绑定的冒泡阶段的代码,所以在指标元素上就不会恪守先产生捕捉后产生冒泡这一规定,而是先绑定的事件先产生。
不是指标元素,它下面绑定的事件会恪守先产生捕捉后产生冒泡的规定。
e.stopPropagation()
:阻止事件流传。不仅能够阻止事件在冒泡阶段的流传,还能阻止事件在捕捉阶段的流传。
e.preventDefault()
: 阻止事件的默认行为。默认行为是指:点击 a 标签就转跳到其余页面、拖拽一个图片到浏览器会主动关上、点击表单的提交按钮会提交表单等
http 缓存: 强制缓存和协商缓存
良好的缓存策略能够升高资源的反复加载进步网页的整体加载速度。缓存原理:
- 浏览器在加载资源时,依据申请头的
expires
和cache-control
判断是否命中强缓存,是则间接从缓存读取资源,不会发申请到服务器。 - 如果没有命中强缓存,浏览器会发送一个申请到服务器,通过
last-modified
和etag
验证是否命中协商缓存。当向服务端发动缓存校验的申请时,服务端会返回200
ok 示意返回失常的后果或者304
Not Modified(不返回 body)示意浏览器能够应用本地缓存文件。304 的响应头也能够同时更新缓存文档的过期工夫 - 如果后面两者都没有命中,间接从服务器加载资源。
强缓存通过 Expires
和Cache-Control
实现。
协商缓存是利用的是 【Last-Modified,If-Modified-Since】
和【ETag、If-None-Match】
这两对 Header 来治理的。
Expires
Expires 是 http1.0 提出的一个示意资源过期工夫的 header,它是一个相对工夫,由服务器返回。Expires 受限于本地工夫,如果批改了本地工夫,可能会造成缓存生效。Expires: Wed, 11 May 2018 07:20:00 GMT
Cache-Control
Cache-Control 呈现于 HTTP / 1.1,优先级高于 Expires , 示意的是绝对工夫。
no-store:没有缓存。缓存中不得存储任何对于客户端申请和服务端响应的内容。每次由客户端发动的申请都会下载残缺的响应内容。no-cache: 缓存但从新验证。每次有申请收回时,缓存会将此申请发到服务器(译者注:该申请应该会带有与本地缓存相干的验证字段),服务器端会验证申请中所形容的缓存是否过期,若未过期(返回 304),则缓存才应用本地缓存正本。private:只容许客户端浏览器缓存。public: 容许所有用户缓存。例如两头代理、CDN 等
max-age=<seconds>:示意资源可能被缓存的最大工夫。绝对 Expires 而言,max-age 是间隔申请发动的工夫的秒数。针对利用中那些不会扭转的文件,通常能够手动设置肯定的时长以保障缓存无效,例如图片、css、js 等动态资源。must-revalidate: 触发缓存验证。验证它的状态,已过期的缓存将不被应用
Last-Modified,If-Modified-Since
Last-Modifie 示意本地文件最初批改日期,浏览器会在 request header 加 If-Modified-Since(上次返回的 Last-Modified 的值),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
然而如果在本地关上缓存文件,就会造成 Last-Modified 被批改,所以在 HTTP / 1.1 呈现了 ETag。
ETag、If-None-Match
Etag
就像一个指纹,资源变动都会导致 ETag 变动,跟最初批改工夫没有关系,ETag
能够保障每一个资源是惟一的。
If-None-Match
的 header 会将上次返回的 Etag
发送给服务器,询问该资源的 Etag
是否有更新,有变动就会发送新的资源回来。
ETag
的优先级比 Last-Modified
更高。
具体为什么要用ETag
,次要出于上面几种状况思考:
- 一些文件兴许会周期性的更改,然而他的内容并不扭转(仅仅扭转的批改工夫),这个时候咱们并不心愿客户端认为这个文件被批改了,而从新 GET;
- 某些文件批改十分频繁,比方在秒以下的工夫内进行批改,(比方说 1s 内批改了 N 次),If-Modified-Since 能查看到的粒度是 s 级的,这种批改无奈判断(或者说 UNIX 记录 MTIME 只能准确到秒);
- 某些服务器不能准确的失去文件的最初批改工夫。
防抖和节流
防抖:当你在频繁调用办法时,并不会执行,只有当你在指定距离内没有再调用,才会执行函数。
节流:在一个单位工夫内,只能触发一次函数。如果这个单位工夫内触发屡次函数,只有一次失效。
// 防抖
function debounce(fn, wait) {
let time = null;
return (function() {
const context = this;
const args = arguments;
if (time) {clearTimeout(time);
time = null;
}
time = setTimeout(() => {fn.call(context, args);
}, wait);
});
}
// 节流
function throttle(fn, wait) {
let lastTime;
return (function() {
const context = this;
const args = arguments;
let nowTime = + new Date();
if (nowTime > lastTime + wait || !lastTime) {fn.call(context, args);
lastTime = nowTime;
}
}
);
}
大小单位区别
px:像素。
em:参考物是父元素的 font-size,具备继承的特点。如果本身定义了 font-size 按本身来计算,整个页面内 1em 不是一个固定的值。
rem:绝对于根元素 html 的 font-size 计算,不会像 em 那样,依赖于父元素的字体大小,而造成凌乱。
vw:视窗宽度,1vw 等于视窗宽度的 1%。
vh:视窗高度,1vh 等于视窗高度的 1%。
vm:min(vw, vh)。
%:是绝对于父元素的大小设定的比率,position:absolute;
的元素是绝对于曾经定位的父元素,position:fixed;
的元素是绝对可视窗口
- 浏览器默认字体是 16px, body 设置 font-size:62.5%, 那么 1rem =62.5% * 16=10px。
- 谷歌浏览器强制最小字体为 12 号,即便设置成 10px 最终都会显示成 12px,当把 html 的 font-size 设置成 10px, 子节点 rem 的计算还是以 12px 为基准。
Box-sizing
content-box
:这是默认状况,整个元素的高度和宽度就是元素内容border-box
:这种状况下,你设置的width
和height
属性值就是针对整个元素,包含了 border,padding,和元素内容。
函数申明和函数表达式
// 函数申明
function wscat(type){return 'wscat';}
// 函数表达式
var oaoafly = function(type){return "oaoafly";}
- JavaScript 解释器中存在一种变量申明被晋升的机制,也就是说函数申明会被晋升到作用域的最后面,即便写代码的时候是写在最初面,也还是会被晋升至最后面。
- 用函数表达式创立的函数是在运行时进行赋值,且要等到表达式赋值实现后能力调用
函数申明 在 JS解析时 进行函数晋升,因而在同一个作用域内,不论函数申明在哪里定义,该函数都能够进行调用。而 函数表达式 的值是在 JS运行时 确定,并且在表达式赋值实现后,该函数能力调用。这个渺小的区别,可能会导致 JS 代码呈现意想不到的 bug,让你陷入莫名的陷阱中。
事件循环 EventLoop
JavaScript 是一个单线程的脚本语言。
所有同步工作都在主线程上执行,造成一个执行栈 (Execution Context Stack);而异步工作会被搁置到 Task Table(异步解决模块),当异步工作有了运行后果,就将注册的回调函数移入工作队列(两种队列)。一旦执行栈中的所有同步工作执行结束,引擎就会读取工作队列,而后将工作队列中的第一个工作取出放到执行栈中运行。(所有会进入的异步都是指的事件回调中的那局部代码)
只有主线程空了,就会去读取工作队列
,该过程一直反复,这就是所谓的 事件循环
。
宏工作和微工作
宏工作会进入一个队列,微工作会进入到另一个队列,且微工作要优于宏工作执行。
- 宏工作:script(整体代码)、setTimeout、setInterval、I/O、事件、postMessage、MessageChannel、setImmediate (Node.js)
- 微工作:Promise.then、MutaionObserver、process.nextTick (Node.js)
宏工作会进入一个队列,而微工作会进入到另一个不同的队列,且微工作要优于宏工作执行。
Promise
1. Promise.all: 全副 fulfilled, 才进入 then, 否则 catch
2. Promise.race: 任一个返回,不论是 fulfilled 还是 rejected, 进入相应的 then/catch
3. Promise.allSettled: 全副返回,不论是 fulfilled 还是 rejected, 进入 then
4. Promise.any: 任一个返回 fulfilled, 就进入 then, 否则 catch
跨域
为什么跨域?因为浏览器同源策略。所谓同源是指 ” 协定 + 域名 + 端口 ” 三者雷同,即使两个不同的域名指向同一个 ip 地址,也非同源。浏览器引入同源策略次要是为了避免 XSS,CSRF 攻打。
同源策略的具体表现:
- http 申请不能向不同源的服务器发动 HTTP 申请
- 非同源的网页不能共享 Cookie、LocalStorage、IndexDB
- 禁止对不同源页面的 DOM 进行操作。次要场景是 iframe 跨域的状况, 不同域名的 iframe 限度互相拜访
解决方案:
- JSONP
实现原理:<script>
标签不受浏览器同源策略的影响, 容许跨域援用资源。
实现形式: 动态创建 script 标签, 利用 src 属性进行跨域,通过回调函数解决申请后果。
长处: 兼容性好
毛病:
只能反对 GET 申请, JSONP 在调用失败时不会返回各种 HTTP 状态码
安全性。如果提供 JSONP 的服务被人管制, 那么所有调用这个 JSONP 的网站都存在破绽。
- CORS
跨域资源共享(CORS) 是一种机制,它应用额定的 HTTP 头来通知浏览器 让运行在一个 origin (domain) 上的 Web 利用被准许拜访来自不同源服务器上的指定的资源
虚构 dom 原理
Virtual DOM 是对 DOM 的形象, 实质上是 JavaScript 对象, 这个对象就是更加轻量级的对 DOM 的形容.
箭头函数与一般函数区别
- 语法更加简洁、清晰
- 不绑定 this,会捕捉其所在的上下文的 this 值,作为本人的 this 值
- 箭头函数继承而来的 this 指向永远不变
- .call()/.apply()/.bind()无奈扭转箭头函数中 this 的指向
- 不能作为构造函数应用,因为:
- 没有本人的 this,无奈调用 call,apply。
- 没有 prototype 属性,而 new 命令在执行时须要将构造函数的 prototype 赋值给新的对象的
__prpto__
- 没有本人的 arguments,在箭头函数中拜访
arguments
实际上取得的是外层部分(函数)执行环境中的值。如果要用,能够用 rest 参数代替。 - 没有原型 prototype, 指向 undefined
- 不能用作 Generator 函数,不能应用 yeild 关键字
new
new
关键字会进行如下的操作:
- 创立一个空的简略 JavaScript 对象(即
{}
); - 链接该对象(设置该对象的
__proto__
)到结构器函数的原型; - 将新创建的对象作为
this
的上下文; - 返回。如果该函数没有返回对象,则返回
this
。
function newFunc(father, ...rest) {
// 首先创立一个空对象
var result = {};
// 将空对象的原型赋值为结构器函数的原型
result.__proto__ = father.prototype;
// 更改结构器函数外部 this,将其指向新创建的空对象
var result2 = father.apply(result, rest);
if ((typeof result2 === 'object' || typeof result2 === 'function') && result2 !== null) {return result2;}
return result;
}
程度与垂直居中的实现形式有哪些
程度居中
- text-align: center; 行内元素实用
- margin: 0 auto; 实用块级元素
- width: fit-content; 若子元素蕴含 float:left 属性, 为了让子元素程度居中, 则可让父元素宽度设置为 fit-content, 并且配合 margin。
.parent {
width:fit-content;
margin:0 auto;
}
- flex
.parent {
display: flex;
justify-content: center;
}
- 盒模型, 应用 flex 2009 年版本
.parent {
display: box;
box-orient: horizontal;
box-pack: center;
}
- transform
.son {
position: absolute;
left: 50%;
transform: translate(-50%, 0);
}
- 两种不同的相对定位办法
.son {
position: absolute;
width: 固定;left: 50%;
margin-left: -0.5 * 宽度;
}
.son {
position: absolute;
width: 固定;top: 0;
right: 0;
margin: 0 auto;
}
垂直居中
- 单行文本, line-height
- 行内块级元素, 应用 display: inline-block, vertical-align: middle; 加上伪元素辅助实现
.parent::after, .son {
display:inline-block;
vertical-align:middle;
}
.parent::after {
content:'';
height:100%;
}
- vertical-align。vertical-align 只有在父层为 td 或者 th 时, 才会失效, 对于其余块级元素, 例如 div、p 等, 默认状况是不反对的. 为了应用 vertical-align, 咱们须要设置父元素 display:table, 子元素 display:table-cell;vertical-align:middle;
- flex
.parent {
display: flex;
align-items: center;
}
- 盒模型
.parent {
display: box;
box-orient: vertical;
box-pack: center;
}
- transform
.son {
position: absolute;
top: 50%;
transform: translate(0, -50%);
}
- 两种不同的相对定位办法
.son {
position: absolute;
height: 固定;
top: 50%;
margin-top: -0.5 * height;
}
.son {
position: absolute;
height: 固定;
top: 0;
bottom: 0;
margin: auto 0;
}
flex, 盒模型, transform, 相对定位, 这几种办法同时实用于程度居中和垂直居中
冒泡排序
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {for (let j = i + 1; j < len; j++) {if (arr[j] < arr[i]) {[arr[j], arr[i]] = [arr[i], arr[j]];
}
}
}
return arr;
}
抉择排序
抉择排序 (Selection-sort) 是一种简略直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,寄存到排序序列的起始地位,而后,再从残余未排序元素中持续寻找最小(大)元素,而后放到已排序序列的开端。以此类推,直到所有元素均排序结束。
function selectionSort(arr) {
const len = arr.length;
for (let i = 0; i < len - 1; i++) {
let index = i;
for (let j = i + 1; j < len; j++) {if (arr[j] < arr[index]) {index = j;}
}
if (index !== i) {[arr[i], arr[index]] = [arr[index], arr[i]];
}
}
return arr;
}
插入排序
插入排序(Insertion-Sort)的算法形容是一种简略直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应地位并插入。插入排序在实现上,通常采纳 in-place 排序(即只需用到 O(1)的额定空间的排序),因此在从后向前扫描过程中,须要重复把已排序元素逐渐向后挪位,为最新元素提供插入空间。
function insertionSort(arr) {
const len = arr.length;
for (let i = 1; i < len; i++) {
let j = i - 1;
const value = arr[i];
while (arr[j] > value && j >= 0) {arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = value;
}
return arr;
}
归并排序
归并排序是建设在归并操作上的一种无效的排序算法。该算法是采纳分治法(Divide and Conquer)的一个十分典型的利用。归并排序是一种稳固的排序办法。先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2 - 路归并。
function mergeSort(arr) { // 采纳自上而下的递归办法
var len = arr.length;
if (len < 2) return arr;
const middle = Math.floor(len / 2);
let left = arr.slice(0, middle);
let right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right) {let result = [];
while (left.length && right.length) {if (left[0] <= right[0]) {result.push(left.shift());
} else {result.push(right.shift());
}
}
return result.concat(left).concat(right);
}
疾速排序
疾速排序的根本思维:通过一趟排序将待排记录分隔成独立的两局部,其中一部分记录的关键字均比另一部分的关键字小,则可别离对这两局部记录持续进行排序,以达到整个序列有序。
function quickSort(arr) {if (arr.length <= 1) return arr;
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr[pivotIndex];
let left = [];
let right = [];
for (var i = 0; i < arr.length; i++){if (arr[i] < pivot) {left.push(arr[i]);
} else {right.push(arr[i]);
}
}
return quickSort(left).concat([pivot], quickSort(right));
};
洗牌算法
function shuffle(arr){
const length = arr.length;
while (length > 0) {random = Math.floor(Math.random() * length);
length--;
[arr[length], temp] = [temp, arr[length]];
}
return arr;
}
// 或
arr.sort(function(){return .5 - Math.random();
});