原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
在一些后台管理系统中,我们经常会遇到复制某条数据的功能,比如在教务系统中,老师新建了一条学生的数据之后,可以将上一条学生的数据复制一条,然后只需要修改学生的姓名、性别、联系方式字段就行。与这种场景相对应的设计模式就是原型模式,原型模式将一个对象进行复制产生新的对象。
// 学生构造函数
class Student {
constructor(name, age) {
this.name = name;
this.age = age;
}
setName(name = "") {
this.name = name;
}
setAge(age = 0) {
this.age = age;
}
clone() {
return this;
}
}
// 构造学生A
const A = new Student('A', 12);
console.log(A); // { name: 'A', age: 12 }
A.setAge(14);
// 构造学生B
const B = A.clone();
console.log(B); // { name: 'A', age: 14 }
A.setAge(16);
// 需要注意对生成对象进行处理,防止循环引用问题
console.log(A); // { name: 'A', age: 16 }
console.log(B); // { name: 'A', age: 16 }
优点:
缺点:
大话设计模式 -- 程杰
]]>代理模式:为其他对象提供一种代理以控制对这个对象的访问
当我第一次看到代理模式的定义是,脑海中浮现的一句代码就是const that = this;
,我们通过that
来代理this
的访问,虽然没有体现控制的意味,但体现了代理的意思。
在日常的开发工作之中,我对于代理模式的理解是:对对象的操作添加过滤的手段。
之前在工作中遇到一个场景:平台管理系统需要去维护各子系统的公共数据(CommonData),各子系统在使用时去拉取公共数据,但是这样拉取公共数据其实会是比较大的开销,因为公共数据的变化比较缓慢,时效性比较低,我们可以通过代理拉取公共数据的函数来操作。代码如下:
// 拉取公共数据函数
const getCommonData = async (type) => {
return await Ajax(type);
};
// 代理函数
const CommonDataMap = new Map();
const getCommonDataProxy = async (type) => {
let data = CommonDataMap.get(type);
if (!data) {
data = await getCommonData(type);
CommonDataMap.set(type, data);
}
return data;
};
####使用场景
在前端中,我遇到或使用代理模式的场景有:
大话设计模式 -- 程杰
]]>装饰模式:动态地给一个对象添加一些额外的指责,就增加功能来说,装饰模式比生成子类更加灵活
「装饰模式」对于前端开发者而言一定是非常熟悉的,什么是「装饰模式」呢?顾名思义,「装饰模式」就像是给一个类穿上一件一件衣服,通过不同的搭配,让这个类呈现出不同的特点。那我们在工作的过程中,在编写页面的时候,做的好像也是一样的事情对吧?比如按钮组件,他会有default
、primary
、hover
、active
、disabled
等等状态,我们常用的方式就是通过给button
添加不同的class
来实现这种种不同的效果,其实这种实现的方式,就是「装饰模式」的应用。只不过今天我们说的是「装饰模式」在JS中的应用而已,我们可以通过CSS的思维来帮助我们理解。
实例:当下我们需要做一套考试系统,一套试卷由一道道试题组成,试题会有练习、考试和解析3种状态:
由上我们可以想象出具体的实现模式就是根据源数据在不同条件下显示不同的状态和内容,就可以结合「装饰模式」来实现。
// 试题渲染
class Question {
constructor(question) {
this.question = question;
}
render() {
console.log(this.question.topic);
console.log(this.question.options);
}
}
// 分数渲染装饰器
class ScoreQuestion {
constructor(question) {
this.question = question;
}
render() {
this.question.render();
console.log(this.question.question.score);
}
}
// 解析渲染装饰器
class AnalyzeQuestion {
constructor(question) {
this.question = question;
}
render() {
this.question.render();
console.log(this.question.question.analyse);
}
}
const question = {
topic: '题干',
options: '选项一、选项二、选项三',
score: 10,
analyse: '选项一是正确答案'
};
let instance = new Question(question);
instance.render();
// 题干
// 选项一、选项二、选项三
instance = new ScoreQuestion(instance);
instance.render();
// 题干
// 选项一、选项二、选项三
// 10
instance = new AnalyzeQuestion(instance);
instance.render();
// 题干
// 选项一、选项二、选项三
// 10
// 选项一是正确答案
如果我们实现一项功能可以做到将核心指责与装饰功能区分的话,就可以使用「装饰模式」来实现。「装饰模式」的优点就在于把类中的装饰功能从类中搬移去除,这样可以简化原有的类。
大话设计模式 -- 程杰
]]>单例模式(Singleton),保证一个类只有一个实例,并提供一个访问它的全局访问点。
一说到只有一个实例,并能全局访问,我觉着前端同学能想到的类库就太多太多了,在前几年我们还没有像现在这样实行模块化开发的时候,我们用到的JQuery
、moment
、lodash
等等类库,全部都能满足上面的特点。
但其实我们在平时的应用当中,大多数情况下只能感受到单个实例的好处,也没太多感受到单个实例的坏处。但是我们可以举一个很通俗的例子,我们平时都会通过玩游戏来消遣时间,在Dota中有这么一个道具叫「刷新球」,它的作用是重置所有物品和技能的冷却时间,那如果我玩一个英雄,他的某个技能很厉害,那我是不是出6个「刷新球」,爆炸了...但其实很破坏游戏体验,所以Dota中有一个机制就是不管出多少个刷新球,他们共享一个冷却CD,那怎么实现这样的效果呢?
单例模式就可以闪亮登场了:
// 刷新球类
class Refresher {
constructor() {
this.cooldown = 180;
this.lastCD = 0;
this.timer = null;
this.instance=null;
}
// static 静态方法调用直接在类上进行,不能在类的实例上调用。静态方法通常用于创建实用程序函数。
static getInstance() {
if(!this.instance) {
this.instance = new Refresher();
}
return this.instance;
}
use() {
if (this.lastCD) {
console.log('技能还没冷却好!');
return;
}
this.lastCD = this.cooldown;
this.timer = setInterval(() => {
this.lastCD--;
if (!this.lastCD) {
clearInterval(this.timer);
}
}, 1000);
}
getCD() {
return this.lastCD;
}
}
var refresher = Refresher.getInstance();
var refresher2 = Refresher.getInstance();
refresher.use();
refresher.getCD(); // 174
refresher2.getCD(); // 174
refresher.use(); // 技能还没冷却好!
refresher2.use(); // 技能还没冷却好!
由上可见「单例模式」的作用。在日常工作中,单例模式的使用多被状态管理工具替代了。假如没有状态管理工具,我们也可以借助「单例模式」来实现购物车功能。
大话设计模式 -- 程杰
从ES6重新认识JavaScript设计模式(一): 单例模式
单一职责原则(SRP),就一个类而言,应该仅有一个引起它变化的原因。
这个原则说的就是一个类只关注一件事情,我们经常会看到这个原则,比如我们开始学习编程的时候,会说到怎么优化代码,其中一项就是一个函数只做一件事情这样的,那时候「只做一件事情」就在我脑海中留下了比较深刻的印象。
现在随着前端组件化的发展,我们将页面也细分为大大小小的组件来进行拼接。在这个时候,「单一职责原则」就和前端的工作息息相关了,我们在拆分组件的时候,也应该保证每一个组件只做一件事情,只关注一个重点。这样我们的页面在后期的扩展维护上就会节省很多的功夫,这也是优秀的组件应该做到的。
「单一职责原则」的有点也是显而易见的:
开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。[ASD]
我们在开发系统的时候,经常会有这样的感受,在产品最开始设计的时候,我们就尽可能把准备工作多做一些,希望最开始的框架能够满足未来的一些需求。但是随着开发工作的逐渐深入,我们最头疼的情况就发生了:不管我们开始的时候想的有多么完善,都无法满足未来的需求,总是需要去扩展我们的框架。这种时候,「开放-封闭原则」就是我们的指导。
其实我们需要面对的问题就是:但原框架无法适应新需求的时候,我们是修改原来的代码,还是扩展原来的代码?我们经常会发现一个需求只要修改一小段代码就可以完成,然后我们就用了很快的时间完成了任务,之后却发现引起了许多的问题。所以我们在处理新需求的时候,应该更多地在源代码的基础之上去加东西,而不要去改动原有的代码,以防止影响原来的功能。
「开放封闭原则」的特征就是:
它的优点也显而易见:
开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、复用性好、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。
里氏代换原则:子类型必须能够替换掉他们的父类型。
我的理解是「里式代换原则」保留了父类的可扩展性,即我们设计的时候在父类的基础上添加特性,在具体实现的时候利用子类来实现。好处就是:
依赖倒转原则
A. 高层模块不应该依赖低层模块。两个都应该依赖抽象。
B. 抽象不应该依赖细节。细节应该依赖抽象。
说的清楚一些应该就是我们要「针对接口编程,而不是针对实现编程」。
假如当下我们要做一个关于考试分数的柱状图标,首先我们就会想到用echarts来做这个图表。
// echarts类
class Echarts {
constructor() {}
// 绘制柱状图
renderBar(options) {}
// 绘制折线图图
renderLine(options) {}
// 绘制饼图
renderPie(options) {}
}
// 绘制图表类
class Render {
tool = new Echarts();
constructor() {}
// 绘制柱状图
renderBar(options) {
this.tool.renderBar(options);
}
// 绘制折线图图
renderLine(options) {
this.tool.renderLine(options);
}
// 绘制饼图
renderPie(options) {
this.tool.renderLine(options);
}
}
const render = new Render();
render.renderBar();
render.renderLine();
render.renderLine();
但是这时客户觉得更喜欢阿里的G2图表,这时我们修改代码就发现很难去修改,因为我们的业务绘制代码直接依赖了Echarts,如果我们需要改图表依赖,我们就要改很多东西,那怎么做才好一些呢?
// 依赖倒置原则
// echarts类
class Echarts {
constructor() {}
// 绘制柱状图
renderBar(options) {}
// 绘制折线图图
renderLine(options) {}
// 绘制饼图
renderPie(options) {}
}
// g2
class G2 {
constructor() {}
// 绘制柱状图
renderBar(options) {}
// 绘制折线图图
renderLine(options) {}
// 绘制饼图
renderPie(options) {}
}
function getTool(type) {
switch (type) {
case 'echarts':
return new Echarts();
break;
case 'g2':
return new G2();
break;
}
}
// 绘制图表类
class Render {
constructor(tool) {
this.tool = tool;
}
// 绘制柱状图
renderBar(options) {
this.tool.renderBar(options);
}
// 绘制折线图图
renderLine(options) {
this.tool.renderLine(options);
}
// 绘制饼图
renderPie(options) {
this.tool.renderLine(options);
}
}
const render = new Render(getTool('g2'));
render.renderBar();
render.renderLine();
render.renderLine();
由上可见,我们新增了一个getTool
的方法,如果我们需要调整或者扩展图表类,我们只需要扩展底层方法即可,代价和难度都会大大降低,也减少了高层代码(绘制图表类)对底层代码(图表类)的依赖。
迪米特法则(LoD):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
举个例子,现在我们打车都用上了高德地图,但是还是有些地方地图定位的不是特别准确,这些时候就需要我们口头告诉司机应该怎么走。这种场景下我们就有了3个类:乘客、司机和汽车,其实我们最终的目的是希望指挥车子的运动(向左转、向右转等等),但是其实我们和车子的控制没有关系,我们就需要通过第三者(司机)来达成目的。
// 迪米特法则
class Car {
constructor() {}
// 左转
turnLeft() {}
// 右转
turnRight() {}
// 掉头
turnRound() {}
// 直行
goStraight() {}
}
class Driver {
constructor(car) {
this.car = car;
}
// 左转
turnLeft() {
this.car.turnLeft();
}
// 右转
turnRight() {
this.car.turnRight();
}
// 掉头
turnRound() {
this.car.turnRound();
}
// 直行
goStraight() {
this.car.goStraight();
}
}
class Customer {
constructor() {}
inform(driver, direction) {
switch (direction) {
case 'left':
driver.turnLeft();
break;
case 'right':
driver.turnRight();
break;
case 'round':
driver.turnRound();
break;
case 'straight':
driver.goStraight();
break;
}
}
}
const car = new Car();
const driver = new Driver(car);
const customer = new Customer();
// 乘客告诉司机下个路口左转
customer.inform(driver, 'left');
// 乘客告诉司机下个路口右转
customer.inform(driver, 'right');
- 在类的结构设计上,每一个类都应当尽量降低成员的访问权限;
- 迪米特法则其根本思想,是强调了类之间的松耦合;
- 类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
一个类对另一个类的依赖应该建立在最小的接口上。
对于这个原则,我的理解是这样的:在当下我们写页面的时候,常用的UI框架有ElementUI和Ant Design UI,在这两个UI框架中,有三个组件:Input
、AutoComplete
和InputNumber
,我们会发现他们有很多共同的地方:
除此之外他们也都有自己的特性:
AutoComplete
支持自动搜索InputNumber
对数字有特殊的处理其实我们能不能把这些特性全部集中在Input
上呢?其实应该也是可以的,那样的话Input
组件就会很臃肿,也提供了很多我们平时用不到的特性。
那最好的方式就是将「自动搜索」和「数字特殊处理」当做独立接口处理,以形成我们今天的3个组件,让我们在需要的地方用最适合的组件。
我想这是前端工程师对「接口隔离原则」的一个很棒的应用。
大话设计模式 -- 程杰
Element-UI
Ant Design
今天我们讨论的设计模式是「策略模式」,策略模式比较好理解,首先我们看一下他的定义:
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,他可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合「DEP」。
由上面的定义我们可以看到,策略模式有3个要素:
策略模式在生活中也比较常见,下面举例说明:
当我们在生活中乘坐公交地铁的时候,合肥市的票价是2元、学生半价、老年人免票,这就是一种常见的设计模式,我们可以看出:
那我们就有实现方案:
//策略模板
class StrategyTemplate {
constructor() {}
}
// 老年人免票
class OldPersonStrategy extends StrategyTemplate {
constructor() {}
calculatePrice() {
return 0;
}
}
// 学生半价
class StudentStrategy extends StrategyTemplate {
constructor() {}
calculatePrice() {
return 1;
}
}
// 普票2元
class NormalStrategy extends StrategyTemplate {
constructor() {}
calculatePrice() {
return 2;
}
}
// 上下文传入策略并使用
class BusContext {
constructor(strategy) {
this.strategy = strategy;
}
pay() {
return this.strategy.calculatePrice();
}
}
// 使用
const oldPerson = new BusContext(new OldPersonStrategy());
oldPerson.pay();
const student = new BusContext(new StudentStrategy());
student.pay();
const person = new BusContext(new NormalStrategy());
person.pay();
生活中我们随处可见也有很多策略模式的使用,比如洗衣机(快洗、漂洗、大件等)、电饭煲(煮饭、煮粥等)、电动牙刷(高频、中频、低频)、购物(满减、打折、返利等)。
由上面这么多的例子我们可以总结出来,策略模式的使用场景有几个特点:
优点就是:
switch/if else
判断缺点就是:
[大话设计模式 - 程杰]:
每当我听到有人讨论设计模式时,我听到最多的概念好像就是「工厂模式」,他就像是背单词时候的「abandon」,它易于理解且经常用到,所以我也将它作为学习「设计模式」的第一步。
我们都知道,工厂模式是为了创建对象而存在的(主要是听到的太多了~)。对象是一个系统的基石,我们编写的功能都可以抽象成由一个个对象组合而成,请求是由一个个XmlHttpRequest对象执行的、页面由一个个DOM节点对象堆砌而成等等。我们在前端框架之中往往会对请求作一层层的封装(比如我们会在JQuery.ajax、axios之上再封装一层),那我们在生成这些对象的时候,会发现他们都有着相似之处,就像是由工厂生产出来的一个个产品,那我们封装的过程其实就和「工厂模式」很相近了。
工厂模式属于**「创建型模式」**的一种,与之相关的有:简单工厂模式、工厂模式和抽象工厂模式。
简单工厂模式可以理解为「一家工厂根据不同的模具来生产产品」的模式,如下举例:
// 工厂
class SimpleRequestFactory {
constructor() {}
createRequest(type) {
let req = null;
switch (type) {
case 'get':
req = new GetRequest(); // GetRequest为get请求的模具
break;
case 'post':
req = new PostRequest(); // PostRequest为post请求的模具
break;
}
return req;
}
}
// 工厂生产处get请求的产品
const getRequestInstance = SimpleRequestFactory.createRequest('get');
getRequestInstance.setUrl('https://xxx'); // 设置get请求的url
getRequestInstance.setParams({id: 'xxx'}); // 设置get请求的参数
getRequestInstance.request();
// 工厂生产处post请求的产品
const postRequestInstance = SimpleRequestFactory.createRequest('post');
postRequestInstance.setUrl('https://xxx'); // 设置post请求的url
postRequestInstance.setParams({id: 'xxx'}); // 设置post请求的参数
getRequestInstance.request();
以上就是简单工厂模式运行模式,利用这种方式,我们就可以根据不同请求的需要,来不断生产request请求对象,我们并不需要关心request的实例对象是怎么生成的,我们只需要得到它、使用它即可。
所以「简单工厂模式」的特点就是:
由上面的代码我们也能看出,简单工厂模式是具有三大要素的:
工厂模式是在简单工厂模式之上做了优化处理之后形成一种模式,我们先看有关于工厂模式的代码
class RequestFactory {
constructor() {}
createRequest() {
// 我只代表每种RequestFactory都要实现createRequest方法
// 而我不生产任何产品
}
}
// get请求的工厂
class GetRequestFactory extends RequestFactory {
constructor() {}
createRequest() {
return new GetRequest();
}
}
// post请求的工厂
class PostRequestFactory extends RequestFactory {
constructor() {}
createRequest() {
return new PostRequest();
}
}
// put请求的工厂
class PutRequestFactory extends RequestFactory {
constructor() {}
createRequest() {
return new PutRequest();
}
}
// 生产get请求的产品
const getRequestIns = GetRequestFactory.createRequest();
getRequestIns.setUrl('https://xxx'); // 设置get请求的url
getRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
getRequestIns.request();
// 生产post请求的产品
const postRequestIns = PostRequestFactory.createRequest();
postRequestIns.setUrl('https://xxx'); // 设置get请求的url
postRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
postRequestIns.request();
// 生产put请求的产品
const putRequestIns = PutRequestFactory.createRequest();
putRequestIns.setUrl('https://xxx'); // 设置get请求的url
putRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
putRequestIns.request();
由上面的代码可以看出,我们把每一种请求的生产工厂都独立出来了,看似没有简单工厂模式方便,但是我们可以在生活之中找到例子辅助理解。
比如我们A公司是一家生产饮品的公司,我们最开始有一条生产线专门生产矿泉水,我们叫「XX山泉」,后来我们发现矿泉水做的不错,我们想多做一些产品,如「XX AD钙奶」和「XX 纯牛奶」,那我们应该怎么做呢?这时我们可能根据生活中看到的例子,可以想到,我们只要照搬A公司的基础部门(如行政),再成立几家公司,分别生产「XX AD钙奶」和「XX 纯牛奶」即可。那我们为什么不在A公司的基础上不断扩充生产线呢?因为一旦生产线越来越多,管理就越来越复杂,我们需要不断地折腾A公司,还不如复制一个抽象公司,然后专事专做。
上面的例子就是为了帮助我们理解「简单工厂模式」和「工厂模式」的区别的,那我们什么时候用哪种模式呢?我的理解就是:
其实上面说的也关乎到设计模式中的一个原则——「开闭原则」。
再回到「工厂模式」之上,我们可以看到工厂模式的特点就是:
「工厂模式」包含四大要素:
此时肯定有朋友想到了一个问题:「现实中我们也并不是所有的工厂都只生产一类产品,牛奶工厂可以生产纯牛奶、酸奶等等」,这就是我们提到的抽象工厂模式了。示例代码如下
// 抽象工厂
class RequestFactory {
constructor() {}
createRequest() {
// 我只代表每种RequestFactory都要实现createRequest方法
// 而我不生产任何产品
}
}
/** 看这 start **/
// 抽象的get请求
class GetRequest{
constructor() {}
request() {}
}
// 简单的get请求(不需要带参数的get请求)
class SimpleGetRequest extends GetRequest{
constructor() {}
request() {}
}
// 普通的get请求
class NormalGetRequest extends GetRequest{
constructor() {}
request() {}
}
// 抽象的post请求
class PostRequest{
constructor() {}
request() {}
}
// 简单的post请求(不需要带参数的post请求)
class SimplePostRequest{
constructor() {}
request() {}
}
// 普通的post请求
class NormalPostRequest extends PostRequest{
constructor() {}
request() {}
}
/** 看这 end **/
// get请求的工厂
class GetRequestFactory extends RequestFactory {
constructor() {}
createSimpleRequest() {
return new SimpleGetRequest();
}
createNormalRequest() {
return new NormalGetRequest();
}
}
// post请求的工厂
class PostRequestFactory extends RequestFactory {
constructor() {}
createSimpleRequest() {
return new SimplePostRequest();
}
createNormalRequest() {
return new NormalPostRequest();
}
}
// 生产get请求的产品
const simpleGetRequestIns = GetRequestFactory.createSimpleRequest();
simpleGetRequestIns.setUrl('https://xxx'); // 设置get请求的url
simpleGetRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
simpleGetRequestIns.request();
const normalGetRequestIns = GetRequestFactory.createNormalRequest();
normalGetRequestIns.setUrl('https://xxx'); // 设置get请求的url
normalGetRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
normalGetRequestIns.request();
// 生产post请求的产品
const simplePostRequestIns = PostRequestFactory.createSimpleRequest();
simplePostRequestIns.setUrl('https://xxx'); // 设置get请求的url
simplePostRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
simplePostRequestIns.request();
const normalPostRequestIns = PostRequestFactory.createNormalRequest();
normalPostRequestIns.setUrl('https://xxx'); // 设置get请求的url
normalPostRequestIns.setParams({id: 'xxx'}); // 设置get请求的参数
normalPostRequestIns.request();
通过上面的代码,我们可以看到,抽象工厂模式之于工厂模式的不同就是在工厂模式的基础上,对产品也进行了一层抽象,从而实现了一个实体工厂也能生产多个产品的功能。
「抽象工厂模式」的好处就是:
「抽象工厂模式」有五大要素:
创建型模式: 创建型模式抽象了实例化的过程,他们帮助一个系统独立于如何创建、组合和表示它的那些对象。创建型模式都会将关于该系统使用哪些具体的类的信息封装起来。允许客户用结构和功能差别很大的「产品」对象配置一个系统。配置可以是静态的,即在编译时指定,也可以是动态的,就是运行时再指定。
开闭原则: 开放-封闭原则是说软件实体(类、模块、函数等等)应该是可以扩展、但是不可修改[ASD]。
[大话设计模式 - 程杰]
作者:毛姆
今天把毛姆的「刀锋」读完,这本书和「月亮与六便士」都给了我同样的触感:「为什么有的人能这样生活下去呢?」。这两本书的主人公都追求着不同于普通人生的生活,他们不惧世俗的眼光,都只是过自己想过的生活。
但是我觉得「刀锋」相对于「月亮与六便士」而言要相对温和些。拉里作为一个「战争英雄」的角色进入到读者的眼中,他本拥有着「不错的」未来,但是他却执意放弃原本的生命节奏,取消订婚,不断地读书、体验生活、拜访导师,去寻找生命的真谛,最终「散尽家财」,过上普普通通的生活,只为寻求自己生命的真谛。
拉里传达给我的消息现在想来其实很简单:「生命属于自己,不要随波逐流」。
我们时常也用这句话安慰自己,告诉自己不必去比较、不必去在意别人的看法,但是往往无法做到,我们就是无可避免去把自己周边的人作比较,就是在意A有几套房、B工资几何、C结婚生子。每个人的人生都只有一次,也许父母朋友会把各种道理摆上台前,以过来人的身份告诫我们要听从劝告,只是我们要有分别的去对待这些「道理」。
每个人都希望寻求自己生命的真谛,在生命的种种挫折之下,我们往往服从于生活,我们希望过上跟别人一样的生活,放弃了自己的特异性。这种放弃同样也给了我们束缚,我们要把自己跟别人摆在一起,我们会去在意别人有什么而我们没有,这样我们就感到焦虑;在意我们有什么别人没有,我们就感到优越。这种比较我有时觉得是人生而既有的东西,我们一生追求的无非就是平和而已。
在经历种种「焦虑症状」突发之后,我发现自己依然无法放下这些比较,因为这种躺平并不是为了寻求告解,只是为了躺平而躺平而已,很难受,很纠结,很焦虑。思考的习惯无时无刻不在提醒自己要努力一些,只有真切感到焦虑发作的时候才会告诉自己要平和,身体的习惯却只能堕落性质地躺平。一切都处在违和的状态之下,却一次次在冲突之中寻找出口。
我们未必能做到大彻大悟,拉里在最后也没有说一定做到顿悟,只是在平常的生活之中继续体验、继续思考、继续总结。
我认为生活就是为了一次一次的「体验」,我们要更多地尝试新鲜的实物、去认识新鲜的人、去看看新鲜的地方。在到达生命尽头之前,永远保持好奇心,不是为了势利地前进,而是为了与世界更多次地握手。我们未必能体会到成功者地生活,但不能体验到的生活亦不可强求。
越过刀锋实属不易,因而智者常言救赎之道艰辛。
]]>本篇主要是「JavaScript高级程序设计-面向对象的程序设计」一章中第三篇「继承」回顾的笔记
备注
示例
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.propety;
}
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty
}
var instance = new SubType();
alert(instance.getSuperValue()); // true
## 借用构造函数
1. 基本思想
* 在子类型构造函数内部调用超类型构造函数
2. 示例
```javascript
function SuperType() {
this.colors = ['red', 'blue', 'green']
}
function SubType() {
// 继承SuperType
SuperType.call(this);
}
var instance = new SubType();
instance.colors.push('black');
alert(instance.colors); // "red, blur, green, black"
概念
思路
示例
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);
};
var instance = new SubType('Matthew', 27);
instance.colors.push('black');
alert(instance.colors); // red, blue, green, black
instance.sayName(); // Matthew
instance.sayAge(); // 27
备注
示例
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
var person = {
name: 'Matthew',
friends: ['Wudan', 'Sanmao', 'MaP']
};
var anotherPerson = object(person);
anotherPerson.name = 'BLSM';
anotherPerson.friends.push('YQS');
alert(person.friends); // 'Wudan', 'Sanmao', 'MaP', 'YQS'
备注
示例
function object(o) {
function F() {};
F.prototype = o;
return new F();
}
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert('hi');
}
return clone;
}
var person = {
name: 'Matthew',
friends: ['Wudan', 'Sanmao', 'MaP']
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // hi
备注
示例
function inheritPrototype(subType, superType) {
var prototype = Object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
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;
}
inheritPrototype(SubType, SuperType);
SubTyper.prototype.sayAge = function() {
alert(this.age);
}