课程布景:
- 前端的首要编程言语为JavaScript.
- JavaScript做为一种融合了多种编程范式的言语,灵敏性十分高。
- 前端开发人员需求依据场景在不同编程范式间自如切换。
- 进一步需求发明范畴特定言语笼统业务问题。
课程收益:
- 了解不同编程范式的起源和适用场景。
- 把握JavaScript在不同的编程范式特别是函数式编程范式的运用。
- 把握创立范畴特定言语的相关东西和办法。
PS:课程讲师的气泡音实在太刺耳清楚了,学习得花一半的心思去留意他讲了什么。学习很难进入沉溺状况
编程言语
机器言语
第一代核算机(1940年代末至1950年代初):第一代核算机运用的是机器言语,这种言语是二进制的,十分难以阅览和编写
- 乃至一开始他是经过线缆来操控的
汇编言语
汇编言语(1950年代中期):为了使程序员能够更简略地编写代码,汇编言语被发明晰出来。汇编言语是一种更高等级的机器言语,运用助记符来代替二进制代码,使程序员能够更简略地编写和阅览代码
中级言语
中级言语是介于机器言语和高档言语之间的一种言语。它一般是一种可移植的高档言语,但在执行时被转换成机器言语。中级言语具有比高档言语更挨近机器言语的特色,因而它们一般比高档言语更快,但比机器言语和汇编言语更易读和编写。一些常见的中级言语包含C言语和C++言语
C:”中级言语”进程式言语代表
-
可对位,字节,地址直接操作
- 代码中的
*(&x) = 20;
语句能够直接修正变量x
的值,阐明C言语能够对位、字节、地址进行直接操作
- 代码中的
-
代码和数据别离倡导结构化编程
- 代码中的
#include <stdio.h>
语句引进了规范输入输出库,阐明C言语倡导代码和数据别离,支撑结构化编程
- 代码中的
-
功用齐全:数据类型和操控逻辑多样化
- 代码中声明晰整型变量
x
和字符指针变量str
,运用了printf
函数进行输出,阐明C言语的数据类型和操控逻辑十分多样化,功用齐全
- 代码中声明晰整型变量
-
可移植能力强
- 代码中运用了规范输入输出库,这使得代码能够在不同的平台上运转,阐明C言语具有很强的可移植能力
#include <stdio.h> // 引进规范输入输出库
int main() // 主函数
{
int x = 10; // 声明并初始化一个整型变量x
char* str = "Hello, World!"; // 声明并初始化一个指向字符的指针变量str
printf("x = %d\n", x); // 输出x的值
printf("str = %s\n", str); // 输出str所指向的字符串
*(&x) = 20; // 对x的值进行修正,阐明C言语能够对位、字节、地址进行直接操作
printf("x = %d\n", x); // 输出修正后的x的值
return 0; // 回来0表明程序正常完毕,阐明C言语支撑函数回来值
}
C++:面向目标言语代表
-
C with Classes
- C++开始是作为C言语的一种扩展,其基本语法与C言语相同,但增加了类、承继、多态等面向目标的特性,因而C++也被称为C with Classes
-
承继
- 代码中的
class Student : public Person
语句界说了一个Student类,它承继自Person类,这阐明C++支撑承继的特性
- 代码中的
-
权限操控
- 代码中的
public
、protected
和private
关键字用来操控成员变量和成员函数的拜访权限,这阐明C++支撑权限操控的特性
- 代码中的
-
虚函数
- 代码中的
virtual void sayHello()
语句界说了一个虚函数,这阐明C++支撑虚函数的特性。虚函数能够完成多态,即在运转时依据目标的实践类型来调用相应的函数
- 代码中的
-
多态
- 代码中的
void sayHello() override
语句完成了函数的重写,这阐明C++支撑多态的特性。在运转时,假如调用的函数是虚函数,那么实践调用的函数将依据目标的实践类型来确认
- 代码中的
#include <iostream> // 引进输入输出库
// 界说一个类Person
class Person {
public: // 公有权限
// 结构函数
Person(std::string name, int age) : mName(name), mAge(age) {}
// 成员函数
virtual void sayHello() { // 界说虚函数,支撑多态
std::cout << "Hello, I'm " << mName << ", " << mAge << " years old." << std::endl;
}
protected: // 保护权限
std::string mName; // 名字
int mAge; // 年纪
};
// 界说一个类Student,承继自Person
class Student : public Person {
public: // 公有权限
// 结构函数
Student(std::string name, int age, std::string school) : Person(name, age), mSchool(school) {}
// 重写父类的虚函数
void sayHello() override { // 界说虚函数,支撑多态
std::cout << "Hello, I'm " << mName << ", " << mAge << " years old, and I'm studying at " << mSchool << "." << std::endl;
}
private: // 私有权限
std::string mSchool; // 校园
};
int main() {
// 创立一个Person目标
Person person("Tom", 20);
person.sayHello(); // 调用Person的sayHello函数
// 创立一个Student目标
Student student("Jerry", 18, "ABC University");
student.sayHello(); // 调用Student的sayHello函数,完成多态
return 0; // 回来0表明程序正常完毕
}
Lisp:函数式言语代表
- 与机器无关
- 列表:代码即数据
- 闭包
(setq nums `(1 2 3 4)); 数据列表
(setq add `+) ;加操作
(defun run(op exp) (eval (cons op exp)) ) ;将数据构建为代码列表 连接列表
(run add nums) ;运转
JavaScript
-
依据原型和头等函数的多范式言语
-
进程式
- JavaScript开始被规划为一种进程式的脚本言语,它能够在Web浏览器中嵌入HTML页面,完成动态交互效果
-
面向目标
- JavaScript是一种支撑面向目标编程的言语,它支撑类、目标、承继、封装等面向目标的特性。JavaScript中的目标是动态的,能够随时增加或删去属性和办法
-
函数式
- JavaScript是一种支撑函数式编程的言语,它的函数能够作为一等公民,能够赋值给变量,能够作为参数传递给其他函数,能够作为回来值回来给其他函数
-
呼应式
- JavaScript能够经过DOM操作完成呼应式编程,能够完成页面元素的动态更新,与用户的交互效果等
-
除了上述视频中说到的这几点,还有额外的特色进行弥补:
- 弱类型:JavaScript是一种弱类型的言语,不需求事先声明变量的类型,变量的类型会在运转时主动揣度(在TS中变成强类型)。
- 解说性:JavaScript是一种解说性的言语,不需求编译成可执行文件,能够直接在浏览器中执行(经典的例如V8引擎会进行处理)。
- 高阶函数:JavaScript中的函数能够作为参数传递给其他函数,也能够作为回来值回来给其他函数,这种函数称为高阶函数。
- 闭包:JavaScript中的函数能够构成闭包,即在函数内部界说的变量能够在函数外部拜访,这种特性能够完成私有变量和函数的封装。
高档言语
高档言语是一种人类易于了解和运用的核算机言语。它运用自然言语的办法来描述问题,而不是运用机器言语或汇编言语。高档言语一般具有较高的可读性和可保护性,使程序员能够更简略地编写和修正代码。一些常见的高档言语包含Java、Python和JavaScript等
思维导图总结
编程范式
程序言语特性
- 是否允许副效果
- 操作的执行次序
- 代码安排
- 状况办理
- 语法和词法
编程范式
-
指令式:指令式编程是一种以核算机执行的指令为中心的编程范式,它首要分为面向进程和面向目标两种办法
-
面向进程
- 面向进程是一种以进程为中心的编程办法,它将问题分解为一系列进程,经过函数的调用来完成程序的功用。面向进程的代码一般是一系列的指令,描述了核算机执行的详细进程
-
面向目标
- 面向目标是一种以目标为中心的编程办法,它将数据和函数封装在一同,经过目标的交互来完成程序的功用。面向目标的代码一般是一系列的目标,描述了程序中的实体和它们之间的联系
-
-
声明式:声明式编程是一种以描述问题为中心的编程范式,它首要分为函数式和呼应式两种办法
-
函数式
- 函数式编程是一种以函数为中心的编程办法,它将核算视为函数的运用,经过函数的组合来完成程序的功用。函数式的代码一般是一系列的函数调用,描述了核算的进程
-
呼应式
- 呼应式编程是一种以数据流为中心的编程办法,它将数据和函数封装在一同,经过数据的改变来触发函数的执行,完成程序的功用。呼应式的代码一般是一系列的数据流,描述了数据的改变和处理
-
进程式
自顶向下
调用的进程
结构化编程
结构化编程是一种以结构为中心的编程范式,它首要重视程序的可读性、可保护性和可扩展性,经过一系列的结构化的操控流程来安排程序的逻辑。
结构化编程的首要特色是:
- 次序结构:程序按照次序执行,从上到下依次执行每一条语句。
- 挑选结构:程序依据条件挑选执行不同的语句,包含if语句、switch语句等。
- 循环结构:程序经过循环执行一组语句,包含for、while、do-while等循环语句。
结构化编程的长处在于:
- 代码明晰:结构化编程经过一系列的结构化操控流程来安排程序的逻辑,使得代码愈加明晰易懂。
- 可保护性高:结构化编程使得代码的逻辑愈加明晰,易于保护和修正。
- 可扩展性强:结构化编程使得程序的逻辑愈加明晰,易于扩展和增加新的功用。
结构化编程是现代编程言语的根底,几乎一切的编程言语都支撑结构化编程。结构化编程的思想也是面向目标编程、函数式编程等其他编程范式的根底。
上图中左面是不和案例,右边是正确示例
JS中的面向进程
下方中完毕的
;
归于可加可不加的,但详细的规矩如下:
- 行完毕:当一行代码完毕时,假如下一行代码不是有效的JavaScript代码(比方空行或注释),JavaScript解析器会主动插入分号。
- 语句块完毕:当一段代码块完毕时,假如下一行代码不是有效的JavaScript代码,JavaScript解析器会主动插入分号。
- return语句:在return语句后边的表达式假如不是一行代码的最初,JavaScript解析器会主动插入分号。
- break语句和continue语句:在break语句和continue语句后边假如不是一行代码的最初,JavaScript解析器会主动插入分号。
//进行导出
//数据
export let car = {
meter:100,
speed:10
};
//算法:函数能够看作面向进程中的算法
export function advanceCar(meter){
while(car < meter){
car.meter += car.speed;
}
}
//导入(命名导入),除此之外还有默许导入的计划
import { car , advanceCar } from ".car"//导入上方模块内容
function main(){
console.log('before',car);
advanceCar(1000)
console.log('after',car)
}
- 模块化的计划不止ES6中的import导入export导出计划。在ES6正式出来之前,社区也有自己依据需求编写了其他的计划,目前还在流行的有CommonJS计划
- ES6的模块化计划和CommonJS都是JavaScript中常见的模块化计划,它们都支撑导入和导出模块的功用,但是在详细的语法和运用办法上有所不同。ES6运用import和export关键字来导入和导出模块,而CommonJS运用require和module.exports来导入和导出模块。ES6的导入和导出是静态的,不能在运转时动态导入和导出,而CommonJS的导入和导出是动态的,能够在运转时动态地导入和导出模块
// 导出
module.exports = {
a: 1,
foo: function() {},
MyClass: class {}
};
// 导入
const { a, foo, MyClass } = require('./module.js');
面向进程式编程有什么缺点?为什么后边会出现面向目标
-
数据与算法相关弱
-
不利于修正和扩大
- 可保护性差:面向进程式编程缺少封装性和笼统性,代码的耦合度高,修正代码时简略影响其他部分的代码,导致保护性差
-
不利于代码重用
- 可扩展性差:面向进程式编程很难对程序进行扩展,因为程序的逻辑分散在各个函数或进程中,很难进行整体性的扩展
面向目标的出现处理了这几个问题
- 可读性好:面向目标编程将数据和函数封装在一同,代码的可读性好,易于了解整个程序的逻辑。
- 可保护性好:面向目标编程具有封装性和笼统性,代码的耦合度低,修正代码时只需求修正目标的内部完成,不会影响其他部分的代码,导致保护性好。
- 可扩展性好:面向目标编程将数据和函数封装在一同,目标之间经过接口进行交互,易于对程序进行扩展。
面向目标
- 封装
- 承继
- 多态
- 依靠注入
封装
- 将数据和行为封装在一个目标中,经过拜访操控来保护目标的数据和行为,防止外部目标直接拜访和修正
- 封装的意图是躲藏目标的完成细节,供给一个统一的接口来拜访目标的数据和行为,增加目标的安全性和可靠性,一同也进步了程序的可保护性和可扩展性
class Person {
// 名字、年纪和性别都为private,外部目标无法直接拜访和修正
#name;
#age;
#gender;
constructor(name, age, gender) {
this.#name = name;
this.#age = age;
this.#gender = gender;
}
// 公共的getter办法,用于拜访和获取私有的数据成员
getName() {
return this.#name;
}
getAge() {
return this.#age;
}
getGender() {
return this.#gender;
}
// 公共的setter办法,用于修正私有的数据成员
setName(name) {
this.#name = name;
}
setAge(age) {
this.#age = age;
}
setGender(gender) {
this.#gender = gender;
}
}
// 创立一个Person目标,并拜访和修正私有的数据成员
const person = new Person('小余', 20, '男');
console.log(person.getName()); // 输出:小余
person.setName('小满');
console.log(person.getName()); // 输出:小满
承继
无需重写的状况下进行功用扩大,这个写法在React中是经常运用的
class Student extends Person {
#id;
#score;
constructor(name, age, gender, id, score) {
// 调用父类的结构函数,初始化名字、年纪和性别
super(name, age, gender);
this.#id = id;
this.#score = score;
}
// 公共的getter办法,用于拜访和获取私有的数据成员
getId() {
return this.#id;
}
getScore() {
return this.#score;
}
// 公共的setter办法,用于修正私有的数据成员
setId(id) {
this.#id = id;
}
setScore(score) {
this.#score = score;
}
}
// 创立一个Student目标,并拜访和修正私有的数据成员
const student = new Student('张三', 20, '男', '1001', 90);
console.log(student.getName()); // 输出:张三
console.log(student.getId()); // 输出:1001
student.setScore(95);
console.log(student.getScore()); // 输出:95
多态
不同的结构能够进行接口共享,进而到达函数复用
- 依据上面的Person类和Student类,创立了一个printInfo函数,用于打印目标的信息。这个函数承受一个Person或Student目标作为参数,依据目标的类型,打印不同的信息
- 咱们界说了一个printInfo函数,用于打印目标的信息。这个函数承受一个Person或Student目标作为参数,依据目标的类型,打印不同的信息。在函数中,咱们运用了instanceof关键字,判断目标的类型,完成了多态
function printInfo(obj) {
console.log(`名字:${obj.getName()},年纪:${obj.getAge()},性别:${obj.getGender()}`);
if (obj instanceof Student) {
console.log(`学号:${obj.getId()},成果:${obj.getScore()}`);
}
}
// 创立一个Person目标和一个Student目标,并别离调用printInfo函数
const person = new Person('张三', 20, '男');
const student = new Student('李四', 22, '女', '1001', 90);
printInfo(person); // 输出:名字:张三,年纪:20,性别:男
printInfo(student); // 输出:名字:李四,年纪:22,性别:女,学号:1001,成果:90
依靠注入
去除代码耦合
- 依靠注入(Dependency Injection,简称DI)是一种规划办法,它的首要意图是为了解耦合,使得代码愈加灵敏、可扩展和可保护。在一个运用程序中,各个组件之间一般会存在一些依靠联系,例如一个类需求运用另一个类的目标或许数据。在传统的代码完成中,一般是在类内部创立和办理依靠的目标,这样会导致代码的耦合性很高,一旦依靠的目标发生改变,就需求修正很多的代码,导致代码的可保护性很差。
- 而依靠注入则是经过将依靠的目标从类内部移动到类的外部,在类的结构函数或许办法中注入依靠的目标。这样做的好处是,使得类与依靠的目标解耦合,使得代码愈加灵敏、可扩展和可保护。一同,依靠注入也使得代码的测验愈加方便,因为测验代码能够注入不同的依靠目标,测验不同的场景和状况。
面向目标编程_五大准则
-
单一责任准则SRP(Single Responsibility Principle)
- 一个类只负责一个功用范畴中的相应责任,或许能够界说为一个类只有一个引起它改变的原因。这个准则的意图是将责任别离,进步类的内聚性,下降类的耦合性,使得代码愈加灵敏、可保护和可扩展
-
敞开封闭准则OCP(Open-Close Principle)
- 一个软件实体(类、模块、函数等)应该对扩展敞开,对修正封闭。这个准则的意图是使得代码愈加灵敏、可扩展和可保护,一同也能下降代码的风险和复杂度。经过运用笼统化和多态等技术,使得代码能够习惯不同的需求和改变
-
里式替换准则LSP(the Liskov Substitution Principle LSP)
- 一切引证基类(父类)的地方有必要能透明地运用其子类的目标。这个准则的意图是保证代码的正确性和可靠性,防止在子类中破坏父类的行为和逻辑。经过遵循这个准则,能够使得代码愈加灵敏、可扩展和可保护
-
依靠倒置准则DIP(the Dependency Inversion Principle DIP)
- 高层模块不应该依靠于底层模块,两者都应该依靠于笼统;笼统不应该依靠于详细完成,详细完成应该依靠于笼统。这个准则的意图是下降代码的耦合性,进步代码的灵敏性和可扩展性。经过运用接口和笼统类等技术,使得代码能够习惯不同的需求和改变
-
接口别离准则ISP(the Interface Segregation Principle ISP)
- 一个类不应该依靠于它不需求的接口,一个类应该只依靠于它需求的接口。这个准则的意图是下降代码的耦合性,进步代码的灵敏性和可扩展性。经过将接口进行别离,使得代码愈加灵敏、可保护和可扩展
面向目标编程有什么缺点?为什么咱们推荐函数式编程
函数式编程
函数的特色
- 函数是”一等公民”
- 纯函数/无副效果
- 高阶函数跟闭包
优势
经过一节课很难深入领会到他的好处,需求额外的拓展学习
- 可缓存
- 可移植
- 可测验
- 可推理
- 可并行
//代码1
const retireAge = 100
function retirePerson(p){
if(p.age > retireAge){
p.status = "retired"
}
}
//代码2
function retirePerson(p){
const retireAge = 100
if(p.age > retireAge){
return {
...p,
status = "retired"
}
}
return p
}
经过上述两段代码,能够看出代码二的如下优势:
- 增加了代码的可测验性:因为代码2中的函数回来了一个新目标,而不是直接修正原目标,因而能够更方便地进行单元测验,防止了测验进程中修正原目标的副效果。
- 增加了代码的可保护性:因为代码2中的函数不直接修正原目标(在React中这个称之为不可变的力量),而是回来一个新目标,因而更简略保护和修正。假如要修正函数的行为,只需求修正函数内部的代码即可,不会对其他代码产生影响。
- 增加了代码的可读性:因为代码2中的函数回来了一个新目标,而不是直接修正原目标,因而代码的含义愈加明晰明确。一同,代码2中的函数运用了解构赋值和目标展开运算符,使得代码愈加简洁、易读。
柯里化函数
这里我就搬我以前的笔记了
-
柯里化也是归于函数式编程里边一个十分重要的概念
-
维基百科解说:
- 在核算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化
- 是把接纳多个参数的函数,变成承受一个单一参数(开始函数的第一个参数)的函数,并且回来承受余下的参数,而且回来成果的新函数的技术
- 柯里化声称”假如你固定某些参数,你将得到承受余下参数的一个函数“
-
柯里化总结:
- 只传递给函数一部分参数来调用它,让它回来另一个函数处理剩下的参数
- 这个进程称为柯里化
//假设咱们有一个需求填入4个参数的 函数
function foo(m,n,x,y){
}
foo(10,20,30,40)
//柯里化的进程
//咱们对其进行转化,变得只需求传入一个参数,但这里边需求回来一个函数持续处理剩下的参数
function bar(m){
return function(n){
return function(x,y){
//你也能够将y参数持续return
m+n+x+y
}
}
}
bar(10)(20)(30,40)
柯里化的结构
//正常结构
function add(x,y,z){
return x+y+z
}
var result = add(10,20,30)
console.log(result);
//柯里化
function sum(x){
return function(y){
return function(z){
return x+y+z
}
}
}
var result1 = sum(10)(20)(30)
console.log(result1);
//简化柯里化代码
var sum2 = x=>y=>z=>{
return x+y+z
}
//还能再次简化var sum2 = x=>y=>z=>x+y+z
var result2 = sum2(20)(30)(40)
console.log(result2,"运用箭头函数简化柯里化的办法")
柯里化的效果
-
那么为什么需求有柯里化呢?
- 在函数式编程中,咱们其实往往希望一个函数处理的问题尽可能的单一,而不是将一大堆的处理进程交给一个函数来处理
- 那么咱们是否就能够将每次传入的参数在单一的函数中进行处理,处理完后在下一个函数中再运用处理后的成果
单一责任准则(SRP)
面向目标 -> 类 -> 尽量只完成一件单一的作业
柯里化 – 单一责任的准则
//悉数挤在一同处理
function add(x,y,z){
x = x + 2
y = y * 2
z = z * z
return x + y +z
}
console.log(add(10,20,30));
//柯里化处理
function sum(x){
x = x + 2
return function(y){
y = y * 2
return function(z){
z = z * z
return x + y + z
}
}
}
console.log(sum(10)(20)(30));
柯里化案例
只举例一个,否则内容过多
//打印日志时刻
function log(date,type,message){
console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}
log(new Date(),'DEBUG','查找到轮播图的bug')//[22:24][DEBUG]:[查找到轮播图的bug]
log(new Date(),'DEBUG','查询菜单的bug')//[22:24][DEBUG]:[查询菜单的bug]
log(new Date(),'DEBUG','查询数据的bug')//[22:24][DEBUG]:[查询数据的bug]
---------------------------------------------------------------------------------------------
//柯里化优化
var log = date => type => message =>{
console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}
//假如我打印的都是当前的时刻,咱们就能够将时刻复用
var nowLog = log(new Date());
nowLog("DEBUG")("查找小满去哪了")//[22:32][DEBUG]:[查找小满去哪了]
//或许时刻+类型都悉数复用
var nowLog1 = log(new Date())("小满系列查找");
nowLog1("查找小满人去哪了")//[22:34][小满系列查找]:[查找小满人去哪了]
nowLog1("查找小满的黑丝去哪了")//[22:34][小满系列查找]:[查找小满的黑丝去哪了]
nowLog1("查找小满的裤衩子被谁拿走了")//[22:34][小满系列查找]:[查找小满的裤衩子被谁拿走了]
nowLog1("查找小满有没有去按摩店找小姐姐")//[22:34][小满系列查找]:[查找小满有没有去按摩店找小姐姐]
组合函数
组合(Compose)函数是在JavaScript开发进程中一种对函数的运用技巧、办法:
- 比方咱们现在需求对某一个数据进行函数的调用,执行两个函数fn1和fn2,这两个函数是依次执行的;
- 那么假如每次咱们都需求进行两个函数的调用,操作上就会显得重复
- 那么是否能够将这两个函数组合起来,主动依次调用呢?
- 这个进程便是对函数的组合,咱们称之为 组合函数(Compose Function);
function double(num){
return num*2
}
function square(num){
return num ** 2//平方
}
var count = 10
var result = square(double(count))
console.log(result);
//如何将double和square结合起来,完成简略的组合函数
function composeFn(m,n){
return function(count){
return n(m(count))
}
}
var newFn = composeFn(double,square)
console.log(newFn(10));
容器式编程
- 能够当做容器的类型,类型支撑对容器内元素进行操作
- 常见的:functor:Array(Iterable).map,Promise.then
a.b != null ? (a.b.c != null ?(a.b.c.d !== a.b.c.d.e :null) : null) :null
….这部分听得实在太费劲了,气泡音听得难受(越过),知道有这个概念,去其他地方学习即可
呼应式编程
维基百科界说:在核算中,呼应式编程或反应式编程(英语:Reactive programming)是一种面向数据流和改变传达的声明式编程范式。这意味着能够在编程言语中很方便地表达静态或动态的数据流,而相关的核算模型会主动将改变的值经过数据流进行传达。
- 通俗来说,呼应式编程便是一种处理数据流的编程办法。咱们能够把数据流当作一条河流,数据就像是水流相同从上游流向下游。在呼应式编程中,咱们能够方便地界说这条河流,并在河流中处理数据的改变,就像是在河流中处理水流相同。这样,咱们就能够很方便地处理数据的改变,而不需求手动追踪和处理每一个数据改变的位置。
没有纯粹的呼应式编程言语,咱们需求借助东西库的帮助,例如RxJS
-
异步/离散的函数式编程
-
数据流
-
操作符
- 过滤
- 兼并
- 转化
- 高阶
-
观察者办法
观察者办法(Observer Pattern)是一种规划办法,它界说了一种一对多的依靠联系,让多个观察者目标一同监听某一个主题目标,当主题目标发生改变时,它的一切观察者都会收到告诉并主动更新。
在观察者办法中,有两个中心人物:主题目标和观察者目标。主题目标保护一个观察者列表,并供给增加、删去和告诉观察者的办法;观察者目标则界说了接纳告诉并进行更新的办法。
观察者办法的长处包含:
- 松耦合:观察者办法将主题目标和观察者目标之间解耦,使得它们能够独登时改变和扩展。
- 可复用性:因为观察者目标能够动态地增加和删去,因而能够在不修正主题目标的状况下增加新的观察者目标,进步了代码的可复用性。
- 扩展性:在观察者办法中,能够灵敏地增加和删去观察者目标,因而能够方便地扩展和修正系统的功用。
观察者办法在实践运用中广泛运用,例如GUI界面中的事情处理机制、微信公众号的订阅功用等等。
迭代器办法
迭代器办法(Iterator Pattern)是一种规划办法,它供给了一种次序拜访聚合目标中的元素,而不需求露出聚合目标的内部表明。迭代器办法能够将遍历聚合目标的进程从聚合目标中别离出来,从而能够简化聚合目标的完成和遍历算法的完成。
在迭代器办法中,有两个中心人物:聚合目标和迭代器目标。聚合目标是一组目标的调集,它供给了一个办法来获取迭代器目标;迭代器目标则界说了拜访和遍历聚合目标中元素的办法。
迭代器办法的长处包含:
- 简化聚合目标的完成:因为迭代器办法将遍历聚合目标的进程从聚合目标中别离出来,因而能够简化聚合目标的完成,使其只需求重视自己的中心业务逻辑。
- 进步聚合目标的拜访功率:在迭代器办法中,迭代器目标能够供给不同的遍历算法,从而能够针对不同的运用场景进行优化,进步聚合目标的拜访功率。
- 进步代码的可复用性:因为迭代器办法将遍历算法从聚合目标中别离出来,因而能够方便地重用遍历算法,进步代码的可复用性。
迭代器办法在实践运用中广泛运用,例如Java中的Iterator接口、C++中的STL迭代器等等。它能够帮助咱们愈加方便地遍历聚合目标中的元素,进步代码的可读性和可保护性。
- 能够类比为Promise和EventTraget超集
呼应式编程的”compose”
- 兼并
- 过滤
- 转化
- 反常处理
- 多播
- 去除嵌套的Observable
总结(思维导图)
构建范畴特定言语
范畴特定言语(Domain-Specific Language,简称DSL)是一种专门用于处理特定范畴问题的编程言语。与通用编程言语相比,DSL愈加重视于特定范畴的问题,使得针对该范畴的编程变得愈加高效、简略和直观。
DSL的规划是为了处理特定范畴的问题,因而它能够愈加贴近范畴的需求和特色,供给愈加快捷和高效的处理计划。DSL一般具有简略的语法和丰厚的范畴专业术语,使得开发人员能够愈加专注于处理范畴问题,而无需重视底层技术完成。
DSL的运用场景包含但不限于:配置文件、作业流程、数据剖析、模型界说等。在这些范畴中,DSL能够供给愈加高效、直观和易于保护的处理计划,提高开发功率和代码质量。
- HTML
- SQL
与之相对应的是General-purpose language(通用言语)
- C/C++
- JavaScript
- ….
特定言语需求由通用言语完成,通用言语无法由特定言语完成
词法解析
- 言语运转
SQL Token分类
- 注释
- 关键字
- 操作符
- 空格
- 字符串
- 变量
lexer
语法剖析
Parser_语法规矩
上下文无关语法规矩
- 推导式:表明非终结符到(非终结符或终结符)的联系。
- 终结符:构成语句的实践内容。能够简略了解为词法剖析中的token.
- 非终结符:符号或变量的有限调集。它们表明在语句中不同类型的短语或子句。
Parser_LL
LL:从左到右查看,从左到右构建语法树
对应的自顶向下的流程图:
Parser_LR
LR:从左到右查看,从右到左构建语法树
LL(K) > LR(1) > LL(1),括号里的内容构建语法树需求向下看的数量
东西生成
使用东西让咱们只需求重视语法方面的问题,语法剖析则交给东西来做
解说和编译
- 运转parser.parse后生成如下语法树
课程总结