/**
* 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)
// obj.__proto = fn.prototype
// 前者创建一个原型指向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 () {}
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
let fn = Symbol()
context[fn] = this
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
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
*/
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)
* function receiveMessage(receive) {
* const { data, origin, source(发送消息的窗口对象) } = receive
* }
*/
/**
* 防抖和节流
* 防抖:是将几次操作合并为一次,n秒后执行,在n秒内再次触发,则重新计时
* 节流:是在一定时间内的操作只触发一次
*/
const _debounce = (fn, delay) => {
let timer
return function () { // 闭包函数
const _this = this
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
}, 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'