总结

327 阅读24分钟

JS基础

0.1+0.2 != 0.3 的根本原因

0.1和0.2转换成二进制是无限循环的,遵循IEEE754尾数位数限制(小数位52位),需要将后面多余的位截掉,就造成了精度损失。

instanceoftypeof

  • instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
  • instanceof 用于判断一个引用类型的变量是否是某个对象的实例
  • typeof 判断基本数据类型的类型。

typeof null 为什么是object

javascript的最初版本中,使用的 32位系统,js为了性能优化,使用低位来存储变量的类型信息。

  • null的机器识别码标识为全0
  • object的机器码低位标识000
  • 所以typeof null的结果被误判为Object,这是一个bug

执行上下文

  • 当函数运行时,会创建一个执行环境,这个执行环境就叫执行上下文
  • 每个上下文都有一个关联的变量对象(variable object),这个上下文中定义的所有变量和函数都存在这个对象上。
  • 当代码执行流进入函数时,函数上下文会被推到一个上下文栈上;上下文中的代码在执行的时候,会创建变量对象的一个作用域链。作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端;如果上下文是函数,则其活动对象用作变量对象。函数执行完之后,上下文栈会弹出该函数上下文,将控制权返还给之前的执行上下文。
  • 上下文之间的连接是线性有序的。每个上下文都可以到上一级上下文中去搜索变量和函数,但任何上下文都不能到下一级上下文中去搜索

全局上下文(window对象)是最外层的上下文,所有通过var定义的全局变量和函数都会成为window对象的属性和方法。使用let和const的顶级声明不会定义在全局上下文上。

上下文在其代码执行完毕后会被销毁,包括定义在它上面的所有变量和函数。全局上下文在应用程序退出前才会被销毁。

闭包

定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。有权访问另一个函数作用域中的变量的函数

闭包的本质:函数在执行的时候会放到一个执行栈上,当函数执行完毕之后会从执行栈上移除; 但是堆上的作用域成员因为被外部使用不能释放,因此内部函数依然可以访问外部函数的成员

节流和防抖

  • 节流: 在间隔时间之内, 只允许执行一次函数; 这样的函数就是函数节流。
function throttle(func ,delay = 500) {
  let timer = null;
  return function () {
    let context = this
    let args = arguments;
    if(!timer){
      timer = setTimeout(() => {
      // 下面这种写法:这里是箭头函数 没有自己的arguments,所以可以接收到来自上层的参数
      // func.apply(context, arguments)
        func.apply(context, args)
        args
        timer = null
      }, delay)
    }
  }
}
  • 防抖:如果在一定的时间间隔之内去重复执行某个程序,则会把程序执行时间重置。
function debounce(fn, delay = 500) {
  let timer = null;
  return function () {
    let context = this
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(context, arguments)
    }, delay);
  }
}

代码实现->点我

输入一个URL到页面呈现

  • 构建请求
  • 查找强缓存,如果命中缓存直接使用缓存资源
  • 没有命中缓存,进入DNS域名解析系统,把域名解析为ip地址
  • 把ip发送到网络供应商,去找对应的主机服务器
  • TCP的三次握手建立连接
  • 发送请求,取回入口文件index.html
  • 开始解析入口文件,并取回需要的资源sources
  • 渲染页面

EventLoop执行过程

image

  • 所有同步任务在主线程上依次直接执行,形成一个执行栈(调用栈);异步任务分别放入任务队列(宏任务进入宏任务队列,微任务进入微任务队列)。
  • 当执行栈中任务执行完,再去检查微任务队列是否为空,有就执行,直到全部执行完。
  • 微任务执行完后,再到任务队列检查宏任务是否为空,有就取出最先进入队列的宏任务压入执行栈中执行其同步代码。
  • 然后回到第二部,依次循环,直到所有任务执行完毕。

浏览器和 Node 环境下,microtask 队列的执行时机不同:

  • 在浏览器中执行完宏任务后会检查微任务队列并清空微任务,然后再去执行宏任务。
  • node 环境下执行宏任务所产生的的微任务被放入微任务队列,然后执行下一个宏任务,最后才执行微任务队列中的微任务。microtask 在事件循环的各个阶段之间执行

宏任务和微任务的代表(那些不常见的了解下)

宏任务

  • setTimeout
  • setInterval
  • Node中的setImmediate
  • requestAnimationFrame
  • I/O
  • UI渲染、网络请求

微任务

  • promise.then()
  • queueMicrotask()
  • MutationObserver(监听DOM)
  • node中的process.nextTick等

浏览器的渲染过程

  • 解析HTML生成DOM树。
  • 解析CSS生成CSSOM规则树。
  • 将DOM树与CSSOM规则树合并在一起生成渲染树。
  • 遍历渲染树开始布局,计算每个节点的位置大小信息。
  • 将渲染树每个节点绘制到屏幕。

cors跨域

  • 简单请求
  • 非简单请求,会发送一次预检请求,返回码是204,预检通过后才会真正发出请求,这才返回200。前端发请求的时候增加一个额外的headers来触发非简单请求

跨域的解决方案

  • CORS:后端设置Access-Control-Allow-Origin:*后,表示服务器可以接受所有的请求源(Origin),即接受所有跨域的请求。

  • jsonp

    jsonp的原理就是利用<script>标签没有跨域限制,通过<script>标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器(script标签的src属性请求回来的东西是一个字符串,浏览器会把这个字符串当作 js 代码来执行),浏览器解析执行,从而前端拿到callback函数返回的数据。

    jsonp的缺点:只能发送get一种请求。

  • 服务器代理,通过修改nginx服务器配置实现代理转发。

实现一个动画

  1. js直接实现;

    let elem = document.getElementById('rect');
    let left = 0;
    let timer = setInterval(function(){
     if(left<window.innerWidth-200){
         elem.style.marginLeft = left+'px';
         left ++;
     }else {
         clearInterval(timer);
     }
    },16);
    

    tips: 我们设置的setInterval时间间隔是16ms。一般认为人眼能辨识的流畅动画为每秒60帧(目前大多数设备的屏幕刷新率为 60 次/秒),这里16ms比(1000ms/60=16.66)帧略小一些,但是一般可认为该动画是流畅的。 在很多移动端动画性能优化时,一般使用16ms来进行节流处理连续触发的浏览器事件。例如对touchmove、scroll事件进行节流等。通过这种方式减少持续事件的触发频率,可以大大提升动画的流畅性。

  2. CSS3 transition

    transition是过渡动画。但是transition并不能实现独立的动画,只能在某个标签元素样式或状态改变时进行平滑的动画效果过渡,而不是马上改变。

    注意:在移动端开发中,直接使用transition动画会让页面变慢甚至卡顿。所以我们通常添加transform:translate3D(0,0,0)或transform:translateZ(0)来开启移动端动画的GPU加速,让动画过程更加流畅。

  3. CSS3 animation

    animation 算是真正意义上的CSS3动画。通过对关键帧和循环次数的控制,页面标签元素会根据设定好的样式改变进行平滑过渡。而且关键帧状态的控制是通过百分比来控制的。

ES6 的新特性

  1. let const 块级作用域

    • 不能重复声明
    • 不会进行预解析
    • 会被所有代码块限制作用范围
    • const 在声明时必须被赋值
  2. 数组解构

  3. 对象解构

  4. 模板字符串

  5. 字符串的新方法:startsWith endsWith includes

  6. 展开运算符spread

  7. 箭头函数

    箭头函数就是普通函数的简写,可以更优雅的定义一个函数,和普通函数相比,有以下几个差异:

    1. 箭头函数不会创建自己的 this,它只会从自己的作用域链的上一层继承 this。

    2. 不绑定 arguments,当在箭头函数中调用 aruguments 时同样会向作用域链中查询结果,可以用剩余参数 rest 代替。

    3. 不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。

    4. 不可以使用 new 操作符,因为:

      • 没有自己的 this,调用 bind,call,apply 绑定 this 值无效。
      • 没有 prototype 属性 ,而 new 命令在执行时需要将构造函数的 prototype 赋值给新的对象的 proto
  8. 对象字面量,若值是变量,且和属性名相同 可省略

  9. Object.is()方法判断两个值是否为同一个值

  10. proxy代理对象(或数组)

    const person = { name: 'xiao', age: 18 }
    // console.log('name' in person); // 该属性是否属于某个对象
    const { log } = console
    const personProxy = new Proxy(person, {
      get(target, property) {
        log(target, property);
        return property in target ? target[property] : 'default'
      },
      set(target, property, value) {
        if (property === 'age') {
          if (!Number.isInteger(value)) {
            throw new TypeError(`${value} is not an integer`)
          }
        }
        log(target, property, value)
      }
    })
    console.log(personProxy.name);
    // personProxy.age = '2'
    
  11. Set 集合

  12. Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。

  13. for of遍历

  14. Reflect是一个内置的对象,它提供拦截JavaScript操作的方法,不能new

this指向

函数的回调(callback)没有绑定任何上下文,因此相当于一个自由变量

var obj1={
    num:5,
    a:function(){
        var arr=[1,2,3,4,5];
        arr.forEach((function(item){
            // 普通回调函数中this将会被重新指向到window,可用bind改变this指向
            console.log(this) // Window
            console.log(this.num,item);
        }).bind(this)); // 改变this为obj1
        
        arr.forEach(item=>{
            console.log(this); //当前箭头函数外this的指向(obj1)
        }) // 不能使用bind,apply,call。无效
    }
}
obj1.a()
  • 事件函数中的this -> 事件侦听的对象;this指向e.currentTarget
  • 箭头函数中,call()、apply()、bind()方法无效
//例1,this指向定义箭头函数所在的运行作用域,它位于对象cat内,但cat不能构成一个运行作用域,所以指向全局window
const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

//例2,此时this也是指向window,不能动态监听button,改成普通函数后this指向按钮对象。
var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});

伪数组转换成数组

  1. Array.prototype.slice.call(arguments)
  2. [].slice.call(arguments)
  3. arr = [...arguments]
  4. Array.from(arguments)

原型链相关

Function.prototype.a = () => {
    console.log(1);
}
Object.prototype.b = () => {
    console.log(2)
}
function A() {}
const a = new A();

a.a(); // TypeError
a.b(); // 2
A.a(); // 1
A.b(); // 2

script元素的defer和async属性的区别

我的掘金

CSS

css定位的几种区别 relative, absolute, fixed,stick

  • 相对定位position:relative;参考物是自己,不脱离文档流(初始位置仍然占据空间),top:100px; 给正值是向该容器的中心点移动;
  • 绝对定位position:absolute; 参考物是外层具有position属性的元素, 如果向外都么有找到最后会参考body做定位
  • 固定定位position:fixed; 参考物是可视窗口
  • 粘性定位: position:sticky; 元素根据正常文档流进行定位,该值总是创建一个新的层叠上下文,偏移值不会影响其他任何元素的位置(相对于最近的滚动祖先元素)

BFC「块级格式化上下文」

参考知乎文章

  • 同一个 BFC 下垂直方向的外边距会发生折叠
  • BFC 可以包含浮动的元素(清除浮动)
  • 不会与浮动的元素相重合(可以阻止元素被浮动元素覆盖)

image.png 满足下列条件之一就可以触发BFC

  • 1:根元素 html
  • 2:float的值不为none
  • 3:overflow的值不为visible
  • 4:display的值为:flex、inline-block、table-cell、table-caption
  • 5:position的值为absolute或者fixed

回流(又叫重排)和重绘

掘金 思否

回流:当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。

重绘: 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。

注意:回流必将引起重绘,而重绘不一定会引起回流

何时发生回流重绘:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
  • 激活CSS伪类(例如::hover
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
  • 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow

当你获取布局信息的操作的时候,会强制刷新渲染队列,引起回流

比如当你访问以下属性或者使用以下方法:

offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()
getBoundingClientRect

如果要使用它们,最好将值缓存起来

回流优化建议

  1. 减少重排范围

    • 尽量以局部布局的形式组织html结构,尽可能小的影响重排的范围
    • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围。
  2. 减少重排次数

    • 样式集中改变;统一在 cssText 变量中编辑
    • 分离读写操作;DOM 的多个读操作(或多个写操作),应该放在一起。
    • 将 DOM 离线。使用 display:none将其从页面上拿掉,我们之后的操作就不会触发重排和重绘,等我们变更后,通过display:block;展示。这样只做了两次重排重绘就可以了。
    • 使用 absolute 或 fixed 脱离文档流
    • 优化动画
      • 可以把动画效果应用到 position属性为 absolute 或 fixed 的元素上,这样对其他元素影响较小。
      • 启用GPU硬件加速
  3. 比起考虑如何减少回流重绘,我们更期望的是,根本不要回流重绘

    使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的

    常见的触发硬件加速的css属性:

    • transform
    • opacity
    • filters

React

redux数据流

  • 组件想要改变 Store 数据的时候,需要创建一个Action ,然后通过dispatch(action)传递给Store 然后Store把Action转发给Reducers
  • 要保证Reducer 是一个纯函数,然后生成一个新的数据传递给Store
  • Store 通过触发subscribe() 这个方法来调用函数, 执行setState使得视图更新

react中key值的理解

  • react利用key来识别组件,它是一种身份标识,相同的keyreact认为是同一个组件,这样后续相同的key对应组件都不会被创建
  • 有了key属性后,就可以与组件建立了一种对应关系,react根据key来决定是销毁重新创建组件还是更新组件。 -key相同,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新。
  • key值不同,则react先销毁该组件(有状态组件的componentWillUnmount会执行),然后重新创建该组件(有状态组件的constructor和componentWillUnmount都会执行)

React嵌套组件加载顺序

class App extends Component {
  componentWillMount() {
    console.log('App-页面即将加载')
  }
  componentDidMount() {
    console.log('App-页面加载完成')
  }
  render() {
    return (
      <div className="App">
        <Header />
        <Body />
        <Footer />
      </div>
    );
  }
}
// console.log()内容和顺序如下
App-页面即将加载
Header-页面即将加载
body-页面即将加载
Footer-页面即将加载
Header-页面加载完成
body-页面加载完成
Footer-页面加载完成
App-页面加载完成

受控组件和非受控组件

非受控组件

在HTML的表单元素中(input、select、textarea),它们通常自己维护一套state,并随着用户的输入自己进行UI上的更新,这种行为是不被我们程序所管控的。

受控组件

而如果将React里的state属性和表单元素的值建立依赖关系,再通过onChange事件与setState()结合更新state属性,就能达到控制用户输入过程中表单发生的操作。被React以这种方式控制取值的表单输入元素就叫做受控组件。

函数组件和类组件的区别「待完成」

setState 底层原理「待学习」

创建虚拟dom,diff算法对比 React在setState之后,会经对state进行diff,判断是否有改变,然后去diff dom决定是否要更新UI。

React 合成事件(SyntheticEvent)

React使用合成事件,其主要目的有三个:

  1. 进行浏览器兼容,实现更好的跨平台。

    React 采用的是顶层事件代理机制,能够保证冒泡一致性,可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异,将不同平台事件模拟合成事件。

  2. 避免垃圾回收

    事件对象可能会被频繁创建和回收,因此 React 引入事件池,在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉,而是存放进一个数组中,当事件触发,就从这个数组中弹出,避免频繁地去创建和销毁(垃圾回收)

  3. 方便事件统一管理和事务机制

优化

性能优化

  • gzip压缩,压缩效率非常高,通常可以达到 70% 的压缩率
  • 去除console.log,terser-webpack-plugin插件配置到webpack来实现
  • 按需加载;资源预加载
  • 异步无阻塞加载JS defer async(脚本下载完立即执行)
  • loading菊花图加载,骨架屏效果
  • 防抖(debounce)/节流(throttle)
  • 扁平化数据,数据驱动视图
  • 缓存接口请求回来的数据
  • 使用Tinypng压缩图片(Tiny: 读作[ˈtaɪni])

HTTP

HTTP 缓存

HTTP 缓存又分为强缓存和协商缓存:

  • 首先通过http头的Cache-ControlExpire这两个表示资源的缓存时间的字段来验证强缓存是否可用,如果命中强缓存,则不发送请求,直接读取缓存。
  • 如果已过期,那么进入协商缓存阶段,发起HTTP请求,这个请求会携带第一次请求返回的有关缓存的header字段信息。
    • 通过If-None-Match头将先前服务器发送过来的Etag发送给服务器,服务器对比Etag是否与服务器的相同,若相同,就将If-None-Match的值设为false,返回状态为304,告诉浏览器使用缓存获取资源。
    • 客户端还会通过If-Modified-Since头将先前服务器端发过来的最后修改时间戳发送给服务器,服务器端通过这个时间戳判断客户端的页面是否是最新的,如果不是最新的,那么返回资源和 200 状态码,如果是最新的,则返回304,客户端继续使用本地缓存。

HTTP 常用的状态码及使用场景?

  • 1xx:表示目前是协议的中间状态,还需要后续请求
  • 2xx:表示请求成功
  • 3xx:表示重定向状态,需要重新请求
  • 4xx:表示请求报文错误
  • 5xx:服务器端错误

常用状态码:

  • 101 切换请求协议,从 HTTP 切换到 WebSocket
  • 200 请求成功,有响应体
  • 301 永久重定向:会缓存
  • 302 临时重定向:不会缓存
  • 304 NOT Modified 协商缓存命中, 通常是在协商缓存中表示本次内容未修改 浏览器缓存
  • 400 Bad Request 前端请求错误
  • 401 Unauthorized 未授权
  • 403 Fobidden 请求被拒绝, 服务器禁止访问
  • 404 Not Found 资源不存在
  • 500 Inter Server Error 服务器端错误
  • 503 server Unavailable 服务器繁忙 | 服务不可用

http和https的区别

http端口号80,https端口号443

HTTPS协议可以理解为HTTP协议的升级,就是在HTTP的基础上增加了数据加密。在数据进行传输之前,对数据进行加密,然后再发送到服务器。这样,就算数据被第三者所截获,但是由于数据是加密的,所以你的个人信息依然是安全的。

webpack

loader和插件plugin的区别

loader 用于转换某些类型的模块,而 plugin 可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

webpack 压缩插件

uglifyjs-webpack-plugin

构建优化

  • webpackPrefetch 预获取,不占用首屏渲染时间;要求浏览器在带宽空闲时预下载当前这个文件,这样实际使用的时候就会从缓存去拿
  • webpackChunkName 命名空间,使用它对文件进行命名,命名空间相同的会打包在一个文件里。
const List = lazy(
  () => import(/* webpackPrefetch: true, webpackChunkName: "mine" */ 'pages/mine/list'),
);

未归类

小程序的生命周期

  • onLoad 监听页面加载
  • onShow 监听页面显示
  • onReady 监听页面初次渲染完成
  • onHide 监听页面隐藏
  • onUnload 监听页面卸载
  • onPullDownRefresh 监听用户下拉操作
  • onReachBottom 页面上拉触底事件的处理函数
  • onShareAppMessage 用户点击右上角转发

小程序的路由跳转有哪几种 介绍, navigateBack 可以传参吗

不可以通过url传参,使用getCurrentPages()函数获取页面栈的实例,直接调用上一个页面对象的setData()方法,把数据存到上一个页面中去

vue的v-model数据绑定 对于数组和对象的绑定有什么区别

  • 数组的形式时可能无法实时更新数据

axios怎么取消请求 原生中有一个取消的方法

可以调用XMLHttpRequest对象上的abort方法 在axios拦截器里, 查找axios的文档,发现可以通过使用CancelToken来取消axios发起的请求

promise三种状态 pending

  • Promise 是异步编程的一种解决方案,对象的状态不受外界影响,一旦状态改变,就不会再变,任何时候都可以得到这个结果,,Promise构造器接受一个函数作为参数,这个函数有两个参数:resolve,reject,分别代表这个Promise实例成功之后的回调函数和失败之后的回调函数。
  • 有三种状态: pending(进行中)、fulfilled(已成功)和rejected(已失败)。
  • 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

请求为什么放在ComponentDidMount中

componentDidmount 是在组件完全挂载后才会执行,在此方法中调用setState 会触发重新渲染

对浏览器缓存的理解

是为了加速浏览,浏览器在⽤户磁盘上对请求过的⽂档进⾏存储,当访问者再次请求这个⻚⾯时,浏览器就可以从本地磁盘显示⽂档,这样就可以加速⻚⾯的阅览

DOM事件中target和currentTarget的区别

  • target是事件触发的真实元素(目标对象)
  • currentTarget是事件绑定的元素(事件侦听的对象)
  • 事件处理函数中的this指向是 currentTarget

flat() 扁平的

  • 该方法创建一个新数组,该数组将所有子数组元素递归连接到指定深度。
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
编写一个程序将数组扁平化去并除其中重复部分数据,最终得到一个升序且不重复的数组

// 方法一
console.log(Array.from(new Set(arr.flat(Infinity))).sort((a, b) => a - b))
// 方法二
console.log(Array.from(new Set(arr.toString().split(','))).map(Number).sort((a, b) => a - b))

原型链

原型链

  • prototype 每一个函数都有一个 prototype 属性,它指向原型对象
  • constructor :构造器 === 构造函数;表明当前的原型对象属于哪个构造函数 :原型对象只有在构造函数体系中才能使用
  • __proto__ 原型指针: 这个对象是由谁构造的, 原型指针就指向这个构造函数的原型(prototype)

在创建一个构造函数的时候,自带了一个prototype属性,这个属性指向一个对象,也就是原型对象 这个原型对象⾥⾯有⼀个constructor构造器,它的作⽤是指向创建⾃⼰的构造函数。除此之外prototype还可以存放公共的属性和⽅法 当我们实例化⼀个对象的时候,这个对象⾃带了⼀个 __ proto __ 属性,这个 __ proto __指向创建⾃⼰的构造函数的原型对象。可以使⽤这个原型对象⾥⾯的属性和⽅法

  • js代码组织原则: 数据放在实例上, 方法放在原型上

继承「待学习」

  • 原型继承
  • 借用构造函数继承
  • 最常用的是组合继承:融合原型继承+借用构造函数继承的优点
function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
}
function SubType(name, age){
    // 借用构造函数继承属性
    SuperType.call(this, name);
    this.age = age;
}

// 通过原型继承方法
// 将一个类型的实例赋值给另一个构造函数的原型
SubType.prototype = new SuperType();

SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function(){
    alert(this.age);
}
  • 寄生式继承
  • 寄生组合继承

Function.prototype === Function.proto // true why?

使对象的属性不可更改

  1. Object.freeze(a)
Object.defineProperty(obj, "propery", {
  writable: false,
})
// Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

设计模式

  • 订阅 发布者模式

    class EventEmitter {
      constructor() {
        this.events = {};
      }
    
      on(name, fn) {
        if (!this.events[name]) {
          this.events[name] = [];
        }
        this.events[name].push(fn);
      }
    
      emit(name, ...args) {
        if (!this.events[name] || !this.events[name].length) {
          return;
        }
        this.events[name].forEach(callback => callback(...args));
      }
    
      off(name, fn) {
        if (!this.events[name] || !this.events[name].length) {
          return;
        }
        this.events[name] = this.events[name].filter(cb => {
          // 判断若是相同的引用 则去掉。
          return cb !== fn
        })
      }
    
      die(name) {
        this.events[name] = [];
      }
    }
    
    const instance = new EventEmitter();
    
    const fnTest = (data) => {
      console.log('a', data)
    }
    instance.on('a', fnTest);
    instance.emit('a', 'hello')
    instance.off('a', fnTest)
    instance.emit('a', 'world')
    
  • 策略模式

    策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。在前端开发中,这可以用于实现不同的表单验证规则,或者实现不同的布局策略。

    策略模式的目的就是将算法的使用和算法的实现分离开来。

  • 装饰者模式

    装饰者模式允许我们在运行时动态地给对象添加额外的职责,而不改变其结构。在前端开发中,这可以用于添加事件处理程序,或者动态修改 DOM 元素的样式和行为。

    比如HOC高阶函数就是装饰器。

  • 中介者模式(Mediator Pattern):中介者模式定义了一个对象,这个对象可以封装一组对象的交互方式。这对于处理复杂的、多组件的交互非常有用,例如在实现复杂的用户界面或者单页应用程序时。

  • 单例模式(Singleton Pattern):单例模式确保一个类只有一个实例,并提供一个全局访问点。在前端开发中,这可以用于管理应用的状态,例如在 Redux 中的单一状态树。

  • 工厂模式(Factory Pattern):工厂模式是一种创建对象的模式,它提供了一种将具体创建逻辑与使用代码分离的方式。在前端开发中,这可以用于创建具有相似结构的多个 DOM 元素,或者创建具有相似行为的多个对象。