• 这是我的第一个 Chrome 扩展,利用豆瓣 API 完成
    感觉和做 iPhone 的 web app 类似,比开发 firefox 扩展成本低,一些心得稍后分享

    1.0.1 更新内容:

    * 解决了 dev 版 Chrome 无法授权认证的问题;
    * 解决了用户在授权后无法正常访问豆瓣第三方应用管理页的问题;
    * API 方面修复了推荐话题链接为空的 bug(by py@douban)

    在线安装:
    https://chrome.google.com/extensions/detail/dofcilnakjpenampigbefbbeekanbfgl?hl=en-us

    默认显示友邻广播时,它长这个样儿
    当滚动条到达底端时会自动载入之前的广播,根据不同分类,每个类别的列表会请求不同的条数

    Google Chrome Extensions Douban Pulse
    发布“我说”时是下面介个样儿

    由于 API 缺少友邻“我说”等的接口,我采取的是先获取全部广播再过滤的方式请求列表,所以你可能会看到,"所有广播"之外的列表项是增量载入的

    Google Chrome Extensions Douban Pulse

    新消息提醒

    Google Chrome Extensions Douban Pulse

    点击后,未读消息会用绿色边框线标示出来方便确认
    保存最近一条未读信息位置,下次打开时自动滚屏还没有做,打算放到下一版

    这个扩展最大的便利之处我认为在于不必每次都返回豆瓣新版社区刷 timeline,而且方便查确认未读消息;与网页相比不足之处应该是单个条目展示信息少

    日后会有更多的改进,欢迎大家试用,有问题随时留言,或者到豆瓣插件小组:
    http://www.douban.com/group/topic/10150470/

    @mockee
  • 留了一半在草稿箱都快忘了,趁闲整理出来以便参考(按自己理解简单翻译记录)
    原文:http://jquery14.com/day-01/jquery-14, http://jquery14.com/day-12


    1.4 中重写了常用方法以提性能

    jquery 1.4 重写了常用方法以提升性能


    Setter Functions

    可以给下面所有的 Setter Functions 传一个函数:
    .css(), .attr(), .val(), .html(), .text(), .append(), .prepend(), .before(), .after(), .replaceWith(), .wrap(), .wrapInner(), .offset(), .addClass(), .removeClass(), .toggleClass()
    这个函数的返回值将附在相应的 html attribute 上;

    对于以下方法:
    .css(), .attr(), .val(), .html(), .text(), .append(), .prepend(), .offset(), .addClass(), .removeClass(), .toggleClass()
    当前值将作为第二个参数传递给该函数;


    Ajax

    jQuery.param 方法加入了嵌套参数的序列化支持;
    (Commit 1, Commit 2)

    通过 content-type 自动识别 JSON, js 的数据类型;
    (jQuery.ajax, Commit 1, Commit 2)

    增加了 Etag 支持;
    (jQuery.ajax(), Commit)

    使用原生方法 JSON.parse 来解析 json;
    (jQuery.ajax(), Commit 1, Commit 2, Commit 3)

    $.serialize() 可序列化 HTML5 的表单元素;
    (jQuery.param(), Commit)

    $.ajax() 增加 context 设置,自定义回调函数中的 this;
    (jQuery.ajax(), Commit)

    所有 ajax 成功请求的回调函数都会有包含原始 XHR 对象的第三个参数;
    (jQuery.ajax(), Commit)

    显式设置 content-type;
    (jQuery.ajax(), Commit)

    通过 jsonpCallback 选项指定 JSONP 的回调函数名;
    (jQuery.ajax(), Commit)

    默认禁止启动前的跨域请求
    (Commit)

    jQuery.ajax() 使用 onreadystatechange 替换了计时器,降低 ajax 请求使用的资源;
    (Commit)


    Attributes

    优化了 .css() 和 .attr() 的性能;

    优化了 .css() 和 .attr() 的性能

    text 与 CDATA 节点也支持 .text() 方法了;
    (.text(), Commit)


    Core

    使用 jQuery() 创建函数时可以同时增加属性和事件;
    (jQuery(), Commit)

    .eq(), .get() 支持负数参数,方便从后向前选取对象;
    (.eq(), .get(), Commit)

    新增 .first() 和 .last() 方法,等效于之前的 :first 和 :last,使用更方便;
    (.first(), .last(), Commit)

    新增 .toArray() 方法,区别于 .get() 的是不支持参数;
    (.toArray(), Commit)

    jQuery() 返回一个空集;
    (jQuery(), Commit)

    使用单个标签名时 $('TAG'),jquery 将会使用更快捷的路径 getElementsByTagName;
    (Element Selector, Commit)

    jQuery('<div>'), jQuery('<div/>'), jQuery('<div></div>') 使用了同一个代码路径 document.createElement
    (jQuery(), Commit)


    CSS

    .css() 方法性能翻倍

    .css() 性能翻倍

    .addClass(), .removeClass(), .hasClass() 方法的性能是以前的3倍



    .toggleClass() 可以切换多个 css class;
    (.toggleClass(), Commit)

    .data() 返回对象, .data(Object) 设置对象
    (Commit 1, Commit 2, Commit 3)

    除非需要,否则不会创建数据缓存;
    (Commit 1, Commit 2, Commit 3)


    特效 Effects

    在单个 .animate() 中设置为多个属性设定 easing 函数;
    (Per-property Easing, Commit)


    事件 Events

    新增 jQuery.proxy() 方法以获取相同作用域的函数,解决了重新定义 this 的问题;
    (jQuery.proxy(), Commit 1, Commit 2)

    .bind()  支持多事件绑定;
    (.bind())

    规范了 change 和 submit 事件;
    (Change, Submit)

    新增 focusin 和 focusout 事件,支持冒泡;
    (.focusin(), .focusout(), Commit)

    .live() 升级,支持所有事件,且接受数据对象作为参数;
    (.live())

    live/die 支持 context 了;
    (Commit)

    确定 ready 事件至少要包含 body 元素,否则会对 body 进行轮询;
    (Commit)

    在不需要手动处理内存溢出的非 IE 浏览器中,unload 的速度提高了;
    (Commit)


    DOM 操作

    .append(), .prepend(), .before(), .after() 的性能提高了



    .html() 性能提升到将近之前的3倍



    .remove() 和 .empty() 则达到了之前的4倍



    新增 .detach() 方法,用来移除元素,但不删除事件;
    (.detach(), Commit)

    新增 .unwrap() 方法,用来删除匹配的父级元素,与 .wrap() 作用相反;
    (.unwrap(), commit)

    domManip 方法中设置了缓存,提高使用字符串进行 DOM 操作的页面性能;
    (commit)

    before, after, replaceWith 可以操作还未加入 DOM 的节点;
    (commit)

    .clone(true) 会复制关联数据(包括所有事件);
    (clone, commit)


    偏移 Offset

    可以利用 .offset() 设置元素的 offset 了;
    (.offset(), commit)


    队列 Queueing

    新增 .delay() 方法,用于延迟队列用的函数执行;
    (.delay(), commit)

    .clearQueue() 用于移除队列中还没有执行的所有函数;
    (.clearQueue(), commit)


    选择符 Selectors

    所有以 id 开头的选择符效率提高,且高于其他选择符;
    (commit)


    遍历 Traversing

    .index() 方法重写,可以获得一个元素在 jquery 集合中的索引;
    (.index(), commit)

    新增 .has() 方法,等效于 :has,使用更灵活;
    (has, commit)

    新增 .nextUntil(), .prevUntil(), .parentsUntil() 方法,类似于 .nextAll(), .prevAll(), .parents(),区别在于当遇到与选择符相同的元素时会终止选取;
    (.nextUntil(), .prevUntil(), .parentsUntil(), commit)

    可以为 .add() 方法指定 context 了,可用于在 Ajax 请求中返回的新元素;
    (.add(), commit)

    可以在 .cloasest() 的第二个参数中指定 context,提高执行速度;
    (.closest(), commit)


    实用工具 Utilities

    jQuery.isEmptyObject() 判断对象是否为空;
    (jQuery.isEmptyObject(), commit)

    jQuery.isPlainObject() 判断对象是否以字面量创建;
    (jQuery.isPlainObject(), commit)

    jQuery.contains() 如果两个参数都是 DOM 节点,判断二者是否存在嵌套;
    (jQuery.contains(), commit)

    jQuery.noop() 返回一个空函数;
    (jQuery.noop(), commit)

    jQuery.unique() 返回结果中的元素是按照它们在页面中的顺序进行排序的;
    (jQuery.unique())


    1.4.1 更新

    .live('focus') 和 .live('blur') 可以用了,映射到 .live('focusin') and .live('focusout');
    (Documentation, Ticket)

    .live('hover') 可以用了,映射到 .live('mouseenter mouseleave);
    (Documentation, Ticket)

    .live() 支持多事件绑定;
    (Documentation, Ticket)

    用 .die() 来移除所有绑定的 live 事件;
    (Documentation, Ticket)

    .height(function) 和 .width(function) 可以用来计算匹配的第一个元素的相关值;
    (Height, Width, Ticket)

    jQuery.parseJSON 将 JSON 解析成 JS 对象;
    (jQuery.parseJSON, Ticket)

    jQuery.error  为插件开发者提供便利,可以覆盖此方法以提供更多更好的错误信息显示;
    (jQuery.error, Ticket)
  • 2009-10-04 20:52:25

    2009.10.4

    2009 满月
  • 2009-09-13 16:04:24

    Yahoo! meme 前端初窥

    发现 咪咪 又发邀请了。从十天前的14个到隔夜后缩减到的3个再到前两天新增的4个,紧张过度了吧,能折腾... 在经历中文用户刷屏后,popular 恢复了正常,不过依然是满眼猎奇大图,也是,如此清新销魂的页面,不使劲儿往上招呼图片视频才是大不敬。

    说正题,来简单关注下 雅虎 meme 的前端:

    先说说设计

    meme 给我的第一印象非常好,甚至是惊喜,也许是受太多 twitter clone(包括围脖)雷同界面的影响一开始对其期望值不高形成的反差。后来发现,其实 mac 下 HelveticaNeue-Light 字体及平滑的文字效果也是功不可没。顺便提一句,Georgia italic 也是俺的大爱:)

    meme 在区域的设计上使用了圆角,包括信息输入、展示区以及 form 中的各式输入框和按钮,对于信息中的图片也采用了双边框的设计,内深(#e2e2e2)外浅(#fcfcfc),有淡淡的立体效果(firefox 中可以使用 -moz-border-bottom 快速完成)。说到圆角,其实只有 gecko 和 webkit 核心的浏览器才能看到,对于 IE 和 Opera 则完全无视了,当然 Camino 下的圆角效果还是一如既往的差,顽固地保持着 firefox2 的残迹,这些下面都会专门提到。皮肤方面,我还是忠于默认:)



    上图是第一个按钮用到的透明图片,边缘溶解,最大限度地分离了前景和背景、减少图片用色(让 IE6 支持此透明 gif)和体积,但最后保存的格式却是 png,不太理解;从这副图片的名字也许可以看出一些原因: posts.gif.png,尝试了去掉 .png,没问题,原来本来就是张 gif,汗~ (注: 来自 chumsdock:关于gif/png的问题,IE6 是支持只有纯透明的 8 位 png 的,用法跟 gif 一样,但是 png 比 gif 尺寸更小。”)

    继续瞎说交互

    这方面我一直都很外行,没数据的情况下基本是靠主观理解 *…*

    yahoo meme

    先看上方的黄色提示,咪咪会识别新信息,但不会强行显示出来,这样的设计很适合中文用户,哈哈,在刚注册时 popular 满屏春色,而且源源不绝,现在虽然少了,但偶尔也能惊喜一下,如果这时被自动推出来,身后有人观摩就太尴尬了 -  -!

    接着看评论输入框,这是在 repost 后显示的,提示和样式都清晰明了;

    最后两条灰色信息是我又爱又恨的地方,爱在于简明地显示了信息来源(你知道我在说哪里),恨在于字太小,如果有图和视频就更显得隐蔽,打开页面一眼望去不好区分哪些信息是首发哪些是 repost(当然现在我知道了按长度区分)。或许雅虎就是有意而为之,认为让用户第一时间获取信息本身才是最好的体验?纯属瞎猜...

    说件土事儿,第一次来 meme 时俺竟然没找到在哪里输入信息,虽然 dashboard 页上方深灰色按钮赫然,但一时没意识到那里就是可以展开的输入区域,还一个劲儿的找大文本框。我想这个应该不属于交互设计的问题,是被 twitter 毒害太深,习惯成自然,meme 这个大框在之后用起来就很顺手了。

    下面说说前端代码

    meme 的前端代码很随性。先看 GBS 最近一次更新:

    yui GBS Q3 2009

    虽然 IE6~8 在 XP 下依然被列为 A-grade,但 meme 似乎已经对 IE6 失去了耐心

    mockee yahoo meme

    IE6(左)高度有偏差,FF3.5(右)为正常

    左侧的黄色区域是一个本不该显示的空层(divMessage),用来防止相应的动态信息,但里面有个 &nbsp;,所以对于 IE6 来说它并不为空,而且还要为其应用继承的文字样式,从而出现了最小高度的问题。黄色区域下方的蓝线是经典的 IE6 图片 3px 问题。

    meme logo 是用 <a> 的背景图实现的,配合 text-indent: -9999px,照顾了爬虫又方便利用 css 控制鼠标悬停切换背景。IE6 用了比较屎的滤镜来解决不支持透明 png 的问题,这种方法的后遗症是无法通过控制背景图片的位置来实现悬停后 logo 外发光的效果,所以在 IE6 下鼠标悬停左上方 logo 是没有任何反应的:

    _background:none;
    _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://l.yimg.com/cc/img/en_PH/1252444248/logo.png', sizingMethod='crop');


    IE6, 7 无法将使用负 margin 的 div 背景图显示在其外层容器区域(#nav)之外,所以利用星标(*)为其做了 hack,这也是上图中 IE6 的 logo 位置会与标准浏览器下不同的原因(IE7 也是如此):

    margin-left: -6px; *margin-left: 0;

    除了 logo,meme 全站使用透明 png 的地方、可以产生经典 bug 的地方,在 IE6 下多多少少都还会有不尽如人意的地方,在此略去。可以说,meme 基本放弃了在外观上 hack IE6。

    除了 IE6,meme 在 Opera 和 Camino 下的表现也不尽如人意:

    yahoo meme mockee

    FF3.5(上)为正常效果,Opera10(下)有明显错位(Camino 有同样问题)

    代码中可以看到,头像为左浮动,用户名(<h1>)和简介(<p>)均为块级显示,而在头像与用户名、简介的水平方向均有相邻的 margin。在其他浏览器中如果是这种情况,用户名与简介的 margin-left 就会从外层容器(.info)的左边缘开始算起(不含 margin 与 padding 的宽度),但 Opera 与 Camino 会与头像的 margin-right 相叠加,所以就出现了上图的错位。解决方法很简单,去掉用户名与简介的 margin-left,只使用头像的 margin-right 来区隔即可。

    yahoo meme

    上图(右侧)是在 Camino 下一处比较诡异的地方,灰框本应是 repost 的按钮(如左图),不知啥原因显示成这样。这个按钮是一个用背景图来控制样式的 input,不知是否和背景图使用了 transparent 有关。

    接下来说圆角,上面已经提到,meme 使用了私有属性实现(-moz-border-radius 和 -webkit-border-radius),没在这方面多较真,很好奇他们的设计稿是啥样的;IE 也就罢了,莫非连 Opera 用户都放弃了?圆角在 firefox 3.0+, safari, chrome 下看都很完美(最赞是 firefox,在 webkit 核心浏览器下,圆角部分比较虚,而同为 gecko 核心的 Camino 表现最差,见下图)

    yahoo meme mockee

    仔细看头像左侧的圆角边框,safari(左) 较虚,firefox(中) 最佳,camino(右) 不但不够平滑,每个方向的圆角样子还不同,这也是在 firefox 2.0 时期同样经历的老问题了,不过 camino 似乎并不在意,最新版 1.6.9 中问题依然存在(userAgent 中也有这样的提示: like Firefox/2.0.0.23)

    说 meme 的前端代码随性还有个重要的地方,就是没有忠于 YUI,css 部分只使用了 reset,而 js 库方面除了 YUI 2.5.0 还用到了 jquery 为主的其他库及插件:
    • jQuery v1.3.2
    • Sizzle CSS Selector Engine - v0.9.3 (dojo)
    • jQuery UI 1.7 custom
    • jQuery Overlabel
    • jQuery Tooltip plugin 1.3
    ... 更多可以直接看 这里

    meme 的 home 页对于图片展示的处理比较有意思,先看图:

    mockee yahoo meme

    默认情况下,信息列表中只显示裁切后的缩略图,当图片的实际尺寸大于缩略图时,点击图片便会出现类似视频加载的样式(左、中),载入完毕,loader 消失,大图显示;如果要载入的是个动态 gif(右),那么一开始就会在缩略图上方叠加个播放按钮,提示用户这里是可以播放的图片。这种加载方式可以带给用户很好的体验,尤其是动态 gif:)

    ps, 上图左侧 IE6,注意看图片下端与边框的距离,3px bug 再现...

    下面来看下实现的 JS 代码:

    goodstuff.ui = function () {
        h = function (t, r) {
            r = r || $(document);
            var s, q = jQuery('<span class="img_loader"></span>');
            r.find(".gifplay").click(function () {
                $(this).parent().children(".image").children("img").click();
            });
            r.find("img.image_expand").click(function (v) {
                imageplace = $(this).parent().parent();
                var u = $(this).parent().parent().children(".img_loader");
                u.show();
                if($(this).hasClass("small")) {
                    $(this).addClass("big").removeClass("small")
                    .load(function () {
                        u.hide();
                        $(this).parent().parent()
                        .children(".gifplay").hide();
                        $(this).parent().parent().parent()
                        .find(".clearexpand").show();
                    });
                    $(this).parent().parent().children(".gifplay").hide();
                    imageurl = $(this).attr("src");
                    $(this).attr(
                        "src",
                        imageurl.replace("/200x200/","/original/")
                    );
                } else {
                    if($(this).hasClass("big")) {
                        u.hide();
                        $(this).addClass("small").removeClass("big");
                        imageurl = $(this).attr("src");
                        $(this).attr(
                            "src",
                            imageurl.replace("/original/","/200x200/"))
                            .load(function () {
                                $(this).parent().parent()
                                .children(".gifplay").show();
                                $(this).parent().parent().parent()
                                .find(".clearexpand").hide();
                            }
                        )
                    }
                }
                return false;
            });
        };
    };

    .gifplay 和 .image_expand 分别是载入时用来叠加在动态 gif 和大图上的 loader 层,图片的切换用的是替换 url 的方法,所以如果想像个人页面一样直接显示大图和动画,自己写个 GM 脚本就好了。代码写的很直白,就不多解释了,汗一下选择符的使用 @__@

    就写到这儿,最后再赞一下 meme 的设计,收工
  • JSLint 不必多介绍,调试脚本的利器,常见语法错误都可以检查到,有了它,对于简单代码的 code review 就不必总让 firebug 占资源了。这里主要说下如何整合 Mac 下的 VIM,方便一起使用;以无用户界面的 Normal version 为例,也就是 terminal 中使用的那个 vi(MacVim 及 TextMate 的整合方式与其类似,而且偏好的配置更方便)。

    首先要下载 JavaScript Lint,或者在他们的 trunk 中 check: svn co https://javascriptlint.svn.sourceforge.net/svnroot/javascriptlint/trunk jsl

    之后解压缩(我放到了 Library/JSLint 中)并设置环境变量:
    在 ~/.profile 中增加 export PATH=${PATH}:~/Library/JSLint/

    接下来下载 javaScirptLint.vim 并将其放在 ~/.vim/plugin 中,如果你没有这两个目录,那么要先 mkdir 下。

    需要的工作做完了,可以找个 JS 试下,当 :w 时会出现检查结果:

    jslint vim

    一切顺利,QuickFix List 中显示的就是对上方 JS 的检查结果,如果你想增设偏好,比如使用自己的快捷键启动调试,那么可以参照 JSLint 的脚本自行添加设置:

    jslint vim

    当然,浏览器中的 JSLint 依然是临时调试的首选。

    明儿下午是 黄修源 童鞋 在五道口光合作用的《豆瓣,流行的秘密》新书发布会,邵夷贝余思 也会到场。先预祝明天发布会成功。

    周末继续爬山,有体力再去游泳,哦也~ 洗洗睡了~
  • 2009-07-11 17:12:31

    最近

    油价一夜飙升,93号汽油已经涨到6块3毛7了,也就是说我每加一箱油就要比涨价前多花26块4,这次涨的真是过;踩几脚油儿就少吃几顿煲仔饭... 就当减肥了;话说上次在老王那儿称,我竟然上了160 @__@ 秤的问题吧?一定是

    新工作两个月了,融入新的团队,结识新的朋友,在一个舒适的环境中与同道中人共事是种很棒的体验。

    在第一阶段前端重构完成后开始了同城地图的相关开发,俺也有幸成为了第二个 Scrum team 的外援。与 bear 的协作很顺利,昨天效率蛮高:) 不得不说 Googe Maps 的文档做的很出色,像我这种第一次接触 GMap 开发的人也能快速上手。推荐看 英文文档,比 中文的 全,比如像 GMapUIOptions 这个 class 在中文文档中就没有出现。

    在看 W3 的 HTML5 文档 时发现 <audio> 标签比想象的还强大,顺手根据现有素材做了个 HTML5 版的电台,由于使用了 CSS3 的一些新增的方法和浏览器的私有属性,所以只能在 Safari 4+ 上完整的运行。昨天让同事试了下,其中提到的一点是俺没想到的 ── iphone & ipod touch 不支持 flash,而避开 flash 使用 JS 完成的电台正好可以用,再加上 safari 又是 apple 自家娃,从风格到功能就都没问题了。

    douban radio html5

    试听地址(三首歌):http://resource.ctba.cn/mockee/radio只限 safari 4.01+
    这里 下载最新版 Safari

    特别注意下,iPhone OS 2.0 的 Safari 版本是 3,无法播放歌曲,要升级到 3.0,3.0 的 Safari 版本才是 4,也才能顺利识别 <audio> 标签。

    iphone safari

    目前 firefox 3.5 虽然 支持 <audio> 标签 但只能播 ogg 格式的歌曲,上面试听的是 mp3,所以也无法播放,不过风格上 css3 支持的没问题。

    douban radio html5

    box-shadow, border-radius, font-face 将原先只能图片完成的效果代码化。时间比较仓促没有用 canvas。下周试试连主曲库,内部再小测下。

    《变形金刚2》还没看,主要公司发的票找不到了,另外一张奖品票也还没领,不知到没到期 @__@  各方面了解了下,还是最想看 3D 的《冰河世纪3》。另外,Eason 的演唱会得去啊~~~

    eason 2009 beijing
  • 2009-05-30 02:32:46

    最近的一些设计

    扯谈新首页部分截图

    在扯谈的简洁版首页上耗了太多时间,直到陷入像素魔咒,恶心的想吐。新首页的设计已经基本完成,讨论修改后会尽快上线,现在暂时保密。上面是新 banner 的部分截图。久违了的“扯谈社”~

    扯谈社 iphone

    打算为 iPhone, iPod Touch 单独做个入口,浏览起来顺手些。iPhone Simulator Platform 效果很棒,做测试再合适不过,没玩过的可以尝试下。

    扯谈社 iphone app icon
    随手备个扯谈的 C 字 icon,睡觉
  • 2009-05-19 00:24:09

    5.18 share@mac

    是开个 mac 分类的时候了,收集一些相关的软件资源、八卦和使用技巧。公司内部最近 mb, mb pro 数量猛增,技术部应该已经接近八成了吧,放眼望去很是壮观:)

    db-mac 是公司内部 mac 爱好者的小群体(现在已经是大群体了),刚刚加入,没太多可贡献的,学习先,把这两天收集的一些技巧分享下:

    下面是洪教授提供的一个方便 Finder 和命令行切换的配置:

    在 ~/.bashrc 里加上

    # Create a command to 'cd' to frontmost Finder window
    # ref: http://www.macosxhints.com/article.php?story=20050924210643297
    cdh()
    {
         cd "`osascript -e 'tell application "Finder"' \
             -e 'set myname to POSIX path of (target of window 1 as alias)' \
             -e 'end tell' 2>/dev/null`"
    }

    这样,就可以在命令行里用 cdh 直接进入 Finder 里的当前目录了

    下面是 NP 找到的一个给文件夹设置独立中文名的方法:
    • mkdir Work
      touch ~/Work/.localized
      这个 .localized 文件保持为空就行,就是个标记
    • cd /System/Library/CoreServices/
      SystemFolderLocalizations /zh_CN.lproj(其他语言环境进其他语音包修改)
    • sudo vi SystemFolderLocalizations.strings
      添加行:“Work” = “工作”;
      ZZ保存退出
    • 重启 Finder 进程,本地化名称已经在 Finder 窗口里生效
    下面是 brant 分享的两个资源链接,我昨天在 ct 也分享了:
    再说个 mb 外接显示器并当主屏使用的方法,是 xyb 告诉俺地,其实很简单:把 mb 屏幕合上,接上显示器,再接上鼠标键盘就行了(注意,虽然合上屏幕会待机,但外接设备后又会激活,这时如果识别到外接显示器就会将其作为主屏);如果开着屏幕,就只能分屏或镜像。看来解决 mac 下的问题的方法就是往简单的方面想 3__3
  • 2009-04-22 07:57:45

    [存两篇] 学习+尝试



    Fluid Grids
    俺比较欠缺的部分,觉得算起来很麻烦,打算尝试下,先拿 ct 的博客皮肤练练手




    Advanced Debugging with JavaScript
    最近正好在用 Opera Dragonfly debuggerDrosera JS debugger

    ps, 昨天完成了投票部分,“豆列”应该归到五一后了,这两天可以提前开始整理老代码并制定新规范,比较杂乱,量也大,急不得
  • 2009-04-21 08:29:46

    入职第一天简单记录

    根据该用户的设定,您没有权限查看这篇日志
  • 根据该用户的设定,您没有权限查看这篇日志
  • 2009-03-14 07:54:21

    睡前脑子里的一些想法

    社区服务站开始提供早点,我是今天的第一位顾客,天蒙蒙亮,吃着久违的早餐,真好;这种感觉会让我想起高中,只是一种感觉,转瞬即逝。

    我厌恶睡觉,向来如此,曾幻想,如果人可以不睡觉的健康生活该是多么美好;当然现在现实很多,只是脑子里多了另一种假设:我宁愿按比例减损我的阳寿以换取持续清醒的精神与永不疲惫的肉体。

    其实并不清楚如果真的可以 7x24 地保持清醒我可以多做些什么,只是讨厌被规律地打断,也许这是一种对于时间管理不当所产生的错觉,我向来不善于管理时间,也一直都是时间的消费者;陈老师的日志提醒了我,管理时间,纸笔足矣,已经坚持了两天;细想起来很惭愧,身边很多朋友其实早已如此。

    我已经越发清晰地察觉到我所欠缺的那一部分东西是什么,同时我也明白,如果我还想继续做我自己,那就永远不能完满。

    刚满 24,或者说,都已经 24 了,我们平时可能过分地注重相对的概念而忽略了一些事物本来的绝对。我们应该懂得分享。

    现在很少去崇拜些什么了,我用我的谬论去解释这一切:I.Q.只是为满足我们的客观需求而存在,是对一种作用于我们从而反作用于这个世界的力的量化,一种很荒谬的存在,它既可以强大到无以复加,又可以瞬间归零,能量守恒对它可能并不适用。一些人领悟了其中的奥妙,他们从不去计较这荒谬,也不去理会诸如机遇这样的借口,自然而然地成为了施力者,也自然而然地脱颖而出。其实大多数人需要的只是时间,也许是几年,但也可能是一生,甚至几个轮回。

    我因该扮演好自己的角色,从始至终。
  • 2009-03-10 13:32:56

    关于淘宝的老认证

    刚打了 4 通电话就为把我一个 05 年的淘宝公司类型帐户的数字证书注销,暂时无果,已经提交到专家组,等待中。问题在于,目前注销公司类型帐户的数字证书需要上传营业执照等相关文件,而我当时注册的时候并不需要也没有(只是觉得可能功能多,注册个公司帐户玩)这些证明文件,而且顺利注册成功了。

    第一通:致电“淘宝”,听说是数字证书问题,让我直接联系支付宝;

    第二通:致电“支付宝”,说公司类型的老证书注销需要“淘宝”提供书面证明,证明我当时确实只用了身份证就注册了公司帐户;

    第三通:再次致电“淘宝”,听说是证书认证的问题说他们并不负责,一切认证是由“支付宝”负责核实的,无法提供证明,让我再次致电“支付宝”。感觉有踢皮球的嫌疑,但细想,凡是跟认证相关、和钱打交道的流程确实都是由阿里旗下技术含量颇高的“支付宝”完成的;

    第四通:再再次致电“淘宝”,我一口气说明了前三通电话的所有问题,“小二”(她们竟然也这么自称,挺2的)经过再次核实,确定暂时无法解决,要提交到专家组,让我留电话等待,最快当日解决。

    句号

    简单评价两句,淘宝确实有完善的客服系统,服务态度也很好(有电话录音监听嘛),对于这种历史遗留的问题只能提交技术部核实解决了。原来经常听寺院阿姨讲述客服的辛苦,所以俺今天统统给打了“非常满意”,善哉~

    21:43 更新
    “支付宝”客户满意中心来电话,依然无果 -__- 说情况比较特殊,明上午给答复

    3.10 13:40 更新
    “支付宝”客户满意中心再次电话,需要我提供身份证彩色复印件,即可注销数字证书
  • I. 什么是 prototype

    JS 中对象的 prototype 属性可以返回对象类型原型的引用(确实拗口),让我们分开来理解。对象的类(Class)和对象实例(Instance)之间是一种“创建”关系,所以类(Class)是对象的一个类型(Type)。在面向对象领域里,实例和类型不是唯一的一对可描述的抽象关系。在 JS 里还有另外一个更高层次的抽象关系:类型(Type)与原型(prototype),它恰好和类型与实例的抽象关系构成了一个三层的链。

    在生活中有个习语“照猫画虎”,这里的猫就是原型,而虎就是类型,用 JS 的 prototype 表示为:“虎.prototype = 某只猫”或“虎.prototype = new 猫()”。当然这只是个比喻。

    要注意的是,原型模式要求一个类型在一个时刻只能有一个原型,这里有两层含义:

    1. 每个具体的 JS 类型有且仅有一个原型(prototype),在默认情况下该原型是一个 Object 对象(不是 Object 类型);

    2. 这个类型的实例的所有类型,必须是满足原型关系的类型联。看个例子:

    function ClassA(){...}
    ClassA.prototype = new Object();  //默认值,可以省略

    function ClassB(){...}
    ClassB.prototype = new ClassA();

    function ClassC(){...}
    ClassC.prototype = new ClassB();

    var obj = new ClassC();

    alert(obj instanceof ClassC);  //ture;
    alert(obj instanceof ClassB);  //ture;
    alert(obj instanceof ClassA);  //ture;
    alert(obj instanceof Object);  //ture;

    简单描述一下原型关系的类型链:

    object <─ ClassA <- objectA <─ ClassB <- objectB <─ ClassC <- objectC

    有意思的是,JS 并没有规定一个类型的原型的类型,因此可以是任何类型,但通常是某种对象,这样,对象 - 类型 - 原型(对象)就可能构成一个环形结构,或其他有意思的拓扑结构。


    II. prototype 使用技巧

    JS 的对象是动态的,prototype 也不例外,给 prototype 增减属性会改变这个类型的原型,以及由这个原型所创建的对象上。

    function Point(x, y){
        if(x) this.x = x;
        if(y) this.y = y;
    }

    //设定 Point 对象的 x, y 默认值并动态的添加一个属性 z
    Point.prototype.x = 0;
    Point.prototype.y = 0;
    Point.prototype.z = 0;

    var p1 = new Point;
    var p2 = new Point(1, 2);

    alert(p1.x + ', ' + p1.y + ', ' + p1.z);
    //0, 0, 0   p1 为默认(0, 0)的对象,加上 z 的值也为 0,所以是 0, 0, 0

    alert(p2.x + ', ' + p2.y + ', ' + p2.z);
    //1, 2, 0   原型属性与对象属性重名,对象属性会覆盖原型属性,所以为1, 2

    如果我们用 delete 运算符删除 p2 的 x 属性,那么 p2.x 会恢复 prototype.x 的默认值 0:

    delete p2.x;
    alert(p2.x)  //0

    关于用 delete 操作还原默认值还有一个例子:

    function ClassA(){
        this.x = 10;
        this.y = 20;
        this.z = 30;
    }

    ClassA.prototype = new ClassA;  //将 x, y, z 同时设为 ClassA 的默认值

    //下面这个方法会将自身的非原型属性删除,达到 reset 的效果
    ClassA.prototype.reset = function(){
        for(var each in this){
            delete this[each];
        }
    }

    var a = new ClassA();

    a.x *= 2;
    a.y *= 2;
    a.x *= 2;

    alert(a.x + ', ' + a.y + ', ' + a.z)  //20, 40, 60

    //调用 reset 方法回复对象的默认值
    a.reset();

    alert(a.x + ', ' + a.y + ', ' + a.z)  //10, 20, 30

    我们还可以利用 prototype 为对象属性设置一个可读的 getter,如果忘记了 getter,可以再回顾下 笔记(1)。实际上,将一个对象设置为一个类型的原型,相当于通过实例化这个类型,为对象创建只读副本:


    //定义一个多边形类型
    function Polygon(){

        //存放多个多边形的定点
        var m_points = [];
        m_points = Array.apply(m_points, arguments);
       
        //利用上面提到的方法为第一个顶点创建只读副本
        function GETTER(){};
        GETTER.prototype = m_points[0];
        this.firstPoint = new GETTER();
       
        //公有属性
        this.length = {
            valueOf : function(){return m_points.length},
            toString : function(){return m_points.length}
        }

        //添加一个或多个顶点   
        this.add = function(){
            m_points.push.apply(m_points, arguments);
        }
       
        //取得序号为 idx 的顶点
        this.getPoint = function(idx){
            return m_points[idx];
        }
       
        //设置一个特定位置的顶点
        this.setPoint = function(idx, point){
            if(m_points[idx] == null){
                m_points[idx] == point;
            }
            else{
                m_points[idx].x = point.x;
                m_points[idx].y = point.y;
            }
        }
    }

    //构建一个三角形 p
    var p = new Polygon{(x:0, y:1), (x:3, y:1), (x:0, y:4))}

    p.firstPoint.x = 100;  //假如我们为第一个定点重新设定 x 值
    alert(p.getPoint(0).x)  // 0,私有成员的值并未受到影响
    delete p.firstPoint.x  //恢复默认值
    alert(p.firstPoint.x);  //0

    p.setPoint(0, {x:3, y:4});  //通过 setter 改写了私有成员
    alert(p.firstPoint.x);  //3,getter 的值发生了改变

    上面的例子还说明了,用 prototype 可以快速创建对象的一个或多个副本,以一个对象为原型来创建大量的新对象,这正是 prototype pattern 的本质:

    var p1 = new Point(1, 2);
    var points = [];
    var PointPrototype = function(){};
    PointPrototype.prototype = p1;
    for(var i = 0, i < 10000, i++){
        points[i] = new PointPrototype();  //由于 PointPrototype 是个空函数,所以它的构造要比直接构造 //p1 快得多
    }

    除了以上作用,prototype 更常见的用处是声明对象的方法,这样避免了在构造函数中每次对方法进行重新赋值,节省了时间和空间。所以应尽量采用 prototype 定义对象方法,除非该方法要访问对象的私有成员或者返回某些引用了构造函数上下文的闭包。

    习惯上,我们把采用 prototype 定义的属性和方法称为静态属性和静态方法,或者原型属性原型方法,把用 this 定义的属性和方法称为公有属性和公有方法。


    III. prototype 的实质、价值和局限性

    上面已经说了 prototype 的作用,现在来透过规律揭示 prototype 的实质。prototype 的行为类似于 C++ 中的静态域,将一个属性添加为 prototype 的属性,这个属性将被该类型所创建的所有实例所共享,但这种共享是只读的。在任何一个实例中只能够用自己的同名属性覆盖这个属性,而不能够改变它。看个例子:

    function Point2D(x, y){
        this.x = x;
        this.y = y;
    }

    Point2D.prototype.x = 0;
    Point2D.prototype.y = 0;

    function ColorPoint2D(x, y, c){
        this.x = x;
        this.y = y;
    }

    ColorPoint2D.prototype = new Point2D();
    ColorPoint2D.prototype.x = 1;
    ColorPoint2D.prototype.y = 1;

    var cp = new ColorPoint2D(10, 20, red);
    alert(cp.x)  //10,先查找 cp 自身属性
    delete cp.x;
    alert(cp.x)  //1,被删除后查找上层原型属性
    delete ColorPoint2D.prototype.x;
    alert(cp.x)  //0,删除后继续查找更上层原型链上的属性

    以一个对象为实例,安全地创建大量的实例,这就是 prototype 的真正含义,也是它的价值所在。

    但由于 prototype 仅仅是以对象为原型给类型构建副本,因此也具有很大局限性,比如改变某个原型上引用类型的属性的属性值,将会彻底影响到这个类型创建的每一个实例。

    总之,prototype 是一种面向对象的机制,它通过原型来管理类型与对象之间的关系,prototype 的特点是能够以某个类型为原型构造大量的对象。
  • 跳过 JS 核心(语言结构、数据类型、函数、对象、集合、字符串等)以及 BOM、DOM 部分,这个系列的笔记主要总结一下《王者归来》面向对象编程部分的知识点,以便梳理和查阅。

    JavaScript 究竟是不是一种面向对象的语言呢?

    “面向对象不是只有类模型一种,prototype-based(基于原型)是 class-based(基于类)的简化版,是一种 class-less 的面向对象。对应的,prototype 继承是 class 继承的简化版(例如省略了多重继承、基类构造函数、忽略了引用属性的继承等),但不能因为它不支持这些特性就不承认它是一种完整的继承。是否为继承添加额外的特性,开发者可以自由选择,但在不需要这些额外特性的时候,还是有理由尽量用 prototype-based 继承。

    总而言之,prototype-based 认为语言本身可能不需要过分多的 reuse 能力,它牺牲了一些特性来保持语言的简洁,这没有错,prototype-based 虽然比 class-based 简单,但它依然是真正意义上的 object-oriented。”


    I. 公有和私有 ── 属性的封装

    JS中,函数是绝对的“第一型”,JS 的对象和闭包都是通过函数实现的。利用闭包的概念,JS 中可以有不逊色于其他各种面向对象语言的公有和私有特性:

    function List(){
        var m_elements = [];   //私有成员,对象外无法访问
        m_elements = Array.apply(m_elements, arguments);

        //公有属性,可以通过“.”运算符或下标访问
        this.length = {
            valueOf : function(){return m_elements.length;},
           toString : function(){return m_elements.length;}
        }

        this.toString = function(){
            return m_elements.toString();
        }

        this.add = function(){
            m_elements.push.apply(m_elements, arguments);
        }
    }

    这个例子中 this.length, this.toString, this.add 是公有成员,其中 this.length 是私有成员 m_elements 的 length 属性的 getter,外部我们可以通过“.”运算符对这些属性进行访问。

    对象的 getter 是一种特殊的属性,它形式上像是变量或者对象属性,但它的值随着传入参数的改变而改变。在不支持 getter 的语言中,我们通常用 get<Name> 方法来替代,其中 <Name> 是 getter 的实际名字,其效果与 getter 等价。ECMAScript v3 不支持 getter,但可以用上面这种构造带有 valueOf 和 toString 方法的对象来模拟 getter。

    对象的 setter 是另一个相对应的属性,它的作用是通过类似赋值的方式改变对象的某些参数或者状态,遗憾的是,ECMAScript v3 不支持 setter,并且目前为止也没什么好的方法可以在 JS 中模拟 setter。要实现,只有通过定义 set<Name> 方法来实现:(


    II. 属性和方法的类型

    在 JS 里,对象的属性和方法支持 4 种不同的类型,下面通过一个例子来说明:

    function myClass(){
        var p = 100;  //private property; 1. 私有属性
        this.x = 10;  //dynamic public property 2. 动态公有属性
    }

    myClass.prototype.y = 20;  //static public property or prototype property 3. 静态公有属性或称原型属性

    myClass.z = 30;  //static property 4. 静态属性或称类属性

    下面说下他们的特点和区别:

    1. 私有属性上面已经提到,它的特点是对外界不开放,只能通过特定的 getter 和 setter 访问。实例化 myClass() 后,如果通过“.”运算符直接访问 p 会得到 undefined;

    2. 动态公有属性的特点是外界可以访问,而且每一个对象实例持有一个副本,他们之间不会相互影响;

    3. 原型属性的特点是每个对象实例共享唯一副本,对它的改写会相互影响;

    4. 类属性的特点是作为类型的属性而不是对象实例的属性,也就是说不能通过对象实例的“.”运算符访问,那样会得到 undefined。上例中通过 myClass.z 直接访问即可。

    有关 prototype 的知识点要放到下几篇,毕竟是 JS 面向对象编程的重点所在,内容比较多。简单预告一下第 2 篇笔记,包括 prototype 的使用技巧、实质及其范例。之后几篇会涉及继承和多态、构造和析构、包装对象、元类,类模板等。

    本篇结尾以一个例子简述一下 prototype,“prototype 是在 IE 4 及其以后版本引入的一个针对于某一类的对象的方法,而且特殊点在于:它是一个给类的对象添加方法的方法”:

    为本地对象 Number 添加数字阶乘方法:Number.fact()

    实现:

    Number.prototype.fact = function(){
        var num = Math.floor(this);
        if(num<0) return NaN;
        else if(num==0 || num==1) return 1;
        else return (num*(num-1).fact());
    }

    alert((10).fact());  //3628800