深入理解margin
margin负值的应用
负margin可以增加元素宽度,典型应用为一列元素,每个元素都有一个margin-right,但是又不想最后那个元素的margin-right显示出来,在父元素和子元素中间增加一层元素来为它设置margin-right为负值,以此来增加父元素宽度,即可实现这种效果。
观察者模式中存在两种类型的实体,一种是观察者(observer或者subscriber),一种是订阅者发布者或者主体(publisher或subject)。当某事件触发时发布者通知(调用)所有的订阅者并且可以向订阅者传递消息。
paper对象作为publisher对象,拥有subscribers数组属性,subscribe方法,unsubscribe方法以及publish方法。
我们在subscribers中维护我们的订阅者,subscribe方法中添加订阅者到数组中,而unsubscribe方法从数组中移除订阅者,而publish方法则遍历数组并调用它们注册时提供的方法。
所有这些方法还需要有个type参数,因为paper对象可以发射多种类型事件,而我们的订阅者可以只订阅其中一种事件。对于任何的发布者对象这些成员都是一致的,所以我们可以使用mixin模式将他们copy到任何对象中使之成为一个publisher。
简单说明mixin模式,就是将多个对象上的属性混合到一个对象上,如这里简单的实现,只适用于浅拷贝场景:
1 | function mix() { |
要深入理解koa中的中间件的运行机制,必须要明白的是koa-compose这个包究竟做了些什么。koa-compose包接收一个数组,返回一个compose之后的函数,并且返回的函数接收一个ctx对象和next函数参数,并最终返回一个promise。理解koa-compose需要首先掌握递归概念以及ES6的promise和generator函数以及async/await等特性。
1 | function compose (middleware) { |
compose这个函数在koa中是在handleRequest函数调用时传入的,在handleRequest的函数体中,调用了compose生成的结果函数fnMiddleware并且传入了合成的ctx对象,至此compose返回的函数的生命周期开始。我们进入到compose的函数体中,也就是下面这一段。
1 | function (context, next) { |
首先context为application中createContext函数返回的context对象,而next最初为undefined。
中间件序号从0开始,将index首先设置为-1,表明上次调用的中间件的序号为-1,也就是代表程序初始状态。此时返回dispatch(0)。于是我们开始调用dispatch函数并且该函数最终返回一个promise。
这里假设我们有两个中间件函数,我们首先取到第一个中间件函数并将其赋值给fn变量,判断条件不成立,继续执行,返回一个promise,但是这个resolve方法的操作数是一个fn函数调用的结果,因此我们需要调用fn函数,并且将fn函数的返回结果传递到resolve方法中。而fn函数就是我们在app.use中传入的中间件函数,于是开始调用第一个中间件函数,执行中间件函数中的代码,如果遇到了await next(),那么我们就调用传入的next方法,而中间件函数中的next参数就是我们这里传入的dispatch.bind(null, i + 1)
函数,于是下一轮的dispatch函数就开始了它的生命周期。
此时dispatch函数中的参数index和i均为1,而相应的fn函数也变成了第二个中间件函数。类似于上面的过程,我们依然返回了一个promise对象,且由于resolve方法的参数为fn函数调用的结果,我们则首先需要调用fn函数,也就是第二个中间件函数的函数体。在此过程中,我们既可以写await next()
,也可以不写。
假如我们写了await next()
,那么dispatch函数会再一次调用,并且传入的i参数又加1,此时变为2。于是我们调用dispatch(2)
。但是此时判定条件成立,于是返回了一个空的promise对象,返回之后,我们继续上一轮fn函数next调用下面的函数内容,直至执行完毕。
此时第二个中间件函数体已然执行完毕,则第一次调用的resolve方法中的参数fn函数中的next部分就执行完毕,于是开始执行next调用下面的内容了,直至函数执行完毕,resolve中的操作数计算完成,此时就可以执行application.js中then方法中的handleResponse了。
总的来说,就是通过promise控制了中间件的调用流程,形成了一种类似栈方式的中间件调用,而本质还是通过递归实现的。
因为Koa.js在将request对象和response对象的属性委托到context对象时,依赖了delegates包,我们就来分析一下这个包
1 | module.exports = Delegator; |
delegates包对外暴露一个函数,其本质为一个构造函数,作用也是作为类存在的,我们通过暴露的构造函数可以实例化一个delegator。Koa.js中在context.js文件中利用了delegates将context对象上的某些getter,setter和方法委托给了context.request以及context.response对象。
application.js是整个koajs的入口文件,对外暴露的是一个Application
类,下面以一段代码作为演示分析koa执行过程。
1 | // 首先引入koa,这个对应的Koa就是application对外暴露的Application类。 |
生成器函数作为ES6的新特性之一,已经并不是什么新鲜的概念了。在此之前,Python,Php等均有生成器的概念。其本质就是一个返回iterator的函数,通过yield关键字可以暂停函数的执行,并在下一次调用相关方法时继续函数的执行。
1 | function* letterGenerator() { |
可以看到通过调用生成器函数,我们得到了一个iterator对象。iterator对象包含next方法,可用于迭代iterable类型的数据。通过调用iterator的next方法,我们得到一个包含两个属性value和done的对象,用于标识我们的迭代过程,这也是generator函数最基本的用法。
NoSQL(Not Only SQL)意指不仅仅是SQL(Structured Query Language)。是为了应对传统关系型数据库所不能解决的挑战而生的。可以分为以下四类:
通用情况下,文档数据库能提供良好的性能以及可伸缩性。在不需要复杂的查询需求时,键值对数据库可以提供最佳性能。
文档(document)也就是自包含的一块信息,用于描述单一实体(entity)。如下面的JSON文档:
1 | { |
当然也可以使用XML数据格式甚至是二进制格式表示这个document,在这里我们采用了JSON。关系型数据库中,这样的document会存放在两个不同的表中,一个persons表,一个addresses表。文档数据库中就仅仅是一个文档。
键值数据库就是保留了必要功能的精简版的文档数据库。键值数据库的键是一个特殊的ID,用于标识某一特定文档,而值则是该键对应的文档。不同的是,键值数据库只允许通过键查询,而文档数据库可以根据文档的内容进行查询,这就使得键值数据库可针对基于键的查询做性能上的优化,并且它们也可以对值进行压缩。
Redis就是很优秀的键值数据库,实际上它将整个数据库保存在RAM中,而在硬盘中做备份,具有闪电般的性能。
MongoDB名字来源于humongous,下载的MongoDB包含的mongod.exe是MongoDB的守护程序,也就是主服务器二进制文件。为了开启数据库服务器,可以执行它。而mongo.exe则是MongoDB提供的repl工具,用于管理数据库以及进行一些测试性的实验。
MongoDB的运行需要一个数据文件夹(在MongoDB的语境中叫做dbpath
)来存储数据库数据。默认的dbpath
是data/db
(取决于当前工作的磁盘),最好总是指定一个明确的dbpath
。
提到继承,不得不说的是JS中的原型链概念。JS不像传统的面向对象语言,在ES6出现以前,JS标准中并没有类的概念,所以往往是通过原型链的特性实现面向对象的继承。看过一些资料以后,发现其实原型链的概念十分简单,一句话总结:JS中的每个对象都有一个__proto__
属性,而这个属性指向它的构造函数的prototype
属性,而因为函数在JS中也是对象,所以函数本身也有__proto__
属性,而在对象上进行属性查找就会沿着这条链一直向上进行,直到原型链的终点Object.prototype
(Object.prototype.__proto__
属性为null)。还有一个重要的点是构造函数的prototype
对象有一个constructor
属性,这个属性指向构造函数本身,我们可以利用这个特性建立对象和其构造函数之间的联系,因为我们知道,对于bar.constructor
属性的查找,如果bar
本身没有constructor
属性,就会在其__proto__
属性上查找,而__proto__
又指向其构造器的prototype
,所以依据这个特性,就能找到某对象的构造函数。另外一个便捷的判断实例构造器的方法是利用instanceof
操作符,这个操作符会沿着对象的原型链一直向上查找直到对象的__proto__
的__proto__
…等于函数的prototype
属性,或是一直到Object.prototype
都没有相等,返回false
。
Given a binary tree, return the zigzag level order traversal of its nodes’ values. (ie, from left to right, then right to left for the next level and alternate between).
For example:
Given binary tree [3,9,20,null,null,15,7]
,
1 | 3 |
return its zigzag level order traversal as:
1 | [ |
以螺旋形层序遍历我们的二叉树。和普通的层序遍历相比,螺旋形只是在原来的基础上增加一个ltr变量,而在我们每次遍历完一层之后,只需要将这个变量取反即可。
1 | /** |
参考https://www.geeksforgeeks.org/level-order-traversal-in-spiral-form/