[笔记]小程序JS 变量的生命周期探究

序言

今天闲逛”小程序开发社区”,无意中看到有一个同行贴出了以下的一段代码。

https://developers.weixin.qq.com/s/mqa4fHme7ZnX

从A页面当中跳转到B页面,点击按钮后,再返回上一页就不能触发按钮对应的事件了。

探究代码

第一次渲染

第一次渲染成功时,用户点击setData测试,按钮能正常触发事件(按钮内容变成setData成功),点击”点击另外打开当前页面”按钮之后,跳转到新的页面。

跳转到第二个页面

此时nodeid发生变化,证实跳转到了新的页面。此时点击左上角的返回按钮后,原来的setData按钮无法触发事件。

分析输出

从代码中我们很容易就能分析出,第一次渲染成功时,that和this指向的页面nodeid应该是acf9ce91,当我们跳转到第二个页面时,此时的that和this就变成了bd622626。

此时神奇的事情就发生了,当我们点击返回按钮回到上一个页面,我们就能看到that的输出值依然是 bd622626 ,但this的输出值就变回了 acf9ce91.

返回第一个页面

由此我的一个想法是,跳转页面只会初始化一个新的页面实例,而不是重新运行这个JS文件.

证明想法

辅助代码

为了证明跳转页面后不是重新运行这个JS文件的想法,我在Page构造器的外部增加了如下的代码.

但我重新渲染页面时,console页面成功输出了2

证明第一次渲染时,小程序是运行过这个JS文件的。但当我们点击跳转到新的页面时,2没有继续出现,此时我认为小程序在新的页面当中,小程序是没有运行过这个JS文件的。

查阅文档

小程序为了实现对视图层标签的管控,开发了一套内置的框架”Exparser框架”。在该框架当中,页面的渲染是基于Exparser框架提供的Page和Component构造器的注入的数据的。

文档链接:https://developers.weixin.qq.com/ebook?action=get_post_info&token=935589521&volumn=1&lang=zh_CN&book=miniprogram&docid=0000aac998c9b09b00863377251c0a

暂时性结论

按照我的个人理解,启动的时候,小程序就已经提前把页面的模板注册好了。如果后面再次访问该页面时,仅仅是会基于页面模板创建出新的页面实例。也就是只有Page()构造器里面定义的data,在同一个页面下,实例与实例之间是独立的,但是Page外部定义的变量是共用的

后续

如果未来有能力的话,希望可以反编译一下小程序的基础库,从源码正向来理解小程序页面渲染的流程。

引用

  1. 教程|《小程序开发指南》
  2. 同一page页面重复打开时,页面js里声明的变量会互相污染?

[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”,在配置后,问题解决

[笔记] 设计模式 面向对象设计原则

  • 课程目标
    • 面向对象设计原则
  • 原则概述
    • 可维护性
      • 指的是软件可以很方便的理解、改正、适应和拓展的难易程度
      • 就是方不方便修改了的意思
    • 可复用性
      • 指的是软件能被重复使用的难易程度
    • 指导性原则
    • 用于评价一个设计模式的使用效果的重要指标之一
    • 为支持可维护性复用诞生
  • 单一职责原则
    • 一个类制作一件事
    • 作用
      • 用于控制一个类的粒度
      • 保证一个类里面不用实现所有的功能,仅仅需要实现单一职责即可
    • 定义
      • 一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中
    • 就一个类而言,应该仅有一个引起变化的原因
  • 开闭原则
    • 不修改源代码,只添加代码
    • 定义
      • 软件实体只能增加功能,尽量不修改功能
      • 尽可能只是拓展代码来增加新功能/修改原有功能
    • 关键
      • 抽象化
    • 具有稳定的抽象层+灵活的具体层
    • 修改配置文件,增加类,这个些操作都符合开闭原则
  • 里氏代换原则
    • 父类对象可被子类对象代替,且程序将不会产生任何错误和异常(因为子类把父类的一些方法重载了)
    • 在程序中尽可能使用基类类型来对对象进行定义
  • 依赖倒转原则
    • 编程使用配置文件,仅仅是接受对象操作对象,在外部进行创建对象注入
  • 接口隔离原则(网络上的那种API接口)
    • 一个接口尽可能做少的事情
    • 把接口都拆开来
  • 合成复用原则
    • is-a继承复用  has-a组合/聚合复用
    • 就是类似于把mysql的obj反射出来,然后再操作
  • 迪米特原则
    • 减少对象之间的交互
    • 两个对象不应该发生任何直接的相互作用
    • 通过”第三者”转发这个调用
    • 引入一个合理的”第三者”来降低现有对象之间的耦合度

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

第一章

  • 课程目标
    • 统一建模语言
  • 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。

探究php的MD5函数输出的原始二进制数据是啥?

前序

最近我的朋友(Ge15emium)在研究CTF,发给我一个关于使用MD5函数实现sql注入的博文。文章通过构造特殊的字符串通过md5函数输出后,可以构造成注入SQL,拿到Flag。
博文链接:http://mslc.ctf.su/wp/leet-more-2010-oh-those-admins-writeup/?tdsourcetag=s_pcqq_aiomsg
通过文章我们知道作者通过使用md5()对ffifdyop进行加密处理后可以构造出‘or’6<trash>这样一个字符串,来规避掉php的代码检查,进而获得Flag。

本文主要探究的是md5函数怎么把32位16进制的报文摘要输出成为目标字符串,不涉及CTF的解密流程以及MD5的加密流程


1.怎么对字符串进行的加密?

php的md5()具有输出原始二进制数据的特性。

通过文章我们得知,博主通过使用php的md5(“ffifdyop”,true)输出其加密后的原始二进制数据得到目标字符串。从这个流程中我们进而引出下一个问题原始二进制数据是啥?怎么来的?


2.原始二进制数据是啥?怎么来的?

原始二进制数据不是指100111这些二进制数据,而是原始字符串转换成ascii码后组成的字符串。
接下来就使用前序中所说的字符串来演示如何转换。

  1. 使用md5(“ffifdyop”)进行加密
    通过加密后将会得到之后的32位16进制字符串:276f722736c95d99e921722cf9ed621c
  2. 将32位16进制字符串按照2个字符为一组切割成为16组16进制的字符串
    切割成27,6f,72,27,36,c9,5d,99,e9,21,72,2c,f9,ed,62,1c
  3. 将每一组16进制的数值转换成2进制
    100111,1101111,1110010,100111,110110,11001001,1011101,10011001,11101001,100001,1110010,101100,11111001,11101101,1100010,11100
  4. 将每一组2进制数值转换称为10进制数值
    39,111,114,39,54,201,93,153,233,33,114,44,249,237,98,28
  5. 最后对照如下的ASCII码表即可翻译的出最终的原始二进制字符串

    以前四组为例39=>’,111=>o,114=>r,39=>’,最终的组成’or’这一个字符串
    (其实理论上我们可以直接把16进制转换成10进制就好了,但是此处是为了展示我的思考过程才会有2进制这一步,希望写细一点可以帮助大家的理解和认识)

结尾

CTF的注入玩法真的是神仙玩法,平常使用md5函数都还没思考过这个问题,借由此机会也提升了我的php基础知识,使其更加扎实可靠,作为2020第一篇原创文,希望日后也能多写一些这样的文章吧。