-
2009-10-04 20:52:25
2009.10.4
-
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 尺寸更小。”)
继续瞎说交互
这方面我一直都很外行,没数据的情况下基本是靠主观理解 *…*
先看上方的黄色提示,咪咪会识别新信息,但不会强行显示出来,这样的设计很适合中文用户,哈哈,在刚注册时 popular 满屏春色,而且源源不绝,现在虽然少了,但偶尔也能惊喜一下,如果这时被自动推出来,身后有人观摩就太尴尬了 - -!
接着看评论输入框,这是在 repost 后显示的,提示和样式都清晰明了;
最后两条灰色信息是我又爱又恨的地方,爱在于简明地显示了信息来源(你知道我在说哪里),恨在于字太小,如果有图和视频就更显得隐蔽,打开页面一眼望去不好区分哪些信息是首发哪些是 repost(当然现在我知道了按长度区分)。或许雅虎就是有意而为之,认为让用户第一时间获取信息本身才是最好的体验?纯属瞎猜...
说件土事儿,第一次来 meme 时俺竟然没找到在哪里输入信息,虽然 dashboard 页上方深灰色按钮赫然,但一时没意识到那里就是可以展开的输入区域,还一个劲儿的找大文本框。我想这个应该不属于交互设计的问题,是被 twitter 毒害太深,习惯成自然,meme 这个大框在之后用起来就很顺手了。
下面说说前端代码
meme 的前端代码很随性。先看 GBS 最近一次更新:
虽然 IE6~8 在 XP 下依然被列为 A-grade,但 meme 似乎已经对 IE6 失去了耐心
IE6(左)高度有偏差,FF3.5(右)为正常
左侧的黄色区域是一个本不该显示的空层(divMessage),用来防止相应的动态信息,但里面有个 ,所以对于 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 下的表现也不尽如人意:
FF3.5(上)为正常效果,Opera10(下)有明显错位(Camino 有同样问题)
代码中可以看到,头像为左浮动,用户名(<h1>)和简介(<p>)均为块级显示,而在头像与用户名、简介的水平方向均有相邻的 margin。在其他浏览器中如果是这种情况,用户名与简介的 margin-left 就会从外层容器(.info)的左边缘开始算起(不含 margin 与 padding 的宽度),但 Opera 与 Camino 会与头像的 margin-right 相叠加,所以就出现了上图的错位。解决方法很简单,去掉用户名与简介的 margin-left,只使用头像的 margin-right 来区隔即可。
上图(右侧)是在 Camino 下一处比较诡异的地方,灰框本应是 repost 的按钮(如左图),不知啥原因显示成这样。这个按钮是一个用背景图来控制样式的 input,不知是否和背景图使用了 transparent 有关。
接下来说圆角,上面已经提到,meme 使用了私有属性实现(-moz-border-radius 和 -webkit-border-radius),没在这方面多较真,很好奇他们的设计稿是啥样的;IE 也就罢了,莫非连 Opera 用户都放弃了?圆角在 firefox 3.0+, safari, chrome 下看都很完美(最赞是 firefox,在 webkit 核心浏览器下,圆角部分比较虚,而同为 gecko 核心的 Camino 表现最差,见下图)
仔细看头像左侧的圆角边框,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 页对于图片展示的处理比较有意思,先看图:
默认情况下,信息列表中只显示裁切后的缩略图,当图片的实际尺寸大于缩略图时,点击图片便会出现类似视频加载的样式(左、中),载入完毕,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 的设计,收工
-
2009-09-05 04:08:40
Mac 下为 VIM 增加 JSLint 支持
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 时会出现检查结果:
一切顺利,QuickFix List 中显示的就是对上方 JS 的检查结果,如果你想增设偏好,比如使用自己的快捷键启动调试,那么可以参照 JSLint 的脚本自行添加设置:
当然,浏览器中的 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 自家娃,从风格到功能就都没问题了。
试听地址(三首歌):http://resource.ctba.cn/mockee/radio (只限 safari 4.01+)
在 这里 下载最新版 Safari
特别注意下,iPhone OS 2.0 的 Safari 版本是 3,无法播放歌曲,要升级到 3.0,3.0 的 Safari 版本才是 4,也才能顺利识别 <audio> 标签。
目前 firefox 3.5 虽然 支持 <audio> 标签 但只能播 ogg 格式的歌曲,上面试听的是 mp3,所以也无法播放,不过风格上 css3 支持的没问题。
box-shadow, border-radius, font-face 将原先只能图片完成的效果代码化。时间比较仓促没有用 canvas。下周试试连主曲库,内部再小测下。
《变形金刚2》还没看,主要公司发的票找不到了,另外一张奖品票也还没领,不知到没到期 @__@ 各方面了解了下,还是最想看 3D 的《冰河世纪3》。另外,Eason 的演唱会得去啊~~~
-
2009-05-30 02:32:46
最近的一些设计

在扯谈的简洁版首页上耗了太多时间,直到陷入像素魔咒,恶心的想吐。新首页的设计已经基本完成,讨论修改后会尽快上线,现在暂时保密。上面是新 banner 的部分截图。久违了的“扯谈社”~
打算为 iPhone, iPod Touch 单独做个入口,浏览起来顺手些。iPhone Simulator Platform 效果很棒,做测试再合适不过,没玩过的可以尝试下。
随手备个扯谈的 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 - mkdir Work
-
2009-04-22 07:57:45
[存两篇] 学习+尝试

Fluid Grids
俺比较欠缺的部分,觉得算起来很麻烦,打算尝试下,先拿 ct 的博客皮肤练练手
Advanced Debugging with JavaScript
最近正好在用 Opera Dragonfly debugger 和 Drosera JS debugger
ps, 昨天完成了投票部分,“豆列”应该归到五一后了,这两天可以提前开始整理老代码并制定新规范,比较杂乱,量也大,急不得 -
2009-04-21 08:29:46
入职第一天简单记录
根据该用户的设定,您没有权限查看这篇日志 -
2009-03-28 02:34:39
经历嗷嗷仰慕的盖世作坊初面
根据该用户的设定,您没有权限查看这篇日志 -
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 更新
“支付宝”客户满意中心再次电话,需要我提供身份证彩色复印件,即可注销数字证书
-
2009-03-09 21:58:22
《王者归来》读书笔记 ── JavaScript 面向对象编程(2)
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 的特点是能够以某个类型为原型构造大量的对象。
-
2009-03-08 19:44:55
《王者归来》读书笔记 ── JavaScript 面向对象编程(1)
跳过 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
-
2009-01-31 02:27:00
[无责任翻译] W3C 文档 ─ Box model: Collapsing margins
关于 margin 折叠的问题一直很困扰(尤其是 ff 下的 margin-top 失效),一般都是 hack 过去的,没有细究,最近为面试在整理知识点,顺便翻文档,把一些犄角旮旯的残留问题来次刨根问底。英语水平实在一般,翻译不周的地方希望各位高人指正,感激不尽 ~__~ 废话不多说,开始俺的第一次:
8.3.1 Collapsing margins
有关盒模型的全部讲解在这里:http://www.w3.org/TR/CSS21/box.html
In this specification, the expression collapsing margins means that adjoining margins (no non-empty content, padding or border areas or clearance separate them) of two or more boxes (which may be next to one another or nested) combine to form a single margin.
在这个说明中,“collapsing margins”所表达的意思是:两个或以上盒模型之间(关系可以是相邻或嵌套)相邻的 margins(不含非空内容、 padding、边框区域或使用清除分离)结合表示为一个单独的 margin。
In CSS 2.1, horizontal margins never collapse.
在 CSS2.I 规范中,横向的 margin 永远不会被折叠。
Vertical margins may collapse between certain boxes:
垂直的 margin 可能在一些盒模型中被折叠:
Two or more adjoining vertical margins of block boxes in the normal flow collapse. The resulting margin width is the maximum of the adjoining margin widths. In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum of the positive adjoining margins. If there are no positive margins, the absolute maximum of the negative adjoining margins is deducted from zero. Note. Adjoining boxes may be generated by elements that are not related as siblings or ancestors.
两个或以上的块级盒模型相邻的垂直 margin 在常规文档流中会被折叠。最终的 margin 宽度是相邻 margin 的最大值。在包含负 margin 的情况下,用相邻 margin 的正值减去最小负 margin 值的绝对值。如果没有正 margin 值,则用 0 减去最小负 margin 值的绝对值。注意:相邻的盒模型可能由元素产生而并没有相邻或继承的关系。
Vertical margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
在一个浮动盒模型与另外任意一个盒模型之间的垂直 margin 不会被折叠(甚至一个浮动的盒模型与它的子元素间也是一样)。
Vertical margins of elements with 'overflow' other than 'visible' do not collapse with their in-flow children.
除取 visible 值以外,设置了 overflow 属性的元素与它子元素间的 margin 不会被折叠。
Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
使用绝对定位的盒模型的垂直 margin 不会被折叠(甚至与他们的子元素间也是一样)。
Margins of inline-block elements do not collapse (not even with their in-flow children).
设置了 inline-block 元素的垂直 margin 不会被折叠(甚至与他们的子元素间也是一样)。
If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.
如果一个盒模型的上下 margin 相邻,这时它的 margin 可能折叠。在这种情况下,元素的位置取决于它相邻元素的 margin 是否被折叠。
o If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
o 如果元素的 margin 与它父元素的 margin-top 折叠,盒模型的顶部边框的边界定义与它的父元素相同。
o Otherwise, either the element's parent is not taking part in the margin collapsing, or only the parent's bottom margin is involved. The position of the element's top border edge is the same as it would have been if the element had a non-zero top border.
o 另外,任何元素的父元素不参与 margin 折叠,或者说只有父元素的 margin-bottom 参与计算。如果元素的顶部边框非零,那么元素的顶部边框的边界位置和原来一样。
An element that has had clearance applied to it never collapses its top margin with its parent block's bottom margin.
一个使用了清除的元素的 margin-top 绝不会和它的块级父元素的 margin-bottom 折叠。
Note that the positions of elements that have been collapsed through have no effect on the positions of the other elements with whose margins they are being collapsed; the top border edge position is only required for laying out descendants of these elements.
注意,已经被折叠的元素的位置对其他已经被折叠的元素的位置没有任何影响;只有在对这些元素的子孙布局时,才需要定义顶部边框的边界位置。
Margins of the root element's box do not collapse.
根元素的 margin 不会被折叠。
The bottom margin of an in-flow block-level element is always adjoining to the top margin of its next in-flow block-level sibling, unless that sibling has clearance.
浮动的块级元素的 margin-bottom 总是与它后面的同级浮动块级元素的 margin-top 相邻,除非那个同级元素已经使用了清除。
The top margin of an in-flow block-level element is adjoining to its first in-flow block-level child's top margin if the element has no top border, no top padding, and the child has no clearance.
如果一个浮动的块级元素没有 border-top,没有 padding-top,且子元素没有使用清除,那么它的 margin-top 与它的第一个浮动块级子元素的 margin-top 相邻。
The bottom margin of an in-flow block-level element with a 'height' of 'auto' and 'min-height' less than the element's used height and 'max-height' greater than the element's used height is adjoining to its last in-flow block-level child's bottom margin if the element has no bottom padding or border.
如果一个浮动的块级元素的 margin-bottom 指定了 height: auto,min-height 小于它的实际使用高度,max-height 大于它的实际使用高度,那么当这个元素没有指定 padding-bottom 或 border 时,它和它的最后一个浮动块级子元素的 margin-bottom 相邻。
An element's own margins are adjoining if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) are adjoining.
如果一个元素的 min-height 属性设置为 0,那么它所拥有的 margin 是相邻的,而且它既没有 border-top 与 border- bottom,也没有 padding-top 与 padding-bottom,它的 height 也可以是 0 或 auto,它不能包含一个 line 的盒模型,它所有浮动子元素(如果有的话)的 margin 也都是相邻的。
When an element's own margins collapse, and that element has had clearance applied to it, its top margin collapses with the adjoining margins of subsequent siblings but that resulting margin does not collapse with the bottom margin of the parent block.
当一个元素拥有了 margin 折叠,并且它使用了清除,那么它的 margin-top 会和紧随其后的同级元素的相邻 margin 折叠,但结果是它的 margin 将无法与其块级父元素的 margin-bottom 折叠。
Collapsing is based on the used value of 'padding', 'margin', and 'border' (i.e., after resolving any percentages). The collapsed margin is calculated over the used value of the various margins.
折叠操作是以 padding、margin、border 的值为基础的(即,在解析了任意百分比之后)。折叠后的 margin 值将覆盖已使用的不同 margin 的值并以此计算。
一个简单例子在这里:http://www.w3.org/TR/CSS21/box.html#mpb-examples。有图示(上图),有兴趣的可以看看,就不翻了 *___*
-
2009-01-25 03:51:07
牛年快乐!
被首页推荐的第三篇(前两篇在这里)日志,开心,继续努力
今天是 yangcai168 的 deadline,我尽力吧,本命年三十通宵干活儿不大好吧 @___@ 还好,过了初一我就可以踏实宅了,时间快转!
大家新年快乐!身体健康!其他自己掂量着办,祝福多了好运就分散了,所以只祝大家 2009 身体健康,革命的本钱、家人的健康最重要~~

