202018.7

《西部世界》整部剧其实想告诉我们到底什么是人工智能,其实人工智能的本质不在于3d打印出来的肉体可以满足游客的肾上腺素刺激,它的本质在于满足人类的最终极欲望:永生。总之它是贪得无厌的人类走向灭亡的走一步。其次,这部剧告诉我们的真相是,人工智能的技术过程,一定是对用户隐私的无误差挖掘,一开始,当剧情发展到后面的时候,我听到说终极武器是收集游客数量时,竟然有些失望,觉得隐私有啥大惊小怪的。但直到我自己去研究智能推荐的时候才明白,用户的数据对智能算法是多么的重要。再联想到这段时间全世界的用户数据滥用问题,真的,那些推崇人工智能的公司,特别是超大公司,都在想尽一切办法把全世界的用户的无误差数据收集起来。所谓无误差是说,不能遗漏掉每一个细节,甚至你在屏幕上滑动的速度都要收集。在这个毫不留情的收集过程中,类似Facebook的事件会越来越多。

14:52:01 已有0条回复
162018.7

k = kilo (10^3, thousand)
M = Mega (10^6, million)
G = Giga (10^9, billion)
T = Tera (10^12, trillion)
P = Peta (10^15, quadrillion)
E = Exa (10^18, ??)

16:20:58 已有0条回复
122018.7

“实现财务自由”已经是一个滥俗到爆的词,今天听到一个更屌爆的词叫实现“言论自由”。当时我们在吃饭,说到李笑来,说他最屌的一句话是“在坐的各位都是傻逼”,大家一笑了之,有人突然说到“这算啥,你看马云,人家想说啥就说啥,这叫实现了言论自由”……听到这句话的时候,我都吓尿了,感觉打开了新世界的大门,我x,原来还有一件事叫“实现言论自由”,什么时候让我财务自由一下先?……

21:20:53 已有0条回复
112018.7

这几天真的是下了苦心,把之前的四个包完完整整的重写了一遍,并将作为自己今后开发的开箱必用包,它们分别是hello-events(事件绑定与触发管理),hello-storage(localStorage等本地化存储简化操作),hello-worker(webworker简便使用),hello-indexeddb(超简单indexeddb使用包),接下来打算把之前写的datamanaer.js重写一下,要用hello-什么做名字还没想好,但是肯定会是最完美的前端数据获取模式。完成这个之后,就是要把hst-virtual-dom再重写一遍,把之前写的toex用上,这样就可以让virtual dom这块完美收官了。

01:12:30 已有0条回复
092018.7

Blob和ArrayBuffer的区别

周末趁没事,就把之前写的一个包重新写了一遍。这个包的作用是为开发者提供更加快捷的indexedDB操作,免除一切烦恼,开箱就用起来。indexedDB是web标准,被大部分现代浏览器完整支持。和localStorage不同的是,indexedDB是真的完整存储js对象的引擎,它甚至支持存储new Date()的值,其他一些基础的js对象类型也可能是支持的。而localStorage是只能存储字符串的,要存储对象,必须通过JSON等进行序列化,而JSON不支持循环嵌套。另外一点就是,localStorage是同步的,indexedDB是异步的。

基于对复杂的indexedDB原生接口进行封装的思路,我发布了hello-indexeddb这个包,可以通过下面这个命令安装

npm install --save hello-indexeddb

完整的文档在github上,可以点击这里阅读。都是key-value数据库,它和localStorage的本质区别在哪里呢?我认为数据库的本质能力是分层检索能力。检索能力对于localStorage简直就是无,它只能通过一个key得到一个value,而且是字符串的。而indexedDB可以通过创建索引进行检索,查询某个object的时候,可以通过主键快速get,也可以通过索引来快速query,这里的索引在代码层面就是一个被记录的object的属性。这看上去好像也没什么,比如我可以通过构造多种变形的key来保存localStorage,然后自己封装一套检索方法来进行查询,甚至我自己构建一套localStorage的索引来快速查询。这当然是可以的,从这点上来讲,indexedDB甚至没有localStorage有优势,因为localStorage是同步的,更直观,而且不会出现事件循环错误,也就不需要事务机制。但是同步有同步的坏处,那就是阻塞页面进程。

读完stackoverflow上的这个讨论后我更加确信localStorage是有局限的,也就是说,它真的真的仅仅适合那些非常小、碎片化的数据存储,而indexedDB才是完整的长久存储数据的解决方案。这也就是说,在一个web应用中,应该合理的选择使用localStorage还是indexedDB。大家之所以那么青睐使用localSotrage,最大的原因莫过于它的接口太过简单了。不过想要让indexedDB变得简单也非常容易,就是使用我上面的包。你甚至可以这么用:

async function() {
let idb = new HelloIndexedDB({ key: 'key' })
await idb.put({ key: 'my_key', value: 'my_value' })
let obj = await idb.get('my_key')
}

就是这么简单,跟localStorage相比,就是异步和同步的区别。在对put这个操作的适合,转换一下localStorage里面的set操作的思维,然后,就非常自然的切换到了indexedDB的怀抱里面。要知道,get回来的,是一个拥有上下文的真实的js对象,而非字符串序列化和反序列化的结果。

就这样,拥抱indexedDB吧~

01:27:00 已有0条回复
082018.7

《我不是药神》真的很不错,以前觉得徐峥吊儿郎当的不怎么样,但是这几年出的电影都很不错,一部好的电影,要有故事,所谓有故事是说让人思考,接地气又有艺术形式,不需要轰轰烈烈的画面,但可以让观众在故事间忘我不出戏,这就很好👍

19:20:48 已有1条回复
  1. 真实即是好。
    #570 苦瓜 2018-07-08 20:41 回复
072018.7

写代码的时候,经常会有几个概念容易混淆,即update, set, put, patch。这几个单词在我们编程过程中经常会出现,我们自己在给方法取名字的时候,也常常不知道该用哪一个好。据一个例子,当你想要将用户修改好的个人信息保存到数据库时,应该用哪一个单词呢?现在我就来具体解释一下它们的使用场景。

set:将对象塞入到集合中,无论这个对象是否在集合中已经存在。这里有一个隐藏的风险,如果这个对象在集合中已经存在了,那么当你set的对象比原来的对象少了一些属性的时候,这些属性就丢失了。在这种情况下,set相当于replace。而如果这个对象本身不存在于集合中,那么set就相当于add了。

update:对传入的对象进行更新。首先,它要更新的内容虽然是对象,但你不确定它的个数;其次,它在更新时并不像set一样进行直接替换,而是对传入update中参数中规定的属性进行更新,因此,参数中未规定的属性保持不变;最后,它只是更新,不会附带add操作。update是一个比较高度抽象的词,因此,你实在不知道应该选用哪个词作为更新操作的时候,用update就行了。

put:和set非常接近。从词语本身看,set更多倾向于replace,put更多倾向于add。put的前提是在集合中已经存在一个位置(空位),而put操作准确的将参数对象放入这个位置。但是实际中,大部分规则下,put被赋予replace的功能。因此,在编程时,可以说put和set没有区别。

patch:和update非常接近,但仅对单个对象进行update,而update可以对一组对象进行更新。在restful中patch和put一样也被定义为更新操作。实际上,我认为patch是对但对象的update,而put被大部分人误解。restful中,大部分人将put定义为更新操作,即对但对象的update操作。但我认为,put既不是update,也不是add,而是这两者的整合,也就是上面说的set的逻辑(repalce/add)。但是patch非常明确,就是对单个对象的更新,因此,在restful设计中应该考虑用patch,而非put。

 

写方法的时候,我们常常会有两种方式,一种是set(key, value)的形式,另一种是set({ key: value })的形式。显然,第一种只能一次更新一个属性,而第二种可以同时更新多个属性。但是很明显,这里如果我们把set理解为上文阐述的意义,那么只能选择第一种形式,它代表将一个对象的某个属性整体替换为(如果不存在则添加)新内容。而第二种形式更适合patch,即我在知道我要更新哪一个对象时(常常通过url确定),我只传入我想要更新的属性名和值。但是在不知道我要更新哪个对象时,则不应该使用patch,而应该用update,并且要求在传入的对象中,提供明确的主键值。

18:02:00 已有0条回复
062018.7

简单package的简单构建脚本

在前段时间,我把之前写的几个package重新命名为hello开头的包,例如hello-storage, hello-events, hello-worker,这些包非常基础,不依赖任何第三方包,就是用基础的api来实现的,因此,代码非常简单。面对这种随即编写,随即打包发布的package,我采用最小化的方式进行发布。

一个包,在发布的时候,其实最重要的是考虑用户(开发者)使用这个包的使用场景,如果不考虑,直接给源码就行了,干嘛还要进行构建发布呢?所以,对于我而言,主要考虑两点:

  • 模块化
  • 代码兼容性

-

模块化是指用户可能在不同的模块规范下使用你的包,目前而言,有amd, cmd, commonjs, es6 module这几种,另外,还要考虑在浏览器中直接使用<script>标签引用脚本(没有模块化环境)。针对这点,我采用umd的方案,但是代码是自己写的,我的源码是es6 module,因此在导出的时候使用export default,但是在其他模块化规范下,并不需要default这样的接口,因此要直接export整个模块,因此,我的方案就是,对于要直接引用es6模块的用户,建议他import源码文件,而其他模块化规范下,引用dist文件:

// ES6
import HelloEvents from 'hello-events/src/hello-events'

// CommonJS
const HelloEvents = require('hello-events')

// 由于webpack在打包时能正确处理export default的问题,因此如果使用webpack,可以
import HelloEvents from 'hello-events'

对于开发者而言,这种解释应该是非常容易理解的。使用习惯上也不会造成困扰。那么怎么来实现这套方案呢?我在代码中采用umd的方案实现:

!function(root) {

// ES6代码转码之后放在这里

if (typeof define === 'function' && (define.cmd || define.amd)) { // amd | cmd
  define(function(require, exports, module) {
    module.exports = HelloEvents;
  });
}
else if (typeof module !== 'undefined' && module.exports) {
  module.exports = HelloEvents;
}
else {
  root.HelloEvents = HelloEvents;
}

} (this || window);

这得益于我以前写过omd.js。那怎么才能最终输出上面这个结构呢?我使用了gulp来做,并用上了我写的gulp-bufferify包来实现文件内容的修改。这样,当我使用babel把es6的代码转化为es5的代码之后,再对文件内容进行修改,就能得到上面这个结构了。

-

代码兼容性是指要考虑在不同浏览器版本中代码都能跑,主要是指一些es6新特性的降级,例如async函数。因为我写的这些pacakge的功能非常弱,不会涉及generator函数,也不会涉及symbol,因此其他大部分功能都可以用es5语法代替,并且找到对应的babel插件即可。

对于我而言,主要考虑到两个点:1.class语法;2.async/await语法;3. ...语法。这三个语法已经是我日常写作的必须品,因此在我的构建脚本里面,首先需要将这三个语法转化为es5语法。1和3使用babel-preset-env就可以自动解决,非常好用,2的话我使用了bebel-plugin-async-to-promises,它可以将async/await语法转换为promise,而preset-env默认使用bebel-plugin-transform-async-to-generator(将async/await语法转换为generator语法)和bebel-plugin-transform-regenerator(使用一个特殊的变量regeneratorRuntime来支持generator语法,这个变量需要你引入babel-polyfill),这两个插件和bebel-plugin-async-to-promises有冲突,因此要禁用默认选项,在.babelrc里面这样写:

{
  "presets": [
    ["env", { "exclude": ["transform-async-to-generator", "transform-regenerator"] }]
  ],
  "plugins": [
    "async-to-promises"
  ]
}

但是,其实这里对于开发者而言,是需要进行权衡的,比如说,我会在代码中随意使用array.filter, Object.assign等,这些新接口是es6新加入的,对于老版本的浏览器是不支持的。但是这些接口不会被babel转译,因此,我也不会特意在自己的代码中去实现这一块。之所以不自己在代码中实现这块,是因为这种接口很大程度上其他包也会用。对于使用包的开发者而言,他需要自己进行权衡,是不是要引入一个polyfill,如果他自己不想支持低版本的浏览器,就不用引入了。这样,可以保证我自己的包里面的代码是绝对干净,但是在大部分情况下是可以运行的。

但是这里有一个问题,就是为什么不把async/await也留给上层的开发者自己去决定。这是因为这个语法没有办法用polyfill,你不可能通过一个polyfill让一个不支持async语法的浏览器变得支持。这就是权衡,如果我在自己的所有包里面都把这两个语法的转译代码写进去,那么最后,上层开发者的应用里面,就会有重复代码,这也是没有办法的,如果webpack等打包工具更加智能,就可以将这些重复代码提升作用域来实现减少重复的效果。

-

上面讲完了我所关心的两个点之后,下面要出代码了。不过别急。我们再讨论一个问题,就是有没有必要任何包都用webpack?答案显而易见,我现在开始不大喜欢webpack,我越来越觉得webpack应该是一个应用层面进行打包时才用的上的工具,对于一个package而言,不应该进行打包,而是以原始的module文件间引用关系对外使用,只不过需要做上述两个点的处理。

好了,上代码:

// 安装对应的npm包
$ npm i -D babel-core babel-plugin-async-to-promises babel-preset-env gulp gulp-babel gulp-bufferify
// 构建脚本 build.js

var gulp = require('gulp')
var bufferify = require('gulp-bufferify').default
var babel = require('gulp-babel')

var namespace = 'HelloWorker' // 修改为自己的ClassName,一定要跟源码里面的class名对应
var entryfile = 'src/hello-worker.js' // 修改为自己的入口文件

gulp.src(entryfile)
  .pipe(babel())
  .pipe(bufferify(function(content) {

    content = content.replace(/Object\.defineProperty\(exports,[\s\S]+?\);/gm, '')
    content = content.replace(`exports.default = ${namespace};`, '')
    content = `
!function(root) {

${content}

if (typeof define === 'function' && (define.cmd || define.amd)) { // amd | cmd
  define(function(require, exports, module) {
    module.exports = ${namespace};
  });
}
else if (typeof module !== 'undefined' && module.exports) {
  module.exports = ${namespace};
}
else {
  root.${namespace} = ${namespace};
}

} (this || window);
    `
    content = content.trim()
    content += "\n"

    return content
  }))
  .pipe(gulp.dest('dist'))
// 进行构建
node build.js

这段构建脚本非常简单,稍微花点时间就读懂了。根据自己的实际情况进行修改,比如路径问题,比如namespace那个地方。总之对于我自己而言,非常简单的包都会采用这样的模式去做。

19:03:52 已有0条回复
052018.7

博客文化,像是一泉清澈的漩流

深夜又睡不着,好久没有正儿八经的写点东西,今天就来谈写博客本身这件事,以及博客圈儿和博客文化。当然,这更像是闲着无聊的文字消遣。但既然要谈,就谈的有些意思出来。

什么是博客?

我读大学的时候,是读历史系。这个很有意思,对一个东西下定义我们都是用概念去套概念,但是历史的方法论不同,对一个东西的认识,如果不从根源说起,概念全是空洞的东西。博客,英文blog,它的起源是什么?为什么要在log前面加b作为这种载体的名称?它是由谁最早提出的?第一个blogger是谁?

写博客而不知博客为何物的人,多半太过现代了,没有时间去挖掘一些有趣的古老的东西。而博客这个东西,其实历史也是很悠久。1997年由Jorn Barger所提出weblog的概念,即「web log」网络日志。但凑巧的是,这个词组可以拆分为「we blog」,现在来看也是奇妙,但在blog这个词没有出现之前,weblog的概念也是潮流。1999年,Peter Merholz首次使用缩略词「blog」。1999年8月,Pyra lab推出了blogger.com。后来,blog取代了weblog,成为专有词汇。

大陆翻译为“博客”的“博”真的是很妙,对比台湾翻译“部落格”,感觉精妙不止多少个层级。「部落格」就像是一堆原始人相互画了格子,把自己关在自己的格子里自娱自乐,而「博客」则像是极尽全力博得看客留足,为天下大事、私家小厨,广纳百士,无所不论的感觉。前者小家子气,后者阔落磅礴。

博客文化

读研究生的时候,胡鸿杰老师对“文化”有过一个精辟的解释,“文化的本质是规则”。但这并不能帮助我们理解文化,对于我而言,所谓文化,即一小搓人的共同认知和行为。注意「共同」这个词,它的意思是同一共同体具有强烈的排斥其他文化的特征,简单的说就是鄙视链。这跟宗教没有太大的差别。

聊及一门文化,“特征”这个词会反复出现,文化特征是对某一共同体最容易的解释。博客圈这样一个群体,具有怎样的特征呢?

  • 表达欲
  • 互动与认可
  • 非连续延时性

先来解释「表达欲」。对于博客主而言,他/她的发表冲动源于他/她对世界的不满和对可能性的乐观,简单的说,博主希望通过加工后的文字,表达自己生活遭遇中突出的一面,忽略平凡的一面。之所以在表达时进行选择性表达,而非真实表达,是他出于对可能性的期待,例如得到别人的认同,或者某些奇妙的化学反应。基于这点,我们可以去观察称得上“博客”的站点,一般分为两类,即「知识分享」和「情感分享」,知识分享又有很多类型,例如专门教人怎么做菜的博客,专门介绍摄影技巧的博客,情感分享也有多种,例如记录个人日常的,或小小说。

「互动与认可」,博客的最大特征是互动,博主比较乐意和读者就单片内容进行讨论,而讨论的话题不限于发表的内容本身,更倾向于暧昧式散发。不过,对于讨论者,博主有进行选择的权利,他可以删除那些对自己不利或不友好的讨论,从而在自己的博客中树立个人形象。基于得到认可的渴望,即使博主并不是因表达欲而发布文章,也宁肯通过转载来吸引读者。

博客的「非连续性」指博客的内容非常发散,它和连续性内容区别很大,即使是专门介绍某项知识的博客,它的内容也不具备连续性,读者所获得的知识不成体系,需要自己进行加工。因此,搜索引擎对于博客站点非常重要。同时博客「延时性」很强,它的内容并不要求读者马上阅读,因此,那种以快速发布新闻为目标的博客并不占多数,相反,对新闻事件进行评论的博客数量更多。

以前,我们认为博客以个人博客为主,但随着博客形式的多变化,其实团队博客越来越成为主流。但是由于团队博客越来越功利化,它们逐渐被排斥出博客圈,这也是博客文化的一个重要特征,也就是鄙视链特征。以原创博客为主的圈子鄙视转载博客圈子,以情感分享为主的博客圈子鄙视知识分享的圈子,以个人博客为主的圈子鄙视运营性博客的圈子。

怎样写博客?

最后的问题是,我们怎么去写博客?对于这个问题的答案,我可能和很多主旋律不一样。我认为好的博客的唯一标准是「个性」。“个性”这个词包含了非常多的意思,但我所指的个性绝对不是胡乱的莫名其妙的乱入,而是清晰的表达自由意志不被约束。

每个博客有自己的风格,无论是界面上,还是语气上,无论是观点上,还是字数上,无论真心实意,还是假惺惺,都坚持自己的风格,一以贯之,不受约束,便是好的博客。

于我而言,博客更像是一张名片,刻画的是我对事物尽可能往深入追求的个性,试图展示的是我不愿意苟且于浅薄文字和囫囵阐述,更多想要把事物原委解释清除的意图。而我读的有些博客,言简意赅,不需要解释,对事物的阐述极尽机灵,可谓个性漫屏,无论它的内容蕴含的深浅,它的整个博客堆起来,便是一个形象的最好字典。

但凡你不幸读到这里,切忌让你的博客随波逐云。

02:34:37 已有6条回复
  1. 评论不太好使,昵称还被挡住了,还是觉得你以前的简洁版好,这个高大上但是高大上的同时也需要充足的内容来衬托。
    #563 苦瓜 2018-07-05 08:53 回复
  2. 我也是喜欢简单到底的,无奈,复杂总是被不断追求。
    这个也谈不上高大上,都是自己折腾来折腾去的地方。
    #564 回复给#563 否子戈 2018-07-05 23:21 回复
  3. 我觉得台湾翻译的“部落格”简直就是音译blog……

    于我而言,博客大抵是用自己最舒坦的方式,记录自己想记录的,装逼的称之为 —— 自己看不见的朋友。
    #565 Betty 2018-07-07 16:58 回复
  4. 很意外 你周末还看博客啊
    #566 回复给#565 否子戈 2018-07-07 18:02 回复
  5. 咦,难道不是应该周末才可以好好看博客吗?
    #568 回复给#566 Betty 2018-07-07 18:51 回复
  6. 印象里你周末都不更新自己博客的
    所以我以为你都是周末去浪,不会管博客的人……
    #569 回复给#568 否子戈 2018-07-07 19:00 回复