0%

inventing js

关于0.1+0.2==0.3 false

0.1 + 0.2不等于0.3?为什么JavaScript有这种“骚”操作?
关于浮点数的二进制表示,js浮点数精度(存目)

保留2位小数

Number.prototype.toFixed 返回指定小数位数的字符串 必要时四舍五入 且必要时以0补足位数
返回number的方法不如用类似 Math.round(num * 100) / 100 保留两位小数

动态语言

Dynamic Programming Language 动态语言或动态编程语言,程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。动态语言如Javascript Python Ruby等 C/C++ Jave则不是

另外 图灵完备

JS Tips

Caps lock sensitive

1
2
var input = document.getElementsByTag('input')[0];
input.addEventListener('keyup',function(event){console.log(event.getModifierState('CapsLock'))});

getModifierState方法挂在KeyboardEvent或者MouseEvent上,可获取的键盘状态见MDN:KeyboardEvent.getModifierState()

运算符

  • 幂运算 2**10=1024

this

this的设计目的是在函数体内部,指代函数当前的运行环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var f = function () {
console.log(this.x);
}

var x = 1;
var obj = {
f: f,
x: 2,
};

// 全局环境执行
f() // 1

// obj 环境执行
obj.f() // 2

另外,严格模式(‘use strict’)下,全局环境下运行时,this不会自动绑定到‘全局对象’上,将变量绑定到全局对象需要显式调用形如global.name = ‘Global’

常用处理

应用场景标题 描述 补充1 补充2
数组去重 Array.from(new Set(array)) 通过内置数据解构特性进行去重 [] => set => [] 通过遍历并判断是否存在进行去重[many items].forEach(item => (item <不存在于> uniqueArr) && uniqueArr.push(item))
数组的最后一个元素 获取数组中位置最后的一个元素 使用array.at(-1)
数组对象的相关转换 对象到数组:Object.entries() 数组到对象:Obecjt.fromEntries()
短路操作 通过短路操作避免后续表达式的执行 a或b:a真b不执行 a且b:a假b不执行
基于默认值的对象赋值 通过对象解构合并进行带有默认值的对象赋值操作 {...defaultData, ...data}
多重条件判断优化 单个值与多个值进行对比判断时,使用includes进行优化 [404,400,403].includes
交换两个值 通过对象解构操作进行简洁的双值交换 [a, b] = [b, a]
位运算 通过位运算提高性能和简洁程度 按位与(&)或(\ )按位取反(~) 取整~~、<<、>> >>>移位
replace()的回调 通过传入回调进行更加细粒度的操作
sort()的回调 通过传入回调进行更加细粒度的操作 根据字母顺序排序 根据真假值进行排序

call 和 apply

1
2
3
4
5
6
7
8
9
10
11
12
// 已有构造函数
function Product(name, price) {
this.name = name;
this.price = price;
}
// 在新函数中复用已有逻辑
function Food(name, price) {
Product.call(this, name, price); // 实际上相当于将构造函数Product的两行拿过来
this.category = 'food';
}
// test
const bread = new Food('bread', 0.8) // Food {name: "bread", price: 0.8, category: "food"}

FunctionX.call(thisArg,…args)中的第一个参数也可以是对象,函数相当于将剩余参数以FunctionX的实现方式应用到该对象内返回一个结果
apply与call的区别仅在:剩余参数是数组,即FunctionX.call(thisArg,[…args])

bind

bind是基于call或apply实现的函数原型方法 FunctionX.bind(thisArg,…args)返回一个新的函数,该函数套用FunctionX的样子,但以thisArg代替FunctionX的this,剩余参数填补FunctionX所需参数(即结果函数所需参数为FunctionX所需参数减去…args)
模拟实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Function.prototype.bind = function (context) {
// 调用 bind 的不是函数,需要抛出异常
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}

// this 指向调用者
var self = this;
// 实现第2点,因为第1个参数是指定的this,所以只截取第1个之后的参数
var args = Array.prototype.slice.call(arguments, 1);

// 创建一个空对象
var fNOP = function () {};

// 实现第3点,返回一个函数
var fBound = function () {
// 实现第4点,获取 bind 返回函数的参数
var bindArgs = Array.prototype.slice.call(arguments);
// 然后同传入参数合并成一个参数数组,并作为 self.apply() 的第二个参数
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
// 注释1
}

// 注释2
// 空对象的原型指向绑定函数的原型
fNOP.prototype = this.prototype;
// 空对象的实例赋值给 fBound.prototype
fBound.prototype = new fNOP();
return fBound;
}

关于替换this的操作可用于回调函数调用类函数, 如下类型A中定义回调函数,该回调函数会作为目标对象B的函数形参,process是类A的函数,但如果在目标对象B中调用callback函数,函数中的this会指向到B
1
2
3
4
5
callback = (response)=>{
this.process(response) // 这里的this指向当前运行环境
}

B.getDataAsync(this.callback)

应使用bind
1
B.getDataAsync(this.callback.bind(this))

柯里化Currying

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
style=function(){this.x="";this.y="";} // 构造函数style
style.prototype.addA=function(){
this.A='A'
return this
}
style.prototype.addB=function(){
this.B='B'
return this
}
style.prototype.addC=function(){
this.C='C'
return this
}
// test
xxx=new style()
xxx.addA()
xxx.addB().addC()

Garbage Collection

  • 游离dom

    1
    2
    3
    4
    5
    6
    let root=document.getElementById("root")
    for(let i=0;i<2000;i++){
    let div=document.createElement("div")
    root.appendChild(div)
    }
    document.body.removeChild(root)

    此时document中移除了dom,但内存中root还在,而且上千个子元素仍然被root对象引用,应设root=null解除引用

  • clearInterval和clearTimeOut

Angular 9 is available, new release switches applications to the Ivy compiler and runtime by default (Feb, 2020)

模板编译

「模版编译」的工作一般是将使用「声明式语言」描述的「模版」转化为「命令式语言」定义的逻辑代码。鉴于这里的主体是 JavaScript,可以理解为「文本」->「函数」的转换。

Angular 模板编译演变

v2 版本中,Angular 采用了和 Svelte 几乎相同的「编译到指令操作」策略,主要区别是对「视图操作」使用的是基于 Renderer 的抽象指令。这种编译方式确实带来了卓越的性能,但由此带来的明显不便之一是生成代码的大小,在 Trotyl Yu:如何评价 angular 2 中的 AoT?中有给出「模版编译」产物的示例,由于太过占用屏幕空间,不在此处贴出。

v4 版本中,Angular 引入了 View Engine 的概念,建立了公共的运行时部分以减少「编译产物」的大小,而「视图定义」部分也不再使用「编译到指令函数」的策略,而是「编译到工厂函数,类似于(详细代码可以参见 【MMR-A】全新的 View Engine 模式):

1
2
3
4
5
6
7
8
9
10
11
function View_MyComponent() {
return viewDef([
elementDef(1, 'div', [['id', 'foo']]),
elementDef(1, 'p', []),
textDef(['Hello ']),
], (check, view) => {
var comp = view.component;
const currVal_0 = inlineInterpolate('Hello, ', comp.name);
check(view, currVal_0);
});
}

注册响应式表单模块

该模块定义了响应式表单涉及到的组件等类型或构造方法
类定义在@angular/forms包中,会在组件中引用,那么为什么要注册模块?

1
2
3
4
5
6
7
8
9
import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }

FormControl

响应式控件类,每个实例对应页面表单中的一个控件,在template中使用formControl=”name”绑定,在FormGroup中使用formControlName=”name”绑定。
edit.component.ts:

1
name = new FormControl('QQstone')

edit.component.html
1
2
<input type="text" [formControl]="name">
<p>{{name.value}}</p>

FormGroup

用于管理一组响应式控件,一个表单可以标记为一个Group,也可以分为多个Group(QQs尚为实践过)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<form #f="ngForm" *ngIf="assetForm" (ngSubmit)="save()" nz-form [formGroup]="assetForm">
<nz-form-label nzSpan="4" nzRequired>Asset Name</nz-form-label>
<nz-form-control nzSpan="8">
<input nz-input formControlName="asset_name" name="name" maxlength="30" placeholder="Name" required />
</nz-form-control>
<nz-form-label nzSpan="4" nzRequired>Status</nz-form-label>
<nz-form-control nzSpan="8">
<nz-select formControlName="status" name="status" nzPlaceHolder="Status"
nzAllowClear required>
<nz-option *ngFor="let item of enum.asset_status" [nzLabel]="item.text" [nzValue]="item.code">
</nz-option>
</nz-select>
</nz-form-control>

</form>

FormBuilder

使用FormBuilder构造FormGroup,每个组件对应一个Array,Array第一个属性是控件初始value,其后是校验器,若存在多个校验器,则该属性为Array
注入 private fb: FormBuilder

1
2
3
4
profileForm = this.fb.group({
name:['', Validators.required],
status:['30']
})

前提 服务号

注册服务号

  • 企业名称
  • 营业执照注册号
  • 验证方式

爬虫(Web Crawler)

郑重声明:本文仅限于编程学习,用于非法目的及造成侵权后果的行为,老子概不负责

http请求

私以为,爬虫程序就是以程序执行代替人工操作,在一定范围的网络资源中找自己要的东西。当人做这项枯燥的工作时,无非输入网址—打开网页—用肉眼识别—下载这样子,下面基本上就是用python模拟这个过程。

1
2
3
4
5
6
7
8
9
10
import requests
import re
response = requests.get('https://qqstone.github.io/qqsnote/2019/10/28/MySQL/')
print(response.text)
if response.text.find('主键'):
print('find it!')
keyUnicode = str('主键'.encode('unicode_escape')).replace('\\\\','\\')[2:14]
print('\S*'+keyUnicode+'\S*')
matchObj = re.findall('\S*'+keyUnicode+'\S*',response.text)
print(matchObj)

反“反爬设置”

有时请求一个网页时,发现无论通过Get或Post以及其他请求方式,都会出现403错误。这种现象多数是由于服务器拒绝了访问,因为这些网页为了防止恶意采集信息,所用的反爬虫设置。

此时可以通过模拟浏览器的header信息来进行访问。

1
2
3
4
5
6
import requests
import re
url='https://www.acfun.cn/a/ac12293064'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36'}
response = requests.get(url,headers=headers)
print(response.text)

上述只是针对user agent检测的手段,通常网站管理员采取的反爬虫基本手段还有封锁IP,检测请求间隔,封锁cookie等,针对这些手段需要采取相应的措施如使用代理,使用sleep模拟点击控制间隔,禁用cookie等

超时处理

使用代理

解析html树

Scrapy框架

依赖:

1
2
3
4
5
pip install Twisted
# windows平台用下面这个
pip install Twisted[windows_platform]

pip install Scrapy

参考 Scrapy文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import scrapy
import re


class QuotesSpider1(scrapy.Spider):
name = "quotes1"
keyword = ""
pages = 75

def start_requests(self):
url = 'https://xxxxxxx.xxx.html'
yield scrapy.Request(url=url, callback=self.parse)
for i in range(2, self.pages):
surl = url.replace('.html', '-'+str(i)+'.html')
yield scrapy.Request(url=surl, callback=self.parse_key_word)

def parse(self, response):
regexp = r'\d{2}'
video_list_tags = response.css('h3::attr(title)')
for vt in video_list_tags:
#if vt.extract().find(self.keyword) >= 0:
match_array = re.findall(regexp, vt.extract())

if len(match_array):
self.log(vt.extract())
filename = 'output.log'
with open(filename, 'a', encoding='utf-8') as f:
f.write(vt.extract()+'-->'+response.url+"\n")

def parse_key_word(self, response):
video_list_tags = response.css('h3::attr(title)')
for vt in video_list_tags:
if vt.extract().find(self.keyword) >= 0:
self.log(vt.extract())
filename = 'output.log'
with open(filename, 'a', encoding='utf-8') as f:
f.write(vt.extract() + '-->' + response.url + "\n")


执行scrapy实例
1
scrapy crawl quotes1

概念

参考:Android 上网概述

使用3g/4g/5g网络的移动设备,通过ppp协议与蜂窝网络的基站建立通信

基带和蜂窝网络
ppp协议是链路层协议

协议结构
Linux对PPP数据链路的建立过程进行抽象,实现了pppd拨号应用程序,专门用于管理PPP数据链路的建立与关闭,见下图。

pppd是一个后台服务进程(daemon),实现了所有鉴权、压缩/解压和加密/解密等扩展功能的控制协议,负责把要发送的数据包传递给PPP协议处理模块,设置PPP协议的参数,用来建立/关闭连接。

Android设备网络访问架构

链路建立的大致过程:
链路建立

( Raspberry Pi Model B Plus Rev1.2 )

setup

官方指引:Setting up your Raspberry Pi

常见异常:HDMI无响应

修改/boot/config.txt

1
2
3
4
5
6
7
hdmi_force_hotplug=1
config_hdmi_boost=4
hdmi_group=2
hdmi_mode=9
hdmi_drive=2
hdmi_ignore_edid=0xa5000080
disable_overscan=1

设置默认命令行启动

1
sudo raspi-config

Boot Options -> Desktop / CLI -> Console
开启ssh server
ssh笔记

GPIO

USB摄像头

Using a standard USB webcam
实践发现,手上的UVC Camera一旦停止调用就会跳出(中断)

搭建直播流服务

4g上网

实践时 电脑USB接口向树莓派供电 插上网卡就电压不足(Under-voltage detected)

lsusb命令识别出” ID 05c6:6000 Qualcomm,Inc.Siemens SG75 “

安装ppp包

内网穿透

ipv6