Skip to content

进程 & 线程(process 和 thread)

启动应用,开启一个进程,同时申请一块内存进程中可以开启多个线程去做很多事情,线程都可以操作共享这部分内存, 一般浏览器可以是多进程(进程间通过 IPC (Inter Process Communication)进行通信)或者单进程的

Chrome 多进程(四进程)

Browser Process:主进程

  • 负责包括地址栏,书签栏,前进后退按钮等部分的工作;
  • 负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问;

Renderer Process :

  • 负责一个 tab 内关于网页呈现的所有事情

Plugin Process:

  • 负责控制一个网页用到的所有插件,如 flash

GPU Process

  • 负责处理 GPU 相关的任务

优点

  • 某一渲染进程出问题不会影响其他进程
  • 更为安全,在系统层面上限定了不同进程的权限

缺点

  • 由于不同进程间的内存不共享,不同进程的内存常常需要包含相同的内容。
  • 为了节省内存,Chrome 限制了最多的进程数,最大进程数量由设备的内存和 CPU 能力决定,当达到这一限制时,新打开的 Tab 会共用之前同一个站点的渲染进程。(之前的就会挂掉)

浏览器事件模型

浏览器基于事件驱动,将很多东西都抽象为事件,比如用户交互,网络请求,页面加载,报错等,可以说事件是浏览器正常运行的基石。

事件传播

  • 事件传播方式:事件冒泡事件捕获
  • 可以使用事件委托来优化 子元素的过多事件绑定,占用内存, 基于事件冒泡原理实现的
js
// 第三个参数: true:标识是事件捕获,false:(默认) 事件冒泡模型
document.addEventListener("click", () => {}, boolean);

事件对象 和 方法

event : target 、xy 定位信息

  • preventDefault 用于阻止浏览器的默认行为,比如 a 标签会默认进行跳转,form 会默认校验并发送请求到 action 指定的地址等
  • stopPropagation 用于阻止事件的继续冒泡行为,后面讲事件传播的时候会提到。

鼠标事件

  • 鼠标点击事件包括 4 个:click(单击)、dblclick(双击)、mousedown(按下)和 mouseup(松开)

  • 鼠标移动事件mouseover 和 mouseout 的效率较低,建议使用 mouseente 和 mouseleave

    mouseover 与 mouseoutmousemovemouseenter 与 mouseleave
    鼠标移入、鼠标移出鼠标在某元素上移动鼠标移入、鼠标移出
    支持冒泡(在子元素内移动,父级元素会被触发)支持冒泡不支持冒泡(在子元素内移动,父级元素不会被触发)
  • 鼠标滚轮事件mousewheel

移动端事件

touchstart 、 touchover 、 touchend

react 的中事件

React 的合成事件: 如果绑定原生事件,原生事件优先于合成事件被触发 , 好处:兼容性、跨端,方便统一管理

BOM & DOM

BOM 对象:

  1. window 对象:BOM 的核心对象,表示整个浏览器窗口,主要用来操作浏览器窗口。所有全局变量和函数都是它的属性,且所有原生的构造函数及其他函数也都存在于它的命名空间下

  2. document 对象:用于处理页面文档

  3. location 对象:用于获取或设置当前页面的地址 (URL),并把浏览器重定向到新的页面

  4. navigator 对象:提供了与浏览器有关的信息

  5. screen 对象:用来获取用户的屏幕信息

  6. history 对象:包含浏览器的历史

DOM 操作

创建空白的文档片段 createDocumentFragment()

js
var element = document.getElementById("ul"); // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ["Firefox", "Chrome", "Opera", "Safari", "Internet Explorer"];

// 把li 添加到文档片段中,因为fragment在内存中,所以不会引起回流(DOM结果发生变化)
browsers.forEach(function (browser) {
  var li = document.createElement("li");
  li.textContent = browser;
  fragment.appendChild(li);
});

element.appendChild(fragment);
  • createElement() 创建一个具体的元素
  • createTextNode() 创建一个文本节点

添加、插入

  • element.appendChild(aChild)
  • parentNode.insertBefore(newNode, referenceNode);

移除

  • parentNode.removeChild(child);

替换、

  • parentNode.replaceChild(newChild, oldChild);

查找

  • getElementsByTagName() 通过标签名称
  • getElementsByName() 通过元素的 Name 属性值
  • getElementById() 通过元素 id
  • document.getElementsByClassName
  • document.querySelector
  • document.querySelectorAll

事件循环

原因

  1. JS 单线程:优点是防止多线程同时修改 DOM 带来不确定性,缺点:线程阻塞
  2. WebAPI: 例如 setTimeout,我们可以通过浏览器把任务延迟执行,webAPI 需要通知主线程执行
  3. 消息(事件)队列: 浏览器中的所有任务、消息统称为事件,队列分为 微任务队列、宏任务队列

这是一个事件循环的基本步骤

  • 执行同步代码(可以理解为一个宏任务),如遇到异步交由 WebAPI,WebAPI 执行完,放入消息队列中
  • 当所有同步代码执行完毕后,检查微任务队列,如果有任务,则依次执行队列中的所有微任务。
  • 微任务执行完毕后,检查宏任务队列,如果有任务,则取出一个宏任务并执行。
  • 重复以上步骤,直到所有任务执行完毕。

宏任务 & 微任务

类型浏览器Node
宏任务script、setTimeout setInterval、postMessage、MessageChannelI/O 操作、setImmediate
微任务promise、queueMicrotask、MutationObservernextTick

MutationObserver 提供了 监视对 DOM 树所做更改的能力。

js

 // 选择需要观察变动的节点
const targetNode = document.getElementById('some-id');

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function(mutationsList, observer) {
	console.log(....)
};

// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);

// 以上述配置开始观察目标节点
observer.observe(targetNode, config);

// 之后,可停止观察
observer.disconnect();

MessageChannel 双端通道

js
var channel = new MessageChannel();
var port1 = channel.port1;
var port2 = channel.port2;
port1.onmessage = function (event) {
  console.log("port1收到来自port2的数据:" + event.data);
};
port2.onmessage = function (event) {
  console.log("port2收到来自port1的数据:" + event.data);
};

port1.postMessage("发送给port2");
port2.postMessage("发送给port1");
// port2收到来自port1的数据: port2
// port1收到来自port2的数据: port1

Promise 源码实现

在线链接

js
let PENDING = "PENDING";
let FULFILLED = "FULFILLED";
let REJECTED = "REJECTED";

class Promise {
  constructor(exec) {
    try {
      exec(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      console.log("出错了:", error.message);
    }
  }
  state = PENDING;
  value = null;
  reason = null;
  successCallback = [];
  failCallback = [];
  resolve(value) {
    if (this.state !== PENDING) return;
    this.value = value;
    this.state = FULFILLED;
    while (this.successCallback.length) this.successCallback.shift()();
  }
  reject(reason) {
    if (this.state !== PENDING) return;
    this.state = REJECTED;
    this.reason = reason;
    while (this.failCallback.length) this.failCallback.shift()();
  }
  /**
   * 1. 同步情况
   * 2. 异步情况
   *  2.1 :所有执行都必须在微任务中
   *  2.2 : 返回结果是promise , 不能返回自己
   */
  then(success, fail) {
    let successCallback = success ? success : (value) => value;
    let failCallback = fail
      ? fail
      : (reason) => {
          throw new Error(reason);
        };
    let promise = new Promise((resolve, reject) => {
      if (this.state === FULFILLED) {
        queueMicrotask(() => {
          let res = successCallback(this.value);
          resolveRes(res, promise, resolve, reject);
        });
      } else if (this.state === REJECTED) {
        queueMicrotask(() => {
          let res = failCallback(this.reason);
          resolveRes(res, promise, resolve, reject);
        });
      } else {
        this.successCallback.push(() => {
          queueMicrotask(() => {
            let res = successCallback(this.value);
            resolveRes(res, promise, resolve, reject);
          });
        });
        this.failCallback.push(() => {
          queueMicrotask(() => {
            let res = failCallback(this.reason);
            resolveRes(res, promise, resolve, reject);
          });
        });
      }
    });
    return promise;
  }
  catch(fail) {
    // let res = fail(this.reason);
    // return res instanceof Promise ? res : new Promise((s, r) => r(res));
    return this.then(_, fail);
  }
  finally(callback) {
    return this.then(
      (value) => {
        return Promise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return Promise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }
  /** 考虑三个点: 链式,参数必须是数组,返回结果必须按输入顺序: arr.length 不能做判断条件 */
  static all(params) {
    let index = 0;
    let p = new Promise((s, r) => {
      if (!Array.isArray(params)) {
        return new Promise((_, reject) => reject(""));
      }
      let resArr = [];
      for (let i = 0; i < params.length; i++) {
        if (p instanceof Promise) {
          p.then(
            (s) => {
              addRes(s, i);
            },
            (err) => r(err)
          );
        } else {
          resArr[i] = p;
          addRes(params[i], i);
        }
      }

      function addRes(res, i) {
        resArr[i] = res;
        index++;
        if (index === params.length) s(resArr);
      }
    });
    return p;
  }
  static resolve(val) {
    return val instanceof Promise ? val : new Promise((s) => s(val));
  }
}
const resolveRes = (res, promise, resolve, reject) => {
  if (promise === res) {
    reject(new Error("不能是相同实例"));
    return;
  }
  if (res instanceof Promise) {
    res.then(resolve, reject);
  } else {
    resolve(res);
  }
};

let p = new Promise((resolve, reject) => {
  // console.log(111);
  // // resolve("success");
  // reject("reject");
  // console.log(222);
  setTimeout(() => {
    console.log(111);
    resolve("success");
    console.log(222);
  }, 100);
});

p.then(
  (s) => {
    console.log("..", s);
    return 23;
  },
  (r) => console.log("...", r)
).then(
  (s) => {
    console.log("11", s);
  },
  (r) => console.log("111", r)
);

chrome 和 Edge 处理跨域问题 (94.0 以上的问题)

edge:

edge://flags/ -> Block insecure private network requests. -> disable

chrome:

Block insecure private network requests. -> disable

CSS

at-rules (常用属性)

@property在线实例

css
@property --test-num {
  inherits: false;
  syntax: "<integer>";
  initial-value: 100;
}

常用属性

clip-path

clip-path CSS 属性使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。

一组坐标绘制一个多边形 : clip-path: polygon(0 0,25% 0,100% 50%,25% 100%,0 100%)

inset

CSS 属性 inset 为简写属性,对应于 top、right、bottom 和 left 属性。其与 margin 简写属性具有相同的多值语法。