前端知识大杂烩

229 阅读10分钟
/**
 * this:this总是指向调用该函数的对象,箭头函数不绑定this
 * 原型:js中对象都有一个特殊的prototype内置属性,其实就是对其他对象的引用,在对象创建时就赋予了非空值
 * 闭包:简单说就是函数嵌套函数, 内部函数引用外部函数的变量,在函数销毁时仍然可用,会导致内存泄漏
 */
/**
* new 操作符
* 作用:创建一个空对象、克隆该构造函数的原型、修改this指向新对象、指定返回
* @param {*} fn 构造函数
* @param  {...any} arg 传递参数
*/
function _new (fn, ...args) {
  if (typeof fn !== 'function') {
    throw '构造函数必须为函数';
  }
  // var obj = Object.create(null); 创建的对象找不到原型上的方法,Object.create(null)创建一个原型指向null的对象,当原型连为null时,不可再修改,所以再次执行obj.__proto__赋值时不会修改,实际上时添加了一个属性,而不是链接原型
  // obj.__proto = fn.prototype; Object.create(null) 和 Object.create({}) 区别
  // 前者创建一个原型指向null的对象,当原型连为null时,不可再修改,所以再次执行obj.__proto__赋值时不会修改,实际上时添加了一个属性,而不是链接原型
  var obj = Object.create(fn.prototype); 
  var result = fn.apply(fn, args);
  return result instanceof Object ? result || obj : obj;
}
/**
 * 总结bind | call | apply
 * 相同点:三则都可传参,用于改变this的指向
 * 不同点:bind可多次传入的分散参数、call是参数列表、apply是数组,皆为一次性传递, bind返回函数, call和apply立即执行
 */
/**
 * 作用:创建一个新函数,新函数被调用时,bind()的第一个参数当作它运行的this,后面的参数传递给返回的函数当作实参数,即改版this的指向
 * 特点:返回一个函数 | 可以传入参数 | 绑定函数可以通过 new 操作
 * 实现:返回函数(apply)| 传参数(argument 类数组对象)
 * 扩展:new 操作 | apply | call
 * @param {*} context 
 */
function _bind(context) {
  if (typeof this !== "function") {
    throw new Error("调用bind必须为函数");
  }

  var self = this;
  var args = args = [...arguments].slice(1);
  var f_P = function () {}; // 中转函数,避免直接修改f.prototype 为 this.prototype

  var f = function () {
    var bindArgs = [...args, ...arguments]
    // 当作为构造函数时,this 指向实例,self 指向绑定函数
    // 当作为普通函数时,this 指向 window,self 指向绑定函数,此时this instanceof self 结果为 false,当结果为 false 的时候,this 指向绑定的 context。
    self.apply(this instanceof self ? this : context, bindArgs);
  }
  // f_p.prototype = this.prototype 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值
  f_P.prototype = this.prototype;
  f.prototype = new f_P();

  return f;
}
Function.prototype.bind = Function.prototype.bind || _bind;
/**
 * 作用:修改this的指向,可传入多个参数执行函数
 * 特点:给this的对象添加一个属性 - 执行属性函数 - 再删除这个属性,可以有返回值
 */
function _call(context) {
  // this参数不传递时只想window全局
  var context = context || window || global; // node 环境不存在window
  let fn = Symbol(); // 避免和对象本身的属性冲突
  context[fn] = this; // 给绑定的函数添加一个fn方法属性
  var args =[...arguments].slice(1)
  var result = context[fn](...args)

  delete context[fn] // 删除新增属性
  return result; // 返回结果
}
Function.prototype.call = Function.prototype.call || _call;

function _apply (context, arr) {
  var context = Object(context) || window;
  let fn = Symbol(); // 避免和对象本身的属性冲突
  context[fn] = this; // 给绑定的函数添加一个fn方法属性
  var result;
  if (!arr) {
    result = context.fn();
  } else {
    result = context.fn(arr);
  }
  delete context.fn // 删除新增属性
  return result; // 返回结果
}
Function.prototype.apply = Function.prototype.apply || _apply;

/**
 * instanceof 判断实例是否属于某种类型:主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上,遍历左边的原型连是否是右边的原型prototype
 * Example:
 * function Foo() {}
 * Object instanceof Object => true; 
 * Object.__proto__ = Function.prototype ===> Function.prototype.__proto__ = Object.prototype
 * Foo instanceof Foo false; Foo的prototype 为 Foo.prototype, Foo.__proto__ = Function.prototype ===> Function.prototype.__proto__ = Object.prototype ===> Object.prototype.__proto__ = null
 */
function _instanceof (l,r) {
  var rProto = r.prototype;
  var l_Proto = l.__proto__;
  while (true) {
    if (l_Proto === null) return false;
    if (l_Proto === rProto) return true;
    l_Proto = l_Proto.__proto__;
  }
}

/** React 16新特性
* 1、createContext定义全局对象穿透子父组件无副作用定义:Ctx = React.createContext() 传递:<Ctx.Provider value={1}> <Child /> </Ctx.Provider> 获取:<Ctx.Consumer> {(val) => return val} </Ctx.Consumer>
* 2、render可支持返回数据、字符串和数子,只要是纯函数就可以,不修改state的值
* 3、React16 使用 Rollup 针对不同的目标格式进行代码打包,由于打包工具的改变使得库文件大小得到缩减(32%)
* 4、规范了 Ref 的获取方式,通过 React.createRef 取得 Ref 对象 创建:this.myRef = React.createRef() 绑定<div ref={this.myRef} /> 获取inputRef.current.value
* 5、v16.3 Fiber 是对 React 核心算法的一次重新实现,将原本的同步更新过程碎片化,避免主线程的长时间阻塞,使应用的渲染更加流畅,组件更新分为2个阶段 Render phase(对比节点更新) 和 Commit phase(更新到真实节点)
* 6、新增了 getDerivedStateFromProps(位于Render阶段,每次props更新都会执行,无副作用) 、getSnapshotBeforeUpdate()、componentDidCatch(自主错误捕获处理,父组件捕获子组件错误,防止整个render失败但无法捕获事件函数错误) 
* 7、不安全componentWillMount、componentWillReceiveProps 、componentWillUpdate(re-render问题)
* 8、v16.5 Profiler 收集组件耗时,提高性能
* 9、v16.7 Hooks 解决状态逻辑复用 让函数组件管理自己的状态和模拟生命周期 useState(初始值绑定) useEffect(执行自定义操作,模拟生命周期)
* useEffect(() => {})// 加载和更新都会执行 useEffect(() => {}, [])// 只加载时执行 useEffect(() => {}, [a])// 加载执行 & a变化时执行 useEffect(() => { return () => {}}, [])// 卸载时执行return
*/

/** 布局方案
 * 1、传统盒子模型 display + position + float
 * 2、弹性布局 Flex
 *   指定flex布局: display:flex | inline-flex(行内元素)称为容器,元素为成员或项目, 容器和项目各6个属性(之后子元素设置的float、clear、vertical-align 属性失效):
 *   容器:1、主轴、项目排列方向 => flex-direction: row(默认) | row-reverse | column | column-reverse
 *        2、排列是否换行 => flex-wrap: nowrap(默认) | wrap | wrap-reverse
 *        3、flex-flow:是1和2的缩写 默认是 row nowrap
 *        4、项目在主轴上对齐方式 => justify-content: flex-start(默认) flex-end | center | space-between(两端对齐,留白平分) | space-around(两端间隔相等,中间间隔翻倍)
 *        5、项目在交叉轴上对齐方式 => align-items: flex-start | flex-end | center | baseline(项目第一行文字基线对齐)| stretch(默认高度)不设置高度时占满容器
 *        6、项目在交叉轴上的对齐方式(多主轴线时生效)=> align-content:flex-start(默认) flex-end | center | space-between(两端对齐,留白平分) | space-around(两端间隔相等,中间间隔翻倍)
 *   项目:1、主轴排列优先权重 => order: number 越小排列越前, 默认0
 *        2、项目放大比例 => flex-grow: number 默认0,有剩余空间也不放大,1为平分剩余空间
 *        3、项目缩小比例 => flex-shrink: number 默认1 空间不足为平分缩小,为0时,空间不足也不缩小
 *        4、在设置宽度后计算项目占的主轴空间flex-basis,默认是auto,即项目本来的大小
 *        5、flex: 以上三者的缩写, 默认是 0 1 auto,快捷值:auto(1 1 auto)和none(0 0 auto)
 *        6、项目在交叉轴上的对齐方式=> self-align: auto(默认) | flex-start flex-end | center | baseline | stretch
 */

/**
 * margin重叠现象和去除重叠 : 触发元素的BFC(初衷是为乐计算高度) 浮动元素、行内块元素、绝对定位不会发生折叠
 * 发生在块元素垂直方向上,margin较小的被较大的覆盖。通过父元素设置overflow:hidden 或子元素设置float,其前元素或后元素清除浮动(clear: both)
 */

/** 媒体查询(根据分辨率宽度变化调整页面布局) | 屏幕适配
 * 在不支持media query的浏览器IE9-,页面中调用 css3-mediaqueries.js
 * 页面宽度缩放问题<meta name="viewport" content="width=device-width; initial-scale=1.0">
 * 元素大小单位设置为rem,相对根元素(html)字体的相对值
 * 
  @media screen and (max-width: 1200px) {
      font-size: 12px;// 浏览器最小字号限制
  }
 */

/**
 * Promise 是异步编程的一种解决方案,解决回调地狱二产生,它的 then 的 注册微任务队列 和 执行 是分离的。
 * 注册 : 是完全遵循 JS 和 Promise 的代码的执行过程。
 * 执行 : 先 同步,再 微任务 ,再 宏观任务。
 */

/**
 * 前端页面优化:加载时 & 运行时
 * 加载性能主要看首屏时间(页面完全加载onload事件)和白屏时间(显示内容)
 * 加载时: 减少http请求:一个完整的http请求要经历DNS查找,TCP握手,浏览器发出http请求,服务器接收请求,服务器处理请求并发回相应,浏览器接收相应过程
 *        使用服务端渲染:服务端返回html,客户端直接解析,首屏渲染快,利于SEO优化(便于搜索引擎搜索排名)
 *        静态资源尽量使用CDN(内容分发网络)
 *        cdn资源预解析,在模版html文件中,添加<link rel="dns-prefetch" href="http://yuming" >或header中添加<meta http-equiv="x-dns-prefetch-control" content="on"> 表示强制打开<a>标签中超链接的DNS预解析
 *        资源加载时机:资源加载通过defer(异步加载,相当于放在body的底部,html解析后执行,多个时,按顺序执行)和async(异步加载,加载完之后立即执行,如果是多个,执行顺序和加载顺序无关。)
 *        使用http的相关缓存
 *        webpack进行文件压缩 js: UglifyPlugin | css: MiniCssExtractPlugin | html: HtmlWebpackPlugin, 开启gzip压缩
 *        图片优化:延迟加载(可视区域内:采用Element.getBoundingClientRect()获取元素的大小及其相对视口的位置,通过监听滚动)、图片裁剪
 *        按需加载:lodash的各种方法, 提取第三方代码,减少冗余,通过tree shaking来删除引用单位使用的代码
 *        减少 ES6 转为 ES5 的冗余代码: 使用"plugins": [ "@babel/plugin-transform-runtime" ]
 * 运行时:减少dom操作、减少重排(大小、位置变化)和重绘(样式)
 */

/**
 * postMessage方法: 跨域通信
 * otherWindow.postMessage(message, targetOrigin) // otherWindow可以为iframe的contentWindow, window.open的新window 以及window.opener的window,默认指当前页面的window
 * window.addEventListener("message", receiveMessage, false); // attachEvent IE浏览器的事件绑定
 * function receiveMessage(receive) {
 *  const { data, origin, source(发送消息的窗口对象) } = receive; // 使用origin和source属性验证发件人的身份,无法检查origin和source属性会导致跨站点脚本攻击
 * }
 */

/**
 * 防抖和节流
 * 防抖:是将几次操作合并为一次,n秒后执行,在n秒内再次触发,则重新计时
 * 节流:是在一定时间内的操作只触发一次
 */
const _debounce = (fn, delay) => {
  let timer;
  return function () { // 闭包函数
    const _this = this; // 指定this为debounce执行时的作用域
    if (timer) {
      clearTimeout(timer); // 清除定时器
    }
    timer = setTimeout(() => {
      fn.apply(_this, arguments)
    }, delay);
  };
};
const _throttle = (fn, delay) => {
  let timer;
  return function () {
    const _this = this;
    if (timer) { // 一定时间内触发不执行直接返回
      return;
    }

    timer = setTimeout(() => {
      fn.apply(_this, arguments);
      timer = null; // 执行完毕后将timer置空
    }, delay);
  }
};
const _throttle = (fn, delay) => {
  let time = 0;
  return function () {
    const _this = this;
    const now = new Date();
    if (now - time > delay) { // 对比上一次和本次执行的时间差
      fn.apply(_this, arguments);
      time = now;
    }
  }
};

/**
 * 事件循环:事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列,常见的宏任务有setTimeout I/O操作 及UI渲染、微任务有Promise.then、process.nextTick
 * 当执行Js代码时就是网执行栈中放入函数,当遇到异步任务时,挂起并在需要执行时加入到任务队列中,一旦执行栈唯恐,事件循环就从任务队列中拿出要执行的代码放入执行栈中执行,其顺序是同步任务 > 微任务 > 宏任务
 * 执行栈是🈯️存储函数调用的栈结构,遵从先进后出的原则
 */

/**
 * 函数去重、交集、并集、差集
 */
const arr1 = [1, 2, 3, 1];
const arr2 = [2, 3, 4];
const pure = [...new Set(arr1)];
const union = new Set([...arr1, arr2]);
const intersect = new Set([...arr1].filter((val) => {arr2.has(val)}));
const diff = new Set([...arr1].filter((val) => !arr2.has(val)));
// 判断数组
arr1 instanceof Array;
Array.isArray(arr1);
Object.prototype.toString.call(arr1) // '[object Array]';
arr1.constructor === 'Array';