React脚手架V1.0

背景

最近开始捯饬React,想着学会了React就能把React Native给学习了,然后就能写App。为了能够在React Native上少踩坑,我搭建这个脚手架之后,准备开发一个博文发布系统,作为DEMO练练手。

依赖构成

  1. React核心
  2. React-router-dom 路由
  3. Redux 状态管理
  4. ESlint 代码格式管理
  5. Vite 打包工具
  6. Axios 网络请求库
  7. Antd UI库

搭建流程

1. 初始化React

1.1 模板初始化

我们可以直接使用Vite自带的模板,初始化React,在本教程中我使用的是template指令来初始化项目。

运行以下指令初始化项目

 # npm 6.0
 npm init @vitejs/app gublog(项目名称) --template react(模板名称)
 ​
 # npm 7.0+ (需要额外的双横线)
 npm init @vitejs/app gublog(项目名称) --(额外的双横线) --template react(模板名称)

运行指令后,npm将创建一个以项目名称命名的文件夹📁,来初始化项目。

初始化完成后的文件树结构为:

 root
 └── gublog(项目名称)
    ├── dist
    ├── node_modules
    ├── src
    ├── index.html
    ├── package-lock.json
    ├── package.json
    └── vite.config.js

1.2 启动环境

  1. cd 项目文件夹
  2. npm install #下载依赖
  3. npm run dev #启动开发环境
环境启动成功

出现上图的网页时,我们就已经搭建出Vite + React的开发环境

2. 安装React-router-dom

为了能够实现无刷新切换页面,此处我的脚手架也会引入官方提供的Router来做前端路由管理。

2.1 安装依赖

运行指令安装路由组件

 npm install react-router-dom --save-dev

2.2 注意⚠️

React-Router-Dom就是React Router V4的浏览器版本,原来文档的Router要被替换成BrowserRouter才能使用。为了不使用方面,我们可以使用as关键字来修改别名。

 import 'BrowserRouter' as 'Router' from 'react-router-dom'

这里我真的很想吐槽,搞一个React-Router就好了嘛🤷‍♂️,干嘛还搞一个React-Router-Dom。这东西本质上就是套娃再封装📦。而且最近的V4版本还舍弃了旧的Router标签,改成了BrowserRouter。真的是NT。

解决方法来源:

3. 安装Redux

安装Redux主要是为了使得组件的耦合降低,使得更容易的管理状态,因此被打包到脚手架当中。

3.1 安装依赖

 npm install redux --save-dev

4. 安装ESlint

4.1 安装依赖

 npm install eslint --save-dev

5. 安装Axios网络请求库

5.1 安装依赖

 npm install axios --save-dev

6. 安装AntdUI库

AntdUI是阿里针对React设计的一个UI库,开箱即用,有丰富的文档。(而且还能改颜色)

6.1 安装依赖

 npm install redux --save-dev

6.2 引入环境

  1. main.css中添加import 'antd/dist/antd.css'导入CSS库
  2. Main.jsx的代码头部添加import {组件名称} from antd导入组件

结尾

这篇文章就此告一段落。Axios和ESlint的使用我还在摸索中,后面还会继续更新,敬请期待。目前在使用这个脚手架重写GuBlog日志程序当中,后面时机成熟后也会同新版本的脚手架文章一同推出。

PPG式心率测量小程序的技术分享

故事

背景

去年6月份的时候,参加了微信小程序官方举办的《高校微信小程序开发大赛》,做出了《活力健身房》这个小程序。按照初版设计,除了可以检测用户的跑步数据外,还可以测量用户的静止心率。但由于个人除了要准备比赛,还要准备痛苦的概率论补考,所以导致日程太过于紧凑,因此在该情况下,我就只完成了第一个目标而忽略了第二个心率测量的目标没有实现。

改进

在暑假期间,处于追求完美的想法,我花了一部分的时间继续学习并完成了心率检测算法的V1.0版本。而后再在这周,时隔一年的时间,重写了检测算法,推出了V1.1的版本。(至于为什么算是1.1版,在文中会说明的😂)。

技术分享

今天我写这篇文章的主要目的是跟大家分享以下的三点内容:

  1. PPG式心率测量的原理
  2. PPG式心率测量的实现
  3. 应用到小程序端的难点

1.技术原理

光体积变化描记图法 (Photoplethysmography,简称PPG) 是借光电手段在活体组织中检测血液容积变化的一种无创检测方法。当一定波长的光束照射到指端皮肤表面,每次心跳时,血管的收缩和扩张都会影响光的透射 (例如在透射PPG中,通过指尖的光线) 或是光的反射 (例如在反射PPG中,来自手腕表面附近的光线)。当光线透过皮肤组织然后再反射到光敏传感器时,光照会有一定的衰减。像肌肉、骨骼、静脉和其他连接组织对光的吸收是基本不变的 (前提是测量部位没有大幅度的运动),但是动脉会不同,由于动脉里有血液的脉动,那么对光的吸收自然也会有所变化。[1]

PPG检测的核心是通过心脏跳动影响血管中血液光线的吸收率变化,进而影响血液对光的反射率,估算受测者的心率数据。用人话说,心脏的跳动,会影响血液反射光的强度。反映到图表上,就如下图所示,心脏的舒张和收缩,会影响光强变化,形成下图的波动曲线。而心率的估算,则是通过计算60s内,有多少个波峰或者波谷。比如60s内,图表上产生了60个个波峰,则意味着1s中,心脏收缩和扩张了一次,即跳动了一次,进而可以得出60BPM的心率数据。

PPG传感器工作原理

2.技术实现

根据技术原理可以知道,PPG检测中我们需要做的事情是,计算一段时间内出现了多少个波峰,即可估算出受测者的心率。由此我们需要考虑实现以下的几个功能:

  1. 获取视频流中的某一帧
  2. 检测传感器是否被覆盖
  3. 获取当前帧的光强数据
  4. 计算波峰个数
  5. 计算BPM

1.获取视频中的某一帧

在当今社会中,人人都会有一台的智能手机。而手机上最好的颜色传感器就是摄像头了,因此在小程序端的实现中,对于数据的来源主要是摄像头。

为了实现方便,这次我依旧是使用小程序作为我的开发平台。之所以使用小程序的核心原因是官方提供了摄像头的API,使得我可以通过简单的$API$就可以获取到来自用户摄像头的视频流数据,并且不用考虑兼容性的问题。

在小程序中,我是用的是wx.createCameraContext()API来获取到视频流,同时借助onCameraFrame()来直接获取到每一帧的视频信息,极大的降低了开发的难度。通过前述的API,我们可以直接获取到当前帧Uint8数组,也就是每个像素的RGBA值。

2.检测传感器是否被覆盖

为了避免外部光照影响血管对光线的反射,因此需要受测者的手指完全覆盖摄像头。为了检测摄像头是否覆盖,我们可以通过经验主义设定一个阈值,当每个像素的实际光强小于阈值,则代表传感器完全被覆盖,此时就可以开始测试。

3.获取当前帧的光强数据

通过API,我们可以获得视频流中特定的一帧的Uint8数据。之所以使用Uint8是因为在该RGBA中,每一个通道的颜色只有256种,而我们仅仅是需要获取该帧中的光强。因此选用Uint8可以大幅度减少运算量,提高运行效率。

通过经验主义的测量以及结合论文[2] [3]的理论支持,我们可知血液在红光和绿光的照射下的吸收率是最大的,也就意味着强度值是最大的。因此可以更好的检测到强度变化,所以在后续的开发当中,我们都将使用单个像素中的红绿通道的强度值,来作为数据的来源。由于只使用了其中两个通道的强度值,我们的运算量就可以几何级数减少。

从原来的 2^{32} 个数据转变为了 2^{16} 个数据

获取光强的方法是:

该帧的光强值 = \frac{单个通道的强度值}{像素的个数}

4.计算波峰个数

在1.0版本中我的实现方法是通过经验值测算出一条固定的基线,来判断历史采集的光强值,有多少个跃出基线的数据,就计作多少个波峰。

V1.0 固定基线法

但这种设计是有问题的,比如最右侧的波峰,因为各种外部原因强度没有达到基线,导致丢失。当通过人眼的辨识,这个波峰不属于噪声,应该是要被记录的。

另外通过测试我们可以很容易的发现下图的假波峰和假波谷的噪声信号,这种变化幅度过大的数据也应该是被丢弃的。

噪声曲线

因此在1.1版本中,基线的计算方法做了改进。通过动态基线可以适应复杂环境光线的变化,提高测量的效率和准确性。

V1.1 动态基线法

通过动态基线,判断有多少多少次实际的跃出,进而换算出有多少个波峰。

5.计算BPM

仅仅记录波峰个数是远远不够的,我们还要计算一次跃出所消耗的时间(从波谷到跃出基线到波峰所消耗的时间)。因为受测者的光线环境如果不稳定,含有频繁的光线变化,则可能导致光强变化幅度过大,造成数据的计算误差。因此我们还需要将跃出时间作为计算的一个因素,计算平均的跃出时间。最后计算出每分钟的心率次数:

BPM = \frac{测量最大时间}{平均跃出时间}

小程序端的技术难点

到目前为止,小程序端最大的难点不在于测量算法的开发上。反而是在优雅的展示上画了大量的时间。

  1. FrameData大小
  2. Canvas绘图效率低

1.FrameData大小。通过Camera组件,我们可以直接获取到手机的视频流。由于我们这个算法中仅仅需要计算帧的光强,所以仅仅需要最低质量的FrameData即可。即降低了资源的消耗也提升了算法运行的效率。通过frame-data属性,修改为small即可启用最低质量的FrameData。

2.Canvas绘图效率低。在去年小程序退出了Canvas 2D的绘图API,可以实现同层渲染。为了更高的渲染效率,我自然就是选择并使用了最新的API进行曲线的绘制。在电脑模拟器上的表现确实印证了效率更好的这一说法。但诡异的是在Android端上,Canvas2D的运行效率非常低,导致经常性假死,不知道怎么修复。因此只能暂时被搁置,待到日后有能力后再做修复。

收获

这次的开发通过学习更多前人的研究结论来帮助我将算法优化出来,收获满满。感觉到非常的振奋人心。期待后面能继续搞点好玩的DEMO。

未来的方向

最近看到Tensorflow的BlazeFace。可以用很小的体积的模型来识别人脸,感觉可以借此,结合PPG算法,实现rPPG式的检测算法,日后有时间可以继续搞一下,感觉会非常的好玩。同时Canvas的问题也需要被修复,或许能够通过WebGL来修复?但是我还不肯定,需要后续的实验才能证明,待到后面再修吧。

结尾

由于本人不是电子工程专业,所以上文中可能会涉及到部分不规范的说明和解释。如果您能够在评论中帮助我指出来并帮助我改正,我将十分感激您,谢谢。

参考

[1] ECG/PPG量测解决方案

[2] 不同波长近红外光下手掌静脉图像质量分析

[3] 血液成分对光吸收规律的实验研究

为什么图片转Base64会多出30%的数据量

背景

由于最近在准备🐧厂的面试,被问及是否知道为什么url-loader将图片转换成Base64后,会有增加33%的数据量。因此,我开始从晚上搜集这方面的资料,但遗憾的是,大多的博文的关注点基本都是在实际应用(Webpack,前端优化…)或者是Base64如何进行编码,没有很清晰的阐述为什么会多出33%的数据量的过程和原因。因此,我希望可以通过这篇博文补充这方面的讯息。

为什么会多出30%的数据

当我们把3个Byte的数据转换成为Base64的字符时,根据Base64的规则,3个Byte的字符会转换成24个二进制位。Base64一位只有6个位二进制位,因此会产生4个Byte的字符。

但是现代计算机系统当中,一个字符要有8个二进制位来存储因此,4个字符对应的6个二进制位都要补充2个0,来转换成8个二进制位的字符存储到计算机当中。因此3个Byte(24位二进制位)的数据要用4个Byte(32位二进制位)来存储。并且32位中只有24位是用来存储数据的,剩下8位都是无用的补充位。

因此我们可以很简单的计算出输出:输入 = 4:3 (33% 数据冗余). 与此同时,我们同样可以总结出,输入n Bytes,输出就有(下图)那么长。

{\textstyle 4\left\lceil {\frac {n}{3}}\right\rceil }

总结

核心思路,原来三个Byte的数据,转换成Base64后,要用四个Byte来存储。多了1/3(33%)的数据要存储,所以数据量会增多。

把照片打包进bundle.js中,看来还是要因地制宜的。url-loader默认数值应该是8192bit(1024 byte),增大33%后就是增加了300byte。因此,url-loader主要还是用于打包只有几个像素的小文件比较适合。不会对整体的加载速度有过多的影响。

引用

https://en.wikipedia.org/wiki/Base64

http://www.ruanyifeng.com/blog/2008/06/base64.html

[NodeJS]非抢占式多级反馈队列调度算法

前序

《操作系统》课程最近布置了一个大作业,要求我们每人实现一个非抢占式多级反馈队列调度算法的模拟程序,作为期末考核的一部分。(u1s1,真的是爽到,老师开心,我们也写的开心!)

实现代码

//Powered By 7gugu

//每一级队列的可运行时间
let timeSlice = [1, 2, 3];

//准备运行的队列
let preRunQueue = [];
//3级运行队列
let runQueue = [
  [],
  [],
  []
];

//准备运行的程序[开始时间, 运行时间]
let progs = [
  [0, 8],
  [1, 4],
  [5, 1],
  [3, 7],
  [4, 2]
];

for (let i = 0; i < progs.length; i++) {
  let prog = {};
  prog.id = "P" + i;
  prog.startTime = progs[i][0];
  prog.runTime = progs[i][1];
  prog.priority = 0; //设置最高优先级
  preRunQueue.push(prog); //把待运行的程序导入运行序列
}

//总运行时间
let totalTime = 0;

while (true) {
  //如果待运行队列中仍然有程序 & 程序已到达开始时间
  if (preRunQueue.length > 0) {
    let prog = preRunQueue[0];
    if (prog.startTime <= totalTime) {
      preRunQueue.shift(); //直接弹出队头的元素
      runQueue[0].push(prog); //把程序加入0级运行队列中
      console.log(prog.id + "开始运行,开始时间为:" + totalTime);
    }
  }

  for (let i = 0; i < runQueue.length; i++) {
    //如果各级队列还有程序的话,就继续运行
    if (runQueue[i].length > 0) {
      let prog = runQueue[i].shift(); //获取各级队列中第一个程序
      if (prog.runTime > timeSlice[i]) {
        //程序运行时间比时间片大
        totalTime = totalTime + timeSlice[i]; //总运行时间累加
        prog.runTime = prog.runTime - timeSlice[i]; //减去每一次运行的时间
        if (i != runQueue.length - 1) {
          //如果未处于最低优先级,则把程序放在下一个优先级队列中
          runQueue[i + 1].push(prog);
        } else {
          //如果处于最低优先级,则把程序放回最低优先级中运行
          runQueue[i].push(prog);
        }
      } else {
        //程序运行时间比时间片小
        totalTime = totalTime + prog.runTime;
        console.log(prog.id + "运行完成,目前时间为:" + totalTime);
      }
      break;
    }
  }
} 

运行结果

仓库地址

参考资料

计算机操作系统(第四版) 西安电子科技出版社

ES6中的箭头函数()=>与function的区别

  1. 写法不同
  2. this指向不同
  3. 构造函数
  4. 变量提升

1.写法不同

//function
function x(a, b){
  return a + b;
}
//箭头函数
const x = (a, b)=>{
  return a + b;
}

2.this指向不同

使用function定义的函数,this的指向随着调用环境的变化而变化的,而箭头函数中的this指向是固定不变的,一直指向的是定义函数的环境。

function x(a, b){
  console.log(this);
}
const obj = ()=>{
  test: 7gugu,
}
x(); //Window
obj.test(); //obj { test: 7gugu }

使用function定义的函数中this指向是随着调用环境的变化而变化的

//使用箭头函数定义函数 
var foo = () => { console.log(this) };
var obj = { aa:foo };
foo(); //Window
obj.aa(); //Window

明显使用箭头函数的时候,this的指向是没有发生变化的。

3.构造函数

//使用function方法定义构造函数 
function Person(name, age){     
  this.name = name;   
  this.age = age; 
} 
var lenhart =  new Person(lenhart, 25);
console.log(lenhart); //{name: 'lenhart', age: 25}
//尝试使用箭头函数 
var Person = (name, age) =>{     
  this.name = name;   
  this.age = age; 
}; 
var lenhart = new Person('lenhart', 25); //Uncaught TypeError: Person is not a constructor

function是可以定义构造函数的,而箭头函数是不行的。

4.变量提升

由于js的内存机制,function的级别最高,而用箭头函数定义函数的时候,需要var(let const定义的时候更不必说)关键词,而var所定义的变量不能得到变量提升,故箭头函数一定要定义于调用之前!

foo(); //123
function foo(){     
  console.log('123');
}  
arrowFn(); //Uncaught TypeError: arrowFn is not a function 
var arrowFn = () => {     
  console.log('456'); 
};

转载自:https://blog.csdn.net/github_38851471/article/details/79446722

Vue Production环境中Proxy无效的解决思路&方法

问题

最近上线Vue项目到服务器,上传之后就出现了,代理404的问题。Dev环境中的代理是工作正常的,这点让我很疑惑,但这恰巧是我的一个误解,下面是这次的解决思路。

解决思路

一开始,我以为vue.config.js中的,devServer中的proxy是在路由(Router)层面做的数据转发,所以在这上面花了一些时间进行研究。后续通过查阅官方文档发现,devServer配置的是一个nodejs的测试服务器参数,而不是路由参数后,恍然大悟。(这就是我的误解所在)

解决这个proxy的方向,应该是关注于配置自身的HTTP服务器的代理上面,如果是Nginx,就要配置Nginx的路由转发;我这里用的是Apache作为我的HTTP服务器,所以应该配置的是对应的代理参数。

配置步骤

HTTP服务器部分

1.打开Apache的httpd.conf,开启以下两个proxy拓展,保存

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so 

2.在https.conf中配置监听接口,保存

3.打开httpd-vhost.conf如下图配置即可

4.此时HTTP服务器部分就配置完成了,此时仅需重启Apache服务器即可生效。

Vue部分

因为是使用了History模式的路由,所以为了正确路由到相关的页面,还需要配置相关的PathRewrite才能正确路由。

1.在vue生成生产环境文件的文件夹中,添加.htaccess文件(我使用的是默认参数,所以就在dist文件夹中)

2.配置以下参数即可。(参数参考官方文档进行配置)

3.至此Vue部分配置完成,重新访问404的问题就消除了。

参考文献

  1. vue项目使用history模式打包应该注意的地方
  2. 前端用vue 上传项目,apache服务器 成功中转接口代理
  3. Apache的ProxyPass简单使用
  4. vue项目上线apache反向代理配置跨域
  5. apache proxy作用——ProxyRequests

Vue import不识别 Unexpected Token (xx:xx)

Bug出现

最近在给工作室打工的时候,Vue做了个静态路由懒加载。

然后编译的时候死活不认这个import,截图如下,完全没有解决的思路。

解决思路

  1. 同事编译莫得问题,遂排除是代码问题
  2. 清除node_module,重新npm install,无效,排除是Node的问题
  3. 因为其他位置的import工作正常,遂排除babel未启用
  4. 最后通过下载安装”syntac-dynamic-import”,在配置后,问题解决

[笔记] 设计模式: 统一建模语言

第一章

  • 课程目标
    • 统一建模语言
  • UML
    • 统一建模语言
    • 概念
      • 通用的可视化建模语言
      • 通过一些标准的图形符号和文字对系统进行建模
    • 作用
      • 对于软件进行描述、可视化处理、构建软件系统的文档
    • 视图(View)
      • 用户视图
        • 以用户的观点来标识系统的目标,它是所有视图的核心
        • 描述系统的需求
      • 结构视图
        • 表示系统的静态行为
        • 描述系统的静态元素,如包、类与对象,以及它们之间的关系
      • 行为视图
        • 表示系统的动态行为
        • 描述系统的组成元素如对象在系统运行时的交互关系
      • 实现视图
        • 表示系统中逻辑元素的分布
        • 描述系统中的文件以及它们之间的关系
      • 环境视图
        • 表示系统中物理元素的分布
        • 描述系统中的硬件设备以及它们之间的关系
      • 视图关系图
  • 图(Diagram)
    • 用例图
    • 类图
  • 模型元素(Model)
    • 模型元素
      • 就是图上面的符号
      • 用于描述事物以及事物与事物之间的关系
    • 每一个模型元素都要一个阈值相对应的图形元素
    • 同一个模型元素可以在不同的UML图中使用
    • 定义
      • 封装了数据和行为
      • 是具有相同属性、操作、关系的对象集合的总称
      • 一个类可以有多个职责(功能),但设计的好的类通常只有单一职责(功能)
    • 类的属性
      • 类的数据职责
    • 类的操作(类实现的方法)
      • 类的行为职责
  • 类图
    • 用来描述不同的类以及它们之间的关系
  • UML类图
    • 类名
  • 类的属性
  • 类的操作(其实就是类实现的方法)
  • 类之间的关系
    • 关联关系
      • 表示一类对象与另一类对象之间的关系
      • 实线连接
      • 通常将一个类的对象作为另外一个类的成员变量
      • 单向关联
  • 双向关联(就是两个类互相作为对方的成员属性)
  • 自关联(就是自己的成员属性存储了一个自身的对象)
  • 多重性关联(就是有2个+的成员变量存储/被存储了)
    • 多重表示,通常在直线上使用一个数字/数字范围来表示
  • 图例
  • 聚合关系
    • 表示整体与部分的关系(容器与成员的关系)
    • 成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立存在
    • 使用带空心菱形的直线表示
    • 图例
  • 组合关系
    • 聚合关系相同的表示
    • 成员是整体的一部分,但是成员对象跟整体对象的生命周期一样,整体对象销毁了,成员对象也会被销毁
    • 使用带实心的菱形直线表示
    • 因为是在整体(Head)对象中new的对象,销毁了head,就会连带把成员对象都销毁掉
  • 依赖关系
    • 表示一个事物使用另外一个事物
    • 体现在某个类的方法使用另一个累的对象作为参数
    • 使用带箭头的虚线
    • 依赖的一方指向被依赖的一方
  • 三种实现方法
  • 泛化关系(继承关系)
    • 用于描述父类与子类的关系
    • 用带空心三角形的直线表示
    • 图例
  • 接口与实现关系
    • 接口之间也存在继承关系和依赖关系
    • 接口与雷志坚存在一种实现的方法
    • 使用带空心三角形的虚线表示
    • 图例
  • 拓展机制
    • 注释
      • 可以给类图(UML)增加注释

[微信小程序] 开发心得Vol.1

前言

最近开始学习ES6的语法,然后突然想到去年挖下来的小程序的坑还没有填完,就开始捣鼓起来了。接下来会分享一些我在这次开发过程中遇到的坑点和经验,由于小程序是微信发起来的,所以有很多问题都可以尝试在小程序的开发社区里面搜一下。

UI框架推荐

这次最蠢的一件事就是手莽课表,殊不知已经有前人开发出了插件。自己写CSS实在是痛苦。有一部分的UI也用了微信的WeUI,但其实还有更多选择:

  1. WeUI官方样式库
  2. iView
  3. ColorUI
  4. Vant

通过这几个组件库可以快速构建小程序,不用花过多的时间在UI上,很适用于我们这些后端开发者。

SSL配置

如果你的小程序需要向你的服务器发起数据请求的话,就要留意这一点。小程序文档中要求你的后端接口的网址必须满足以下两点:

  1. HTTPS
  2. 已经备案

无论你的主机在哪都要给你的域名备案,不然真机模拟的时候就永远都无法请求,会报图1.1的问题。

Provisional headers are shown

如果你的证书已经备案了,还是报这种错误的话,那就是你的接口缺少了证书。一般tx云或者阿里云的免费证书就够用了,直接申请就可以。

如果还是报错,那就是缺失了中间证书。具体体现就是你的浏览器可以直接访问,并且确实走的是HTTPS,但就是无法请求,你可以通过这个网址:https://www.myssl.cn/tools/check-server-cert.html检测是否缺失中间证书。一般是需要把root和domain name的证书打包在一起才行,就是用.pem和.key后缀的文件作为证书文件绑定到你的Http服务器上。

JS平台差异

这个点主要是出现在Date对象上面

//Android平台上这么写将会正常返回对象
let obj = new Date("2020-02-27");
obj.getDay();
//IOS平台只支持用"/"来分割时间
let obj = new Date("2020/02/27")
//如果你使用"-"来创建对象的话,ios将会返回null

这个是我目前认为小程序最为诟病的一个问题。就是多平台JS不通用。小程序的本质上还是VUE,样式基本上没啥问题,但js出现了bug就真的特别难复现,这个bug甚至在控制台中是无法找到的,实在是恶心,希望微信会尽快修好。

结尾

其实小程序是一个很好的分发平台,但是太多毛刺会减弱大家的开发积极性,希望以后会越做越好。也希望能帮到你,Peace

JavaScript OOP笔记[ES3]

简介

最近开始要学习ES6了,翻出JS看了看,发现OOP部分还没有掌握,所以就赶紧进行了补课。下面是这次学习的一些,个人认为重要的知识点。

对象

每次使用JavaScript的构造器时,都会创建一个对象。一个初始化的对象中将会含有一个属性集,称之为prototype(对象属性),还有一个constructor(构造器)。而属性集里面中则会默认存在一个属性_proto_(原型)。这个属性存储的是父类的prototype(可以理解为指向父类的一个指针)。

对象属性

访问对象属性

对象.prototype.属性名

创建对象属性

对象.prototype.属性名 = 值

继承

JavaScript中实现继承主要是通过修改对象的_proto_(原型)指向到父原型来实现的继承。一旦理解了就会很简单,但是这个设计真的不好,而且原型这个称谓真的太容易混淆了。

注意!

//错误的继承
student.prototype = person.prototype;

解释:因为如果是这么赋值的话,在后续的操作中,比如给student增加属性或者方法时,收student的本质还是person,这样子修改的话,本质上还是修改的person的对象属性。

//正确的继承
student.prototype = Object.create(person.prototype)
//此时就可以正确把student的_proto_正确的指向person.prototype了

(JavaScript在ES6中貌似已经引入了extend应该能改善当前这个反人类的设计了)

原型

原型就是对象上一个存储父类的属性,称之为_proto_。由于Object是顶层对象,所以它的原型就是NULL。

原型链

bosn的原型指向Student,Student的原型又会指向Person,Person的原型又会指向Object,则称这条联系为原型链。

InstanceOf

该方法用于判断方法右边的函数是否存在于左边对象的原型链中,返回一个Bool值。其原理还是通过遍历prototype来看看左边的原型链上面是否存在右边函数的prototype。

用途:判断对象是否存在该方法(函数)

尾言

说实话,平时对于JavaScript的应用还停留在普通的事件应用,函数闭包这种层面,第一次了解了关于JS的面对象过程,也是受益匪浅,希望可以帮助到后续的ES6的学习,如果你觉着这篇文章好的话,不妨点一个赞吧,如果我理解有错误,也欢迎在下方评论区中学习交流owo。