杂食性程序猿:专注于 Android,web frontend,flutter,node.js 等大前端领域,也会玩玩 python 和 java 等后端技术 ...

一点碎碎念

看到上次刷了一刷存在感的文章还是在三年以前,之所以很少写文章。主要还是因为觉得没啥想说了。也许年龄渐长,感觉表达欲望也没有那么的强烈了。
正好也乘着这段工作的空档期闲了下来,俗称灵活就业人员。时间比较充裕。看着惨 不忍睹的这个小博客,想来还是更新下吧。

上一段工作经历是一段很不愉快的经历。在一家币圈的小创业团队呆了有八个月。本来是带着美好的憧憬和完全的信任加入的公司 ,哪知道后来发生的事情跟我想法貌似有些不一样。实际上去之前,我也咨询了很多的朋友和以前的同事们。现在回想想,当时是有朋友还是给出了一些委婉的建议的。奈何忠言逆耳,哈哈哈。 想想很多时候,人面对的选择的时候最大 的挑战实际上就是自己。勇敢的面对自己内心最真实的想法,是需要莫大勇气的。道理都懂,但是每个人在成长这块,该踩的坑,一个都少不得。

过去的事情也不必太纠结,就让他过去吧。不过回过头来想一想,我觉得经历了这么一趟,对我来说也不完全是个悲观的事情。在加入这个小团队半年多的时间里,还是学习了很多东西的。基于区块链架构的这么一套产品体系有这么给我打开了一个新的世界。我始终有一个观点,能够提升生产效率,改进生产关系的任何东西都是未来的方向,比如说现在的 defi、web3 等等。
之所以之前一直没有深入了解过这块,到了这家小单位才自己的挖掘研究,很大程度上是受到大舆论环境的引导,这个行业目前唯一最落地的产品,应该是加密货币。“币圈”在 大家的印象里,简直就成了骗子的代名词。我曾经也是这么以为,因为这个行业骗子确实多。目前来说是这个行业是处在发展的早期,很多的想法还是在概念阶段,实际上如果深入研究下,就会 发现很多都 是非常棒的概念,毕竟这不断冲进去的一茬一茬的个人甚至包含一些金融机构的韭菜也不傻。用一个比方来说,隐隐的给人的感觉,现在的区块链就像是在95年的互利网。跟互利网一样,同样是 需要经历过几轮泡沫的洗礼,才会边的越来越成熟,它真正的价值才会慢慢的凸显出来。

后边博客的不定期更新里,会做了相应的学习记录和思考总结。

虽然身在帝都工作的机会多多。但是大龄码农在哪边都不是太受待见,尤其是在这个大经济环境严重下行的九月。整一个月下来,面试非常的多,但是最终谈拢的单位一个 都还没有完全敲定。简单复盘了下,一个原因实实在在讲,性价比是比不过工作几年的小朋友的,待遇比他们高不说,管理成本也比他们高,当然了有些不需要管理成本的高端岗也不是什么时候 都有的。再者,随着年轻和阅历的增加,也越来越清楚自己想要的是什么,市场能够提供的岗位也被我筛去了一大半。

不知不觉碎碎念了这么多,继续加油挣奶粉钱把,骚年!

好像很久没有写文章了呢

我这个博客还是运营着的~ yeah~

树莓派(MCU)中python读取jy901陀螺仪数据

前段时间折腾四轴飞行器。 飞控这块打算用陀螺仪和树莓派的方式来实现。不要问哥为啥不直接买现成的,因为我们是有 ( mei ) 逼 ( you ) 格 ( qian ) 的工程师。

后来我发现原来一个飞控大概是我买的陀螺仪和树莓派的成本的1/5吧 --! 摔啊 !!!
某宝上搜索结果比较多的是国外产的10块钱都不到的大众陀螺仪~ jy901貌似是款国产的陀螺仪芯片。
本着“便宜没好货”的思想作祟下。再加上jy901 PR文稿的的忽悠,就果断的下单了。

模块的示例中,大多都是C代码。对于使用者而言比较晦涩难懂。 Python 的简易示例代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*

#
# @author XU Kai(xukai.ken@gmail.com)
# @date 2017-01-21 星期日
#
#
# #fileOverview 树莓派串口操作事件,用来输入和输出陀螺仪数据信息 
#
#
#

from __future__ import division

import os
import sys
import math
import codecs

import serial

sensor = serial.Serial(port='/dev/ttyAMA0', baudrate='9600', timeout=1)


def convert(hexVal):
    return int(codecs.encode(hexVal, 'hex'), 16)


while True:
    data = sensor.read(size=1)
    if (data == b'\x55'):
        print('Get the data !')
        sensor.read(size=10)
        break

    print('trying', data)

try:
    while True:
        data = sensor.read(size=11)
        if not len(data) == 11:
            print('Byte error !')
            break

        if data[1] == b'\x50':

            # print(convert(data[7]))
            pass

        # Angle Output.
        if (data[1] == b'\x53'):
            hexVal = []
            for i in range(11):
                hexVal.append(convert(data[i]))

            ax = ((hexVal[3] << 8) | (hexVal[2] & 0xff)) / 32768 * 180
            ay = ((hexVal[5] << 8) | (hexVal[4] & 0xff)) / 32768 * 180
            az = ((hexVal[7] << 8) | (hexVal[6] & 0xff)) / 32768 * 180

            print(round(ax, 3), round(ay, 3), round(az, 3))

except KeyboardInterrupt:
    sensor.close()
    print('Close the sensor !')

其中有几点需要说明下:

  1. a). 该段示例代码是对串口信息的读取。不包含数据的写入。
  2. b). /dev/ttyAMA0 是树莓派的串口在设备中的名称,依不同的设备而异。

亲测可用,欢迎交流。

Enjoy it !
转载请注明出处
botobe.net
本文Github链接

树莓派智能小车控制

前段时间折腾了一台小车,使用树莓派进行的控制。DEMO和硬件相关的展示部分如下:

软件相关部分为了三块:
一个是小车端(在树莓派中运行)
一个是服务器端,用来中转手机发送给小车的指令。
一个是手机端,用来做小车的遥控器(目前仅包括了小车前后左右和摄像头上下左右的控制)。
三个部分使用socket协议进行连接。相关代码分享在了Github上。
仓库分别是:
小车代码仓库服务器端仓库遥控器代码仓库
其中小车部分的代码仓库中。积累了不少对树莓派的控制相关代码演示(高低电平以及PWM信号)。
欢迎一起交流学习。

Enjoy it !
转载请注明出处
botobe.net
本文Github链接

coffeescript 词法分析

无论是对我们的代码解释执行,还是编译执行,词法分析都是作为第一个环节开始的。
词法分析的过程,可以理解为将字符序列转换为单词(Token)序列的过程。
本文中以如下的coffeescript代码为示例:

# Assignment:
number = 42
opposite = true
# Conditions:
number = -42 if opposite
# Functions:
square = (x) -> x * x

这里的单词(Token)是指一系列最小的对程序来说有意义的单元,例如"number", "=", "(换行)", "(空格)"等等。
对于coffeescript而言,有10中类型的token
1. identifier(标识符):脚本中各种独立的标示符,例如number,opposite等等
2. comment(注释)
3. whitespace(空格)
4. line(换行)
5. heredoc(本地注释):以"""或者'''开头的代码注释
6. string(字符串):匹配的coffeescript中得字符串,例如'test'(coffeescript中得字符串用单引号围起来)
7. number(数字):匹配的数字,包括了8进制数字和16进制的数字
8. regex(正则表达式)
9. js(js):匹配的正则为/^[^\\](?:\.[^\`])*`/,我觉得我的解释太TM多余了,还是看代码来的直观
10. literal(加减乘除,位运算,与或之列的符号)
输入的是如上的一串coffeescript的代码,输出则是按照这是种token分类好的程序的最小单元的一个数组,提供给语法分析器继续进行分析。
例如:


Enjoy it !
转载请注明出处
botobe.net
本文Github链接

coffeescript 编译调试剖析

工欲善其事,必先利器。我们用node-inspector来调试coffeescript的编译。
用全局的方式安装node-inspector

sudo npm install node-inspector -g 

新建一个叫coffee得目录,用来作为我们的工作目录。
在目录中新建一个package.json文件,可将如下内容填入其中(截止目前为止我们用coffeescript最新的1.8版本进行调试)

{
  "name": "coffee-debugger",
  "version": "0.0.0",
  "private": true,
  "dependencies": {
    "coffee-script": "1.8.0"
  }
}

再命令行中运行如下命令安装当前工程依赖的coffeescript

sudo npm install 

在我们的工作目录中,新建一个用来测试的coffeescript脚本main.coffee
这个时候工程中的目录结构应该是这样

下边就可以正式的开始进入主题了,打一个命令行,启动node-inspector

node-inspector

再打开一个命令行,并且进入到coffee目录中

node --debug-brk node_modules/coffee-script/bin/coffee -c main.coffee

在浏览器中打开URL 127.0.0.1:8080/debug?port=5858 就可以开始调试coffeescript的编译过程了(-c开关表示将main.coffee编译成为main.js脚本)

Enjoy it !
转载请注明出处
botobe.net
本文Github链接

原地满血复活

当我下定决心开始做一个有趣而有意义的事情时,我都没有想到自己的动力会那么大,被自己都吓到了~ 哈哈 :)

在博客杂草重生的荒废了n个月之后,趁着最近略有闲暇,噌噌噌的花了两个星期的业余时间,把博客重新又搞起来了。
新在Github上开了一个repo,把原先的数据库由mongodb改成了mysql。于是乎还是发现mysql用起来比较顺手啊~ VPS由原先的burstNet升级到了linode,express更新到了最新的版本,作为老本行的前端也做了深层次的优化。
瞬间有种鸟枪换炮,犀利哥走上了巴黎时装周感觉~

当然,博客最重要的还是内容啦。
从2010年6月份开始,到2014年的10月份,将近四年的时间遇到过不少人不少事,有傻逼的,又聪明的,有二逼的,有装逼的,有真诚的,有需要感谢的,有永远不想见的,有开心的,有难过的,有值得骄傲的,有痛苦的 ... ...
博客换过域名,换过内容,换过主题,换过技术架构。
总之贯穿了我毕业后的整个职业历程,也记录下了我的成长。看着自己从一个整天自怨自艾的小青年变成今天的自己。想想还是蛮欣慰的。这些年,没有白白的从指缝间溜走。
大大小小的写了约有四十来篇文章,网站迁移过几回,丢失了不少文章和图片。懒得去翻硬盘里那些百八十年的老数据去找了。以往对数据备份完全没啥概念的我,这回可是深有体会了。纸上得来终觉浅,这就算是成长的学费了吧。

总体看来,技术文章的数量在不断的减少,但是质量越来越高了,多了些体会和感悟。
重新上路,相聚无时,后会有期!

过年啦!

已经是年初五了,假期的日子过得就是这么的迅速啊,总是有昨天才放假的感觉。
回顾过去的2013年,有很多想说的话,留给2014年的结尾吧!

大家新年快乐,大吉大利!

2014.02.04
此文仅以刷一下微不足道的存在感
Merci !

自定义你的javascript事件

浏览器内置了一些javascript的方法,比如说click, mouseover, mousein ... 可以在用户进行了一些浏览器内置的默认行为(点击,鼠标滑过DOM)的时候会触发。
说道自定义javascript事件,给大家印象比较深的也许是jQuery的bind方法,可以添加一些用户自定义的事件,再用trigger的方法来触发之。
基本的实现代码如下:

/**
 * 事件管理组件
 * 版权所有(C) 2013 EchoFUN (xukai.ken@gmail.com)
 * 
 * 2013.12.12 初始化文件。
 * 
 * Example:
 *   var test = document.getElementById('test');
 *   evt(test).bind('custom', function() {
 *
 *   })
 *
 *   evt(test).trigger('custom');
 *
 */

;var cacheId = 'cache' + setTimeout(function() {}, 0);

var evt = (function(global, cacheId) {
  'use strict';
  var isIE = !!document.attachEvent;
  var typeExpress = /^[a-z]+$/i;

  // 缓存所有的事件。
  var events = {};

  // 产生一个唯一的键值。
  var globalId = 1;

  function wrap(node) {
    return new _wrap(node);
  }

  function _wrap(node) {
    var _node = (node && node.nodeType == 1) ? node : '';
    if (!_node) {
      return 'Invalidate node !';
    }

    _node[cacheId] || (_node[cacheId] = globalId++);
    this._node = _node;
  }

  function _type(o) {
    var type = {}.toString.call(o);
    return type.slice(8, -1).toLowerCase();
  }

  var proto = _wrap.prototype;
  proto.bind = function(type, handle, capture) {
    if (_type(type) !== 'string' 
      || !typeExpress.test(type) 
      || _type(handle) !== 'function') {
      return 'Invalidate params !';
    }

    var node = this._node;
    if (isIE) {
      node.attachEvent('on' + type, handle);
    } else {
      node.addEventListener(type, handle, capture);
    }

    // 如果还未开始对这个对象有缓存,则加入缓存序列。
    if (!events[node[cacheId]]) {
      events[node[cacheId]] = {};
    }
    var evts = events[node[cacheId]];
    if (evts && evts[type]) {
      evts[type].push(handle);
    } else {
      evts[type] = [handle];
    }
    return this;
  };

  proto.unbind = function(type, handle, capture) {
    if (_type(type) !== 'string' 
      || !typeExpress.test(type) 
      || (handle && (_type(handle) !== 'function'))) {
      return 'Invalidate params !';
    }
    var node = this._node;
    var evts = events[node[cacheId]];
    if (!evts[type]) {
      return 'Not exist specified event !';
    }

    if (isIE) {
      node.detachEvent('on' + type, handle);
    } else {
      node.removeEventListener(type, handle, capture || false);
    }
    if (handle) {
      for (var i = 0; i < evts[type].length; i++) {
        if (evts[type][i] == handle) {
          evts[type].splice(i, 1);
        }
      }
    } else {
      evts[type] = [];
    }
    return this;
  };

  proto.trigger = function(type) {
    if (_type(type) !== 'string') {
      return 'Invalidate params !';
    }

    var args = Array.prototype.slice.call(arguments);
    args.shift();
    var evts = events[this._node[cacheId]];
    if (evts[type]) {
      var evtsList = evts[type];
      for (var i in evtsList) {
        evtsList[i].apply(this._node, args);
      }
    }
    return this;
  };

  return wrap;
})(this, cacheId);

完整示例请移步Github event.js文件

对javascript的事件自定义,只是其庞大事件系统的冰山一角。
口水横飞的讲了这么多,不知道你会不会有这样的疑问。
自定的这些事件有个蛋用啊!?

网上关于自定义事件的方法一抓能有一把,但是总结为何这样做的文章却很少。
具我在项目中使用情况看来,可以总结出起码有如下两种情形:
a). 在制作一些前端组件的时候,可以为特定的事件添加钩子
举个栗子:
人人网的表情组件(PS. 居然注释部分没有压缩掉哈,剩下了我的口水了。)
开发人员可以对new了XN.ui.emoticons这么个对象后的返回值添加各种事件。组件中已经在各个关键节点(渲染结构,绑定事件,展示)的地方fireEvent了这些事件。
这样就可以进行无侵入式的编程。在不改变原有逻辑的情况下,添加一些自己想要的行为,岂不爽哉!?

b). 模块化开发中,可以方面的为各个模块之间进行解耦
现在的前端工程里,有追求的前端们(咳咳,你懂的!)大多使用了common.js的规范来组织前端代码,各个模块的数据和操作都可能是独立的,但是模块和模块之间的沟通通过什么方式来进行呢?
(自定义事件!)
恭喜,都会抢答了。
细细说来,有点类似于前些文章中聊到的观察者模式,在我发布了一篇微博的时候,需要出发右侧的微博数目。这个时候,我就可以在发布框的模块中,trigger或者是fireEvent一下事先已经绑定在右侧数目计数器上的一个自定义的事件,这个举例比较简单,如果模块功能很复杂,就能做到充分的解耦。
能力有限,欢迎各路大神继续补充和拍砖。

写在结尾处:
之前的文章里很少有提及CSS的部分,之所以不涉及这块,并非不是很了解。奈何没有像@张鑫旭单口相声般的牛逼技能,写出来的文章就像懒婆娘的裹脚布一样。这个也是需要我学习和将强的地方啦,哈哈。

Enjoy it !
转载请注明出处
botobe.net
本文Github链接

2013.12.13
最近情绪不佳,有点小郁闷!
依旧Merci !

单例模式的在前端开发中的应用

公司项目中使用到了单例模式,于是撰文一篇。
单例模是Gof提出的一个简单的设计模式。

在前端项目中用到单例模式,给整个前端工程带来的优势是节省了浏览器的CPU的使用,在占用内存方面未必占有优势
从算法的角度看来,算是以空间换时间的一个经典的例子。

常见的地方一般使用在,前端创建需要大量重复使用的一段具有类似功能DOM的时候。
例如微博中的转发的弹框。

让我们先来看看创建了这么一个转发弹框的基本流程。
a). 构建HTML
b). 拼装模板(插入一些动态数据)
c). 绑定自定义事件
d). 插入文档流
顺序可以改变。

这些流程的操作是需要消耗系统的CPU资源的。
让我们考虑在这个弹框足够复杂的情况下。
如果你使用的是chrome神器,自然不会感觉到速度上的差异。但是如果你使用的是IE6之流的老古董,会体验到明显的感觉到差异。
作为一个苦逼的前端,如果你开发的是像google,taobao那样的产品,那么恭喜你。你可以横着脑袋,翘着二郎腿对用户说“赶紧给老子去升级你的浏览器,否则滚蛋!”。
不过我相信98%的前端同学面临的选择题都是:要么用户留下,要么你滚蛋。
那你会选哪个捏?

开个玩笑,言归正传。
实现前端开发中的单例模式的方式有很多种,但是总体来说都是有着类似的思路。
即,在页面上执行一次需要的逻辑(例如上边所说的a, b, c步骤),将执行的结果进行缓存在一个闭包的变量中(防止污染全局变量)。而后,每次的调用只需要执行d步骤就可以了,如果有额外的逻辑,可能还要在d步骤之后或者之前再加上个b步骤(如果每次的展示界面略有差异的话)。

巴巴儿地扯了半天,就像我们浏览某榴网站一样,有些同学可能要抱怨了 —— “没代码你说个j8!“
于是,借花献佛的引用了在腾讯AlloyTeam【Javascript设计模式1】-单例模式中的代码。

var singleton = function( fn ){
  var result;
  return function(){
    return result || ( result = fn .apply( this, arguments ) );
  }
}

这是采用了高阶函数的方式来实现单例模式。
按照这篇文章原作者的思路(可理解为伪代码),调用的方式为。

var createMask = singleton( function(){
  return document.body.appendChild( document.createElement('div') );
})

按照我们所讲的a, b, c, d的四四中执行方式来讲,d步骤和b步骤进行了合并。这种方式并不是太好。
私下认为可以把调用方式改为如下,可能会更加形象。

var getMask = singleton(function() {

  // 执行a, b, c步骤。
  var mask = document.createElement('div');
  console.log('A div has created for once !');
  return mask;
});

var blankMask = getMask();
document.body.appendChild(blankMask);

window.setTimeout(function() {
  blankMask.parentNode.removeChild(blankMask);

  window.setTimeout(function() {
    document.body.appendChild(getMask());
  }, 1000);
}, 1000);

结语:
而前端环境的局限性在于变量的生存周期再一次页面的重新加载中。
而不像后台语言中能做到的那样,一次创建,随意使用。
使用单例模式,能使得代码更易于维护,再者算是满足了程序猿们的小小精神洁癖吧,哈哈,何乐而不为呢?

Enjoy it !
转载请注明出处
botobe.net
本文Github链接

2013.11.06
一切安好。
Merci !