我的2016

这一年对我来说又是一个丰富多彩的一年,过去很长一段时间因为种种原因没有再静下心来写一些什么,所以这次一定要把这一篇写完,不希望2016年的总结和前几年一样不了了之。

工作篇

#友宝 #裁员 #优酷 #阿里 #996

过去一年对我影响最大的事情可能都与工作相关,15年底友宝裁员,我没选择去深圳总部,只能拿钱走人。和同事几个人一起去了他们成立的新公司,和来北京之前一样,工作的第八个年头没有公司年会,没有双薪,没有年终奖,也没有希望,所以我离开了,重新启程。

没能通过美团的面试,也没有去极客学院,最后机缘巧合入职了优酷会员中心。背景调查用了整整一个月的时间,3月入职,4月优酷土豆被阿里收购,所以摇身一变成了阿里人。

新的工作有一些机会可以接触到一些核心业务,也有可以负责一些比较重要的技术改造,令我感到意外的是,这一年竟然几乎没怎么和产品经理打交道。

下半年优酷经历了大规模的空降和换血,新官上任最重要的几件事是融入阿里的文化,使用阿里的技术,当然也要在双十一、双十二时接受考验。代价是很惨痛的,我们经历了4个月的996,多数人连十一长假都在加班,还有一堆的年假和带薪病假等着过期。

令人欣慰的是,昨天,是996的最后一天,今年会有年会,有双薪,有项目奖,有年终奖。

个人成长篇

#英语 #口琴 #吉他 #C #游泳

除了工作,今年花了最多时间的事情是学英语,背了几千个单词,早晚的交通时间大多都用来学英语,如果不出意外,今天会完成在扇贝单词的第174次打卡。

除了996最忙的一段时间之外,每周都会去游泳,一方面为了锻炼身体,另一方面为了保证长时间写代码颈椎不会很痛,今年还是没能掌握自由泳,但是蛙泳各方面都有进步,明年要在速度上有所提高。

今年很少利用业余时间研究技术,这可能是一件很遗憾的事情,但是在工作时间内还是学到了大量的东西,在工作中处理的数据规模和并发量也达到了一个新的层次。

最近几个月花了一些时间重新学习了C语言,现在已经把相关语法特性学习完,正在学习数据结构和算法,希望能坚持下去,用到实际应用中,希望未来有机会能深入了解现在用到的相关技术栈的实现原理。

大概有半年没有再弹过吉他,谱子已经忘光,但是今年还是有进步的,可以大横按F和弦了。布鲁斯口琴也有进步,虽然不是很熟练,但已经基本上找到了压音的方法。

旅行篇

#香港 #泰国 #澳门 #台湾 #北戴河 #深圳 #珠海

护照已经拿到手快三年,今年终于用到了,换工作的空档借道香港去泰国转了转,玩的蛮好,更重要的是也正是从那个时候开始,让我有了另一个目标,想去更远的地方走一走,工作或者生活,也正是从那个时候开始认认真真的学英语。

体验过泰国之后就对国内旅游不再感兴趣了,今年去了一次北戴河,体验糟透了。

下半年花了一些时间规划了一次台湾之旅,整个过程很曲折,不过在台湾的那段时间是非常开心的,也颠覆了我对很多事情的想法,想去了解相关的政治、历史和文化,因为我从来没想过,这些几十年前被迫离开故土的人,如今在这里生活的如此幸福。

去台湾之前顺便去澳门逛了一下,这里的确是男人的天堂,不过最终我还是没有在澳门赌上两把,在见识过别人十几万一局的豪赌之后,我对赌博这个事情有了新的认识;虽然整个在澳门的过程中我都没见到,但估计澳门的色情产业也是很发达的。

理财篇

#互联网金融 #A股 #美股

离开友宝之后我的账户上第一次达到了6位数,所以也开始思考如何进行理财,目前大部分的钱放在定期的互联网金融产品里,一小部分在活期产品里,另一小部分在A股和美股市场,股票这东西我就不展开了,目前还不具备相关的知识,甚至还不能以正确的心态来看待,未来还要持续的关注和学习。

读书篇

  • 《C Primer Plus》内容很丰富,适合入门学习,这是我第一次把一本如此厚的书认认真真的看了两遍,但是看完之后才发现,这只是万里长征的第一步。

  • 《美国种族简史》内容很有趣,的确引发了不少思考。

  • 《图解HTTP》、《 JavaScript设计模式》、《RabbitMQ实战》这三本,我现在竟然已经回忆不起来里面的任何细节了,看来需要好好反思一下自己的读书方法。

  • 《硅谷之谜》今年不经意的发现,吴军博士也开始写烂书了,很失望。

  • 《人月神话》书里讨论了很多软件工程中的问题,很多事情都很有共鸣,这些问题被大家讨论了几十年,但是为什么现在这么多的团队还是做不好?现在还对书里提到的外科医生团队记忆犹新,希望以后可以有机会尝试。

  • 《深入浅出Rails》为数不多的rails中文书籍,书中的内容比较旧,而且也只展示了rails中最基础的一部分,很遗憾没能走上rails这条路。

  • 《Node与Express开发》、《ECMAScript6入门》前两年JavaScript实在是太火了,所以忍不住也学习了一些相关内容,感叹JavaScript相关技术进步速度如此迅速。

  • 《JavaScript高级程序设计》是本好书,我第一次读到面向对象那部分的时候是十分震撼的,进阶必读。

电影&电视篇

  • 地球脉动
  • 西部世界
  • 怦然心动
  • 奇异博士
  • 大鱼海棠
  • 超人总动员
  • 纳尼亚传奇1
  • 釜山行
  • 魔兽
  • 奇幻森林
  • 华尔街之狼
  • 恐怖直播
  • 心迷宫
  • 唐人街探案
  • 寻龙诀
  • 云中行走
  • 一个勺子
  • 老炮儿

今年看剧较少,希望明年有更多的好电影,有了投影仪之后看电影的体验更好了。

游戏篇

#Ingress #皇室战争 #MineCraft #暗黑3

  • 今年还偶尔会进Ingress里转转,暑假的时候变成了军火商,在淘宝卖了不少游戏道具;
  • 沉迷了几个月皇室战争,玩竞技类游戏始终让我感觉很痛苦,特别是这类付费手游,所以最终还是决定放弃竞技游戏;
  • 之后玩了一段时间Mine Craft,是一款很不错的游戏,感觉可以wan shang;
  • 最近两个月为了找回曾经玩电脑游戏的那种快乐,买了暗黑3,装在新mac pro上,进度比较慢,不过估计未来还会玩一段时间;
  • 今年一直对PS和VR眼镜蠢蠢欲动,估计下次有机会就下手了。

剁手篇

  • HHKB Professlonal 2:去年买了一个白色无刻印放在单位,在家李用电脑感觉各种不习惯,眼看着人民币贬值,越来越贵,最终还是又买了一个回来,这样家里的工作台就完美了。

  • 投影仪:大幅度的提升幸福感,没有买各种国产智能投影仪,感觉两三千块拥有这么功能画面和亮度一定不会特别理想,最后买了一款Sony的低端投影,没有内置系统,没有音箱,没有3D,外接了电视盒子和蓝牙音箱,体验蛮好,缺点是噪音有些大,看节目的时候可以忽略不计。

  • 打印机:总会有很多时候需要去打印社或者公司打印、扫描一些东西,后来想一想还是买了,三四百块钱还是无线的,很方便。

  • Apple TV:在香港顺手买的,体验很好,可以在国内实在没有太多用途,挂闲鱼卖掉了。

  • Iphone SE,Iphone 6:上半年终于买了自己的第一部Iphone,到手没到一周去音乐节被扒走了,郁闷了两周之后又入了低配的Iphone 6,入了苹果的坑就很难换别的产品了,另外,16G真的不够用,不够用,不够用。

  • NAS:一方面换了mac之后磁盘空间十分紧张,另一方面也感觉有一些数据很重要,比如代码、照片、文档之类的,需要有安全的备份机制,所以买了nas,两块硬盘做raid,设置了time machine,以后再也不用担心电子设备丢掉或者磁盘坏掉,还买了个PT账号,里面的小电影已经多的看不过来了。

  • 另外还入了登山鞋一双、皮鞋一双、电脑背包和新的眼镜,终于舍得去买一些自己很喜欢的东西了。

其他的碎碎念

  • 拿到了本科毕业证,在等学位证
  • 淘宝店被给了两个恶意差评,被勒索,客服拒绝受理相关投诉
  • 去年过年老板给发了200块钱红包 #大SB
  • 去年过年没赶上航班
  • 今年坐了好多好多好多次飞机
  • 今年仍然没买到春运的火车票 #票贩子越来越专业
  • 大公司政治水很深
  • 乌龟挺健康,但是背甲角质化很严重,买了各种食物来补充营养
  • 种了几次花花草草都没发芽
  • uber hackthon 被队友坑
  • 自己的一些小项目还是搁置状态
  • 发现自己可能再也买不起北京的房子
  • 想成为老司机但是不想买车
  • 已经9个月没有剪头发
  • 了解了一下开超市相关的事情
  • 最近在了解猎头行业

2017想做些什么

  • 学射箭
  • 好好练习一下单板
  • 清明去新加坡的机票已经买好
  • 去欧美看一看
  • 考摩托驾照
  • 练车,这很重要
  • 修牙
  • 英语口语和单词量
  • 蛙泳要更快,自由泳可以以后再学
  • 这是工作的第十个年头
  • C语言要继续学下去
  • 在技术上要有输出和积累
  • 要考虑结婚生娃,为了人类
  • 口琴要继续练

以上,应该就是今年的总结,有些混乱,因为做过的事情和想做的事情实在太多,希望能尽可能都记录下来,这对我来说很重要。

Screeps新手游戏指南

一款玩着玩着就变成JavaScript大神的游戏。

一款可以增加你赚钱能力的游戏。

在写下这段文字的时候,我已经删除了这个游戏里所有的数据,因为这个游戏消耗了我太多时间,本着纪念这段游戏经历的目地,我写了这篇文章。

什么是Screeps?

The world's first MMO strategy open world game for programmers.

http://screeps.com

官方这样定义这款游戏:第一款针对开发者的大型多人在线开放策略游戏。而对于这款游戏,很多报道里会提到:”最难“,”战术沙盒“等概念。

是的没错,screeps就是这样一款游戏,一款需要编写代码才能玩的在线策略游戏。玩家需要在游戏中编写javascript代码来控制所有的游戏行为。比如:

查找并攻击敌人

1
2
3
4
enemy = creep.pos.findClosestByRange(FIND_HOSTILE_CREEPS); // 找到距离最近的敌人
if (creep.attack(enemy) == ERR_NOT_IN_RANGE) { // 攻击敌人
creep.moveTo(enemy); // 如果距离不够则向敌人移动
}

找到能量矿并采集

1
2
3
4
sources = creep.room.find(FIND_SOURCES); // 找到房间里所有的能量矿
if (creep.harvest(sources[0]) == ERR_NOT_IN_RANGE) { // 采集能量
creep.moveTo(sources[0]); // 如果距离不够则向矿移动
}

你的代码会保存在游戏中,并且一直运行下去。游戏甚至给你提供了缓存,有代码提示的在线代码编辑器,控制台和完善的API文档。

在游戏里都能做什么?

  • 发展基地
  • 生产工人和士兵
  • 采集资源
  • 攻击其他玩家
  • 建造防御设施
  • 和盟友互相支援

目前有哪些游戏元素

  • 两种资源:energy和power
  • 建筑:spawn,power spawn, extension,storage,road,wall,rampart,tower,observer,link
  • 四矿地图和Source Keeper
  • control rank 和 power rank
  • 远程兵种,近战兵种,治疗兵种,混合兵种
  • controller 房间等级,越高能造的建筑越多,上限8
  • GCL 游戏等级,每一个等级可以多控制一个房间

更多内容可以查看官方文档 http://support.screeps.com/

Screeps收费嘛?

游戏中程序执行会消耗游戏中的CPU,每个月你可以免费获得600个CPU,如果优化的好,600个CPU可以很轻松的控制20个游戏单位,但是如果你想发展多基地或者进入排行榜,还是建议你购买CPU进行游戏,CPU的使用上限是每天300个,也就是说,这款游戏每天可能花掉你人民币0-6块钱。

新人必须知道的

  • 你的基地叫做Spawn,你的工人和士兵叫做Creep
  • 你的代码每执行一次是一个tick,一个tick是2秒
  • 绿色的地方是泥地,如果不修路走过去比乌龟还慢,不是泥地也需要修路
  • Creep有1500ticks的生命,可以使用spawn给Creep延长生命,但是并不建议这么做
  • 路每隔一段时间就会坏,需要经常修
  • energy矿每300ticks刷新3000个,计算好你的WORK数量,5-6个就够了
  • 一定要有防御机制,任何人都可能攻击你,并且可以在几分钟内摧毁你的所有建筑,杀死你的所有Creep,而且并不需要有理由

游戏策略

如何提高工人效率?

和现实中一样,明确的分工可以有效的提高工作效率,矿工专门采矿,运输工种专门运输,升级工人只负责升级,但由于CPU有限,你需要在效率和工人数量上进行权衡。

如何节省CPU?

你可以像调试其他项目一样调试你的程序效率,找到那些CPU消耗很高的代码并且优化它们,官方文档上对每个方法的CPU消耗情况有详细的说明,对于新手,CPU最大的消耗在于查找目标和跨房间移动,解决这两个问题可以节省超过一半的CPU。

应该选择什么样的房间进行游戏?

遵循一个原则:附近资源丰富并且容易防守

移动速度是如何计算的?

请参考 http://codepen.io/findoff/full/RPmqOd/

如何高效的修路

  • 合理修路可以节约1/4的creep花费,你只要有一半的MOVE就可以达到最大移动速度。
  • 只要有creep经过的地方都应该修路,有一个creep走的地方修一条,有多个creep走的地方修两条,如果非常多,就修很多条,但是不要到处修。

建筑的摆放有什么要注意的

  • storage:让从所有搬运工的走动距离最短,通常选择房间的正中央,让其他使用能量的建筑环绕在周围。
  • extension:离storage要近,游戏中有各种个样的高效摆放方式。
  • spawn:让所有的单位出去工作的距离最短,通常在中心附近。
  • tower:分散在房间的各个角落,并且保证出口附近所有的位置都在10个距离以内,这样能做到最高效的升级围墙,防御敌人,修复建筑,但是要记住tower的效率没有工人高。
  • wall和rampart:建议建在最外围,这样可以让最少的敌人进入房间,易于防守和维修,一个外围的围墙最多能被3个近距离单位+4个远距离单位攻击,而一个内部的围墙可以被至少3个近距离单位+十几个远距离单位围攻。

Link的使用

  • 用来代替搬运工运输资源
  • 用来当升级工人的扩展存储
  • 用来当小型的storage
  • 将link建造在外围rampart上可以实现在有围墙的情况下和其他玩家交换资源

你可能不知道的

  • 在有observer之前,你只能看到有你的单位的房间,我指的是代码里
  • 你可以帮助其他玩家建造和维修
  • 你可以帮助其他玩家升级Controller
  • 你可以把资源丢进其他玩家的storage
  • rampart可以叠加在任何建筑上,千万别忘了给spawn、storage上rampart,wall上也可以放一个rampart
  • renew一个creep并不划算,但是你可以在creep路过的时候顺便renew一下
  • 你可以用Grunt提交你的本地代码 http://support.screeps.com/hc/en-us/articles/203022512-Committing-local-scripts-using-Grunt

我的全部游戏代码已经发布在Github上 https://github.com/m4ker/screeps

我在Screeps里的最后一分钟

死磕JavaScript面向对象 - 定义对象

前言

近来发现JavaScript能做的事情越来越多,用的人也变多了,有太多的框架和模式都搞不清楚原理(早些年的prototype, yui, jQuery, 到后来的MVC,MVVM),所以想深入的学习一下JavaScript。

经过一段时间的摸索,终于知道了ECMAScript的来龙去脉,而我对JavaScript的认知还停留在ES3上面。但对于ES3,也只知道一些简单的用法,看高手写代码竟然完全看不懂。

最近几天卡在了闭包和对象两个章节上,因为和其他语言差的太多,所以看了很多文章仍然还只是一直半解,对于一个函数作为一等公民的面向对象语言,搞不清楚对象的基本知识实在有些说不过去,所以决定对相关内容进行一次深入的整理。

原始方式 1

1
2
3
4
5
6
7
8
9
// 1.js
var robot = new Object;
// var robot = {};
robot.height = '120cm';
robot.weight = '50kg';
robot.color = 'red';
robot.say = function (string) {
console.log(string);
}

用这种方式是不是感觉有点怪?感觉不像一个整体,所以我们要用下面的方法把对象封装起来:

原始方式 2

1
2
3
4
5
6
7
8
9
// 2.js
var robot = {
height : '120cm',
weight : '50kg',
color : 'blue',
say : function (string) {
console.log(string);
}
}

在JavaScript中,我们通常以以上两种方式创建对象,在实际开发过程中我们会遇到一些问题:

  • 问题1: 没有办法识别对象的类型,因为我们创建的所有对象都是Object
  • 问题2: 创建多个对象很麻烦

工厂模式

工厂模式用来解决代码重用的问题(问题2),可以用一个函数多次创建同一个结构的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 3.js
function createRobot(height, weight, color) {
return {
height : height + 'cm',
weight : weight + 'kg',
color : color,
say : function (string) {
console.log(string);
}
}
}

var robot1 = createRobot(100,50,'red');
var robot2 = createRobot(120,60,'blue');
console.log(robot1.height,robot1.weight,robot1.color); // 100cm 50kg red
console.log(robot2.height,robot2.weight,robot2.color); // 120cm 60kg blue

这段代码看上去已经很好的解决了代码重用的问题,但是又产生了新问题:

  • 问题3:多个同类型对象的方法被重复创建

这里指的就是robot1.say和robot2.say方法,本是相同的函数却被声明了两次,体现在代码里就是:

1
console.log(robot1.say === robot2.say); // false

为了解决这个问题,我们要把say方法拿到工厂外面单独进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 4.js
function say(string) {
// var say = function(string) {
console.log(string);
}
function createRobot(height, weight, color) {
return {
height : height + 'cm',
weight : weight + 'kg',
color : color,
say : say
}
}

var robot1 = createRobot(100,50,'red');
var robot2 = createRobot(120,60,'blue');
console.log(robot1.say === robot2.say); // true
console.log(robot1 instanceof Object); // true
console.log(robot1 instanceof createRobot); // false

这样我们就解决了问题3,say方法只被声明了一次,所有robot对象的say方法都指向同一个函数。

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 5.js
function say(string) {
// var say = function(string) {
console.log(string);
}
function Robot(height, weight, color) {
this.height = height + 'cm';
this.weight = weight + 'kg';
this.color = color;
this.say = say;
}
var robot1 = new Robot(100,50,'red');
var robot2 = new Robot(120,60,'blue');
console.log(robot1.height,robot1.weight,robot1.color); // 100cm 50kg red
console.log(robot2.height,robot2.weight,robot2.color); // 120cm 60kg blue
console.log(robot1.say === robot2.say); // true
console.log(robot1 instanceof Object); // true
console.log(robot1 instanceof Robot); // true

构造函数看上去更像其他语言中的面向对象,并且解决了问题1,可以识别对象的类型。

原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 6.js
function Robot() {
}
Robot.prototype.height = '120cm';
Robot.prototype.weight = '60kg';
Robot.prototype.color = 'blue';
Robot.prototype.parts = ['body'];
Robot.prototype.say = function(string) {
console.log(string);
}

var robot1 = new Robot();
var robot2 = new Robot();
console.log(robot1.height); // 120cm
console.log(robot2.height); // 120cm
console.log(robot1 instanceof Object); // true
console.log(robot1 instanceof Robot); // true
console.log(robot1.say === robot2.say); // true

这里可以看到,使用原型模式使得robot1和robot2公用了同一套属性值和方法。并且可以正确识别对象类型。

接下来我又尝试了对实例的属性进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(robot1.hasOwnProperty('height')); // false
console.log(robot2.hasOwnProperty('height')); // false
robot1.height = '100cm';
console.log(robot1.height); // 100cm
console.log(robot2.height); // 120cm
console.log(robot1.hasOwnProperty('height')); // true
console.log(robot2.hasOwnProperty('height')); // false
robot1.say = function(string) {
console.log('Ah...' + string);
};
robot1.say('hello world'); // Ah...hello world
robot2.say('hello world'); // hello world
delete robot1.say;
robot1.say('hello world'); // hello world
robot2.say('hello world'); // hello world

这段代码说明了,实例上定义的属性/方法会屏蔽掉原型上的同名属性/方法,而在delete掉实例上的方法后,原型上的方法在实例上又恢复可用了。但是我遇到了一个例外:

1
2
3
console.log(robot1.parts, robot2.parts); // [ 'body' ] [ 'body' ]
robot1.parts.push('arm');
console.log(robot1.parts, robot2.parts); // [ 'body', 'arm' ] [ 'body', 'arm' ]

对robot1属性的修改同时影响了robot2, 这说明对于数组的修改是发生在原型上的,这并不是我想要得到的效果。

  • 问题4: 对于原型上数组属性的修改影响到了其他实例

《JavaScript 高级程序设计》对原型链进行了详细的解读,当然上面这个问题就是这本书里讲到的,并且还讲了下面一些内容:

1. 创建构造函数的时候发生了什么?

  1. 在创建任何函数的时候,JavaScript都会创建一个原型对象,并且将函数的prototype属性指向该原型对象
  2. 原型对象会有一个constructor属性,指向该函数
  3. 而原型对象的__proto__属性,指向Object.prototype
  4. Object.prototype.constructor 指向 Object 函数
  5. Object.prototype是原型链最顶层,没有__proto__属性
1
2
3
4
5
6
7
8
9
10
11
12
// 7.js
function myFunc () {
}

console.log(myFunc.prototype); // {}
console.log(typeof myFunc.prototype); // object , 证明1
console.log(myFunc.prototype.constructor); // [Function: myFunc] ,证明2
console.log(myFunc.prototype.__proto__); // {}
console.log(myFunc.prototype.__proto__ === Object.prototype); // true,证明3
console.log(myFunc.prototype.__proto__.constructor); // [Function: Object],证明4
console.log(myFunc.prototype.__proto__.__proto__); // null,证明5

创建构造函数时的原型链

2. 使用new进行实例化之后发生了什么?

  1. 基于构造函数创建了一个实例对象
  2. 该实例对象的__proto__属性指向构造函数的prototype属性
  3. 该实例对象的constructor指向构造函数 // todo: 这部分要放到图片里吗?
  4. 原型链其他部分未发生变化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var myObj = new myFunc;

console.log(myObj instanceof myFunc); // true
console.log(myObj instanceof Object); // true
console.log(myObj.constructor); // [Function: myFunc]

console.log(myObj.__proto__ == myFunc.prototype); // true

console.log(myFunc.prototype); // {}
console.log(typeof myFunc.prototype); // object
console.log(myFunc.prototype.constructor); // [Function: myFunc]
console.log(myFunc.prototype.__proto__); // {}
console.log(myFunc.prototype.__proto__.constructor); // [Function: Object]
console.log(myFunc.prototype.__proto__ === Object.prototype); // true
console.log(myFunc.prototype.__proto__.__proto__); // null

实例化对象时的原型链

3. 属性/方法调用的内部流程是怎么样的?

  1. 在实例中找
  2. 如果没找到,去__proto__里找
  3. 如果没找到,去__proto__.__proto__里找
  4. 如果__proto__为null,说明已经到达了原型链的最顶级,也就是Object.prototype,属性/方法在原型链上未定义,返回undefined

4. 重新定义原型链的时候遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
// 8.js
function myFunc() {
}

var myObj = new myFunc();

myFunc.prototype = {
say:function(string){
console.log(string);
}
};

myObj.say('hello world'); // TypeError: undefined is not a function

这看上去很难理解,因为重写构造方法的原型并不会删掉之前的原型,也不会改变实例和原型之间的关系,所以我们又发现了一个问题

  • 问题5:重新定义prototype会切断原型链,并且你会发现constructor也不见了。

重新定义原型前的原型链
重新定义原型后的原型链

所以如果想要完美的重写prototype需要这样:

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
// 9.js
function myFunc() {
}

var myObj = new myFunc;

myFunc.prototype = {
constructor : myFunc,
// __proto__ 会自动创建并指向 Object.prototype
say: function(string) {
console.log(string);
}
};

console.log(myObj instanceof myFunc); // false
console.log(myObj instanceof Object); // true

console.log(myFunc.prototype.__proto__ == myObj.__proto__.__proto__); //true

myObj.__proto__ = myFunc.prototype;

console.log(myObj instanceof myFunc); // true
console.log(myObj instanceof Object); // true

console.log(myFunc.prototype.__proto__ == myObj.__proto__.__proto__); //true

myObj.say('hello world');

除了上面这些问题,其实原型模式还有一个问题:

  • 问题6: 原型模式的构造函数没有参数

混合构造函数&原型模式

为了解决问题4和问题6,就出现和混合构造函数&原型模式

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
// 10.js
function Robot(height, weight, color) { // 问题6解决
this.height = height + 'cm';
this.weight = weight + 'kg';
this.color = color;
this.parts = ['body'];
}

Robot.prototype.say = function(string) {
console.log(string);
}

var robot1 = new Robot(100,50,'red');
var robot2 = new Robot(120,60,'blue');
console.log(robot1);
/*
{ height: '100cm',
weight: '50kg',
color: 'red',
parts: [ 'body' ] }
*/
console.log(robot2);
/*
{ height: '120cm',
weight: '60kg',
color: 'blue',
parts: [ 'body' ] }
*/
robot1.parts.push('arm');
console.log(robot1.parts,robot2.parts); // [ 'body', 'arm' ] [ 'body' ], 问题4解决

动态原型模式

为了让代码看上去更美观一点儿,又有了动态原型模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 11.js
function Robot(height, weight, color) {
this.height = height + 'cm';
this.weight = weight + 'kg';
this.color = color;
this.parts = ['body'];

if (Robot.prototype.say === undefined) {
// if (Robot.say === undefined) { // 这样不行, 会重复执行,why?
// if (this.prototype.say === undefined) { // 这样也不行, TypeError: Cannot read property 'say' of undefined,why?
Robot.prototype.say = function(string) {
console.log(string);
}
}
}

var robot1 = new Robot(100,50,'red');
var robot2 = new Robot(120,60,'blue');

console.log(robot1.say === robot2.say);
robot1.say('hello world');
robot2.say('hello world');

混合工厂模式/寄生构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 12.js
function createRobot(height, weight, color) {
return {
height : height + 'cm',
weight : weight + 'kg',
color : color,
say : function (string) {
console.log(string);
}
}
}

var robot1 = new createRobot(100,50,'red');
var robot2 = new createRobot(120,60,'blue');
console.log(robot1.height,robot1.weight,robot1.color); // 100cm 50kg red
console.log(robot2.height,robot2.weight,robot2.color); // 120cm 60kg blue

W3School和《JavaScript 高级程序设计》中都提到了这样一种模式,而且还都说明了不推荐这样使用。这里让我很想不明白,从代码上看,和工厂模式没有区别,只是实例化的时候使用了new关键字,但是实例化的时候new并没有生效,个人理解这种方式的工作原理和工厂模式是一样的,所以也和工厂模式有着同样的问题。

(未完待续)

经典问题再解读:不用中间变量交换两个变量的值

大概八年前,写过这样一篇文章:不使用中间变量来交换变量的值,后来面试的时候常常遇到这题,最近翻出来看,发现当时对这个问题的理解不够深刻,所以今天又整理了一下。

1. 一些有限制的方法

字符串版本

1
2
3
4
5
6
<?php
$a = "a";
$b = "b";
$a .= $b; // a=ab, b=b
$b = str_replace($b, "", $a); // a=ab, b=a
$a = str_replace($b, "", $a); // a=b, b=a
1
2
3
4
5
6
<?php
$a = "a";
$b = "b";
$a .= $b; // a=ab, b=b
$b = substr($a, 0, (strlen($a) - strlen($b))); // a=ab, b=a
$a = substr($a, strlen($b)); // a=b, b=a

上面这两个方法使用了字符串替换和截取的方法,有一个限制就是只适用于字符串。

加减法

1
2
3
4
5
a = 1;
b = 2;
a = a + b; // a=3, b=2
b = a - b; // a=3, b=1
a = a - b; // a=2, b=1
1
2
3
4
5
a = 1;
b = 2;
a = b - a; // a=1,b=2
b = b - a; // a=1,b=1
a = b + a; // a=2,b=2

乘除法

1
2
3
a = a * b;
b = a / b;
a = a / b;

用除法来解决这个问题,多了一个限制,b不能等于0

一句话版本

1
a = b + 0 * (b = a);
1
a = (b - a) + (b = a);
1
a = (a + b) - (b = a);
1
a = b + (b = a) * 0;

这些方法利用了表达式的返回值

所有的加减乘除的方法里有两方面限制:

  • 只适用于数字
  • 如果变量是浮点数,会有精度上的损失

看到网上有一些人提出适用+和*的时候会导致结果向上溢出,但其实这并不影响结果,因为最后逆操作会产生一次向下溢出。

eval版

1
eval("a="+b+";b="+a);

eval版本可能有两个问题

  • 安全性
  • 是如果想支持更多的数据类型比较麻烦

异或版本

1
2
3
4
5
6
<?php
$a=10; //$a=1010
$b=12; //$b=1100
$a=$a^$b; //$a=0110,$b=1100
$b=$a^$b; //$a=0110,$b=1010
$a=$a^$b; //$a=1100=12,$b=1010

下面是简化版本

1
2
3
4
<?php
$a ^= $b;
$b ^= $a;
$a ^= $b;

异或适用于整数和字符串

2. 适用于所有的数据类型,并且没有限制的方法

对象版

1
2
3
a = {a : b, b : a};
b = a.b;
a = a.a;

数组版

1
2
3
a = [a,b];
b = a[0];
a = a[1];
1
a = [b,b=a][0];

匿名函数版

1
2
3
4
5
6
7
8
a=(function(){
try {
return b;
}
finally {
b = a;
}
})();

PHP 版本

1
list($var1, $var2) = [$var2, $var1];

Python&Ruby版本

1
a,b = b,a; // python和ruby程序员都笑了:D

ES6 版本

1
[a,b] = [b,a];

3. 总结一下

解决这个问题的基本思想有以下几种:

  • 将两个变量同时放入其中一个变量,再分别取出,例如字符串版本,数组版本,对象版本

  • 将两个变量通过某种计算的结果放入其中一个变量,再用计算结果和另一个已知变量逆向取回结果,例如异或版本和一部分加减乘除的版本

  • 利用语言特性,例如eval版本,匿名函数版本,php版本和python&ruby版本

WordPress到hexo搬家小记

WordPress是一个以PHP和MySQL为平台的自由开源的博客软件和内容管理系统。WordPress具有插件架构和模板系统。Alexa排行前100万的网站中有超过16.7%的网站使用WordPress。到了2011年8月,约22%的新网站采用了WordPress。WordPress是目前因特网上最流行的博客系统。

前言

从08年开始玩WordPress,着迷于她的强大和灵活,臣服于她强大的themes机制和plugins机制。当是还有不少博客专门研究wordpress,我也曾经深入的研究过其中的一些原理,还开发了sablog-x转wordpress的工具。

我为什么要放弃WordPress?

时过境迁,不再是曾经意气风发的少年,折腾的少了,内容更新也缓慢,由于WordPress太过庞大,插件质量也参差不齐,导致WordPress出现过很多的漏洞,经常发现内容被篡改,文件被篡改,被垃圾评论骚扰,甚至有些内容被改丢了,而发现的时候可能已经过去几个月了。

我为什么选择hexo?

  • 其实最主要的原因,是最近开始关注Node,而hexo是目前最流行的Node博客系统。而为什么关注Node,说来话长,这可能要从PHP为什么是“最好的语言”开始说起,而我不得不承认JavaScript将成为下一个“最好的语言”。

  • 生成纯静态网站,可以直接发布到Github,几乎不存在性能问题和安全问题,而那些你觉得不可能静态化的内容,用服务就好了,比如:评论、搜索

  • 基于Markdown文件的博客系统,Markdown让写作更美好,曾起何时我也有过这样一个想法,想开发一个基于文本的博客系统,不过最终没有开发完那个系统,后来我用那个思想开发了 http://qltx.cn ,里面有一个从目录文件生成数据的算法。

  • 轻量,有完善的中文文档 https://hexo.io/zh-cn/ ,作者是台湾开发者@tommy351

迁移过程

这里并不想写安装过程,安装步骤可以去官方看文档,简单记录一下数据迁移过程中遇到的一些问题

  • npm 安装速度慢,经常卡住,遇到这种情况可以把vpn调整到全局模式重试

  • 数据转换官方提供了migration插件,通过命令行导入wordpress是非常快的,不过转换完毕后我的hexo就无法访问了,这种情况通常是因为文章中的某些字符无法解析,排除法找到那篇文章处理一下就可以了。

  • hexo中的分类和WordPress中的分类不同,一篇文章只能属于一个分类,所以要调整所有文章的分类内容

  • 官方推荐的很多theme安装后都无法使用,这是版本兼容性的问题

  • 迁移过来之后WordPress中的附件需要处理,我的做法是把整个wp-content目录原样放到了public目录下,这会有一个问题,就是每次generate都会被清理掉,所以更好的办法是放到第三方

  • 评论使用disqus,注册完毕要验证邮箱,创建app,并且要设置安全域名,这样才能成功加载,在disqus中导入WordPress的备份数据可以完成评论的迁移

  • WordPress中的gallery标签在hexo中全部失效,可以用hexo中的photos属性,因为缩略图布局的缘故,所以我改动了theme,增加了一个wpphotos属性,并且用脚本将WordPress中的gallery全部转成了url,caption的格式

  • 后期我对默认theme做了大量的改动,如果想找到更多的themes,可以去官网或者看这篇文章 http://www.zhihu.com/question/24422335

说点儿题外话

在迁移的过程中我又重新整理了我写过的所有文章,一共有500多篇,感慨自己曾经的执着、稚嫩,而那大概400篇文章,将永远沉睡在草稿中。

从我第一次搭建博客到现在已经有10个年头,维护这个网站花掉了我大量的精力,几乎伴随着我整个大学时代和职业生涯,甚至还有高中的一段时间,从一个技术小白到一名web工程师,我在整个过程中学到了非常多的技术知识,也对人生有了很多新的感悟,虽然这里并没有写出特别好的技术类文章。

如果说有什么东西见证过我的青春,那一定就是这里。