接上昨天的《前端基础知识整理汇总(上)》,这些知识虽然是很基础的,但是对于系统的梳理还是非常有帮助的,也希望这些内容对你有所帮助。
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__属性会指向该对象的原型。JavaScript中所有的对象都是由它的原型对象继承而来,而原型也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链。每一个对象都会从原型"继承"属性。
实例对象和构造函数都可以指向原型, 原型可以指向构造函数,不能指向实例(因为可以有多个实例)。
原型对象有两个属性,constructor 和 __proto__。
- function Person() {}
- var person = new Person();
- // 实例原型 === 构造函数原型
- person.__proto__ === Person.prototype // true
- // 原型构造函数 === 构造函数
- Person.prototype.constructor === Person // true
react diff
遍历对象
对象遍历方法总结:
异步加载脚本
默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
异步加载脚本方法:defer与async。
defer与async的区别是:defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
ES6 模块与 CommonJS 模块的差异
回流Reflow与重绘Repaint
回流:元素的大小或者位置发生了变化,触发了重新布局,导致渲染树重新计算布局和渲染。页面第一次加载的时候,至少发生一次回流。
重绘:元素的外观,风格改变,而不会影响布局(不包含宽高、大小、位置等不变)。
如:outline, visibility, color, background-color......等
Reflow 的成本比 Repaint 高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。。回流一定会触发重绘,而重绘不一定会回流
减少重绘与回流
1.CSS方法
2.JavaScript方法
CSS3硬件加速(GPU加速)
CSS3 硬件加速又叫做 GPU 加速,是利用 GPU 进行渲染,减少 CPU 操作的一种优化方案。
render tree -> 渲染元素 -> 图层 -> GPU 渲染 -> 浏览器复合图层 -> 生成最终的屏幕图像。
浏览器在获取 render tree后,渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个个图层中,每个图层又会被加载到 GPU 形成渲染纹理。GPU 中 transform 是不会触发 repaint ,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。
CSS3触发硬件加速的属性:
http请求方法
HTTP1.0定义了三种请求方法:GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
js判断数据类型
1. typeof 操作符
2. instanceof
用来判断 A 是否为 B 的实例,检测的是原型。instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。
3. constructor
4. 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,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,主要出于下面几种情况考虑:
防抖和节流
防抖:当你在频繁调用方法时,并不会执行,只有当你在指定间隔内没有再调用,才会执行函数。
节流:在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
防抖
- 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;的元素是相对可视窗口。
Box-sizing
函数声明和函数表达式
- // 函数声明
- function wscat(type){
- return 'wscat';
- }
- // 函数表达式
- var oaoafly = function(type){
- return "oaoafly";
- }
函数声明在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
虚拟dom原理
Virtual DOM是对DOM的抽象,本质上是JavaScript对象,这个对象就是更加轻量级的对DOM的描述.
箭头函数与普通函数区别
new
new 关键字会进行如下的操作:
- 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;
- }
水平与垂直居中实现方式
水平居中
- .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: 固定;
- left: 0;
- right: 0;
- margin: 0 auto;
- }
垂直居中
- .parent::after, .son {
- display:inline-block;
- vertical-align:middle;
- }
- .parent::after {
- content:'';
- height:100%;
- }
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.splice(pivotIndex, 1)[0];
- let left = [];
- let right = [];
- for (let 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) {
- const random = Math.floor(Math.random() * length);
- length--;
- [arr[length], arr[random]] = [arr[random], arr[length]];
- }
- return arr;
- }
- // 或
- arr.sort(function(){
- return .5 - Math.random();
- });
本文完〜
本文由网易云音乐实时计算平台研发工程师岳猛分享,主要从以下四个部分将为大家...
hk 域名 哪里注册? .hk域名 在国内是可以注册的,只要提供了.hk 域名注册 服务...
作者 | 亮言 来源 | 阿里技术公众号 一 背景 订单状态流转是交易系统的最为核心...
约束与限制 只有运行中的云服务器云主机才允许用户登录。 Windows操作系统用户名...
TOP云 (west.cn)1月25日消息,近日,功夫贷宣布获得4000万人民币A轮融资,本轮...
我们知道效能提升 就是要应用系统方法实践和工具 通过它们改进技术、工程能力和...
描述 你正在和你的朋友玩 猜数字 (Bulls and Cows)游戏:你写下一个数字让你的朋...
本文介绍了北京慧达天下如何使用运维编排OOS提高发布效率。 公司介绍 公司名称:...
有很多人在听说大数据之后,会开始纠结JAVA与大数据的区别,甚至还在纠结Java和...
背景 2020年9月16日 Snowflake成功IPO 交易首日市场估值达到704亿美元 募集资金3...