理解技术债【编译】

Understanding technical debt

原文:http://www.nczonline.net/blog/2012/02/22/understanding-technical-debt
翻译:http://www.haipi8.com/news/382

开发一个网站,会有好几个团队参加,产品经理或者市场代表负责功能和设计,用户界面设计师负责视觉效果,最后开发人员负责落实到最终产品。这些团队中,开发最容易被问到为什么这个不能做,为什么这个不应该做,会被问的有些恼火。出现这种情况是因为自觉或者不自觉的,开发人员要处理项目的技术债。

技术债,简单点说就是项目中待完成的技术工作。这种事情很难量化,通常只是在特定的时间里存在一个或者几个开发人员的脑子里的东西。一开始会跟经理估计要花几个小时或者几天还技术债,之后,每一个技术决策都伴随着某一种技术债。

截止时间很少跟开发时间对得上,所以开发一路伴随着技术上的折中妥协,先用短期的解决方案跑起来,以后再回头重新写个长久的方案,不了解的人会觉得这样做项目有些草率,但是只有这样才能按期发布,缺点是会积累技术债。

技术债不存在项目规划中,所以开发人员都把这些工作附加在其他功能开发上。极少的组织中,有头脑的经理会为技术债买单,但这不是惯例。那些做决定的人通常不会给时间还技术债,在他们脑子里,花时间却没有完成任何功能,为什么要给你时间做忙完了却没有增加新功能的事呢?

最坏的时候,累积的技术债可能会导致一个Web项目失败。典型的技术债就是可扩展性,一开始的目标是建立一个Web应用并运行起来。但建立一个100用户的程序和建立一个1,000,000人用的程序是不同的。为增加可扩展性,开发人员的脑子里会列出一大串需要去做的事,减少访问数据库的次数,多层缓存,减少请求大小,更快的处理等等。

跟金钱债务一样,解决技术债的最好方法就是在变得太大承受不住之前解决掉。定期清理技术债就好像代码卫生(code hygiene)。 如果你十年都没去看过牙医,没准你会因为平时不注意口腔卫生得到一个大惊喜。代码也是一样的,驾驭你的技术债意味着有时间就定期深入解决问题。良好的代码卫生需要你定期:

  • 删除无用代码 – 我常说, 删掉多行代码的一天是美好的一天, 是对得起你薪水的一天。 无用的代码不应该存在代码库,因为这东西就像细菌, 一遇到机会就会造成很大的问题。 如果觉得有些代码已经没用了, 验证一下删掉它们。 要立即处理,不要想着以后处理,那会造成更多的技术债。 更少的代码意味着更少的出错机会和更快的构建,部署。
  • 重构代码 – 重构的时机就是你意识到有问题的时候。 不要让邋遢的糟糕代码继续出现在代码库。 代码会有自己的方式自我复制。 比如新开发人员来找一个例子, 找到了糟糕代码, 拷走, 现在就有两份糟糕代码了, 造成更多的技术债。 提交之前确保自己的代码可以被复制到别的地方而不会造成未来的技术债。 在已有的代码基础上工作时, 评估期间就应该考虑到重构的时间。
  • 重写代码 – 很少有人用重写的方式修改代码, 但这是代码卫生的必要组成部分。 好的代码用该能运行5年而不用重写【注】; 糟糕的代码发现的时候就应该尽快重写, 可以先尝试重构, 重构不行就重写, 在商业环境中重写代码很困难,不过你可以把重写绑到新功能开发上, 这样就更有机会得到需要的时间。

技术债是每个项目的一部分, 需要有关各方参与其中, 每一个版本发布都考虑到代码卫生是个不错的方法, 可以用多种形式: 代码大扫除(bug bashes), 每个人抽出一两天只处理bug; 工程师驱动(engineering-driven),而不是由终端用户决定做那些功能;代码审查(code reviews),就是工程师相互检查对方的代码, 都是保证技术债不失控比较好的实践。

声明:任何在这篇文章中的观点和意见都是作者自己的。不要以任何方式联想到作者的雇主,同事,或其他。

注:五年是根据自己的经验:一般来说人们倾向于约五年从头重写他们的软件。 通常开发需要18个月(不是部署,而是做成你想要的样子)。 接下来18个月应该很容易维护, 之后就会有人开始想从头做了。 但是知直到过一两年,软件的地基上也开始出现裂缝的时候才会做出商业上的决定。 在第四年或者第五年的关头上, 一般新工程师已经不熟悉原有的架构了, 也不知道该怎样修改新出现的问题的时候。 新的解决方案就是: 重写它!

Read More

几个使用jQuery的图片预加载函数

最近项目中用到的一个功能,用户进入网站时显示loading页面,直到主页的几个大图片加载完成才渐隐进入主页。自己写了个插件,看起来结构挺糟糕的,不好意思放到项目里。在网上搜现成的,还挺多。不过得用英文关键词,搜中文的真是垃圾网站一大堆。废话完毕下面开始

第一个

(function($) {
  var cache = [];
  // Arguments are image paths relative to the current page.
  $.preLoadImages = function() {
    var args_len = arguments.length;
    for (var i = args_len; i--;) {
      var cacheImage = document.createElement('img');
      cacheImage.src = arguments[i];
      cache.push(cacheImage);
    }
  }
})(jQuery)

使用方法

jQuery.preLoadImages("image1.gif", "/path/to/image2.png");

这个来自这里:Engineered Web。可以看到其实这个是用原生JavaScript实现,使用jQuery是为了调用方便。
优点:原生JavaScript,速度快。
缺点:不支持回调函数。

第二个

// Helper function, used below.
// Usage: ['img1.jpg','img2.jpg'].remove('img1.jpg');
Array.prototype.remove = function(element) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] == element) { this.splice(i,1); }
  }
};

// Usage: $(['img1.jpg','img2.jpg']).preloadImages(function(){ ... });
// Callback function gets called after all images are preloaded
$.fn.preloadImages = function(callback) {
  checklist = this.toArray();
  this.each(function() {
    $('<img>').attr({ src: this }).load(function() {
      checklist.remove($(this).attr('src'));
      if (checklist.length == 0) { callback(); }
    });
  });
};

使用方法

$.post('/submit_stuff', { id: 123 }, function(response) {
  $([response.imgsrc1, response.imgsrc2]).preloadImages(function(){
    // Update page with response data
  });
});

第二个函数来自 stackoverflow
这个支持了回调函数,全部图片都加载完成后执行,我的项目中就是用的这个。
作者为了方便扩展了Array对象,使用时注意不要影响到其他的代码。
搜索的时候还发现一个更好用的:

第三个

(function($) {
	var imgList = [];
	$.extend({
		preload: function(imgArr, option) {
			var setting = $.extend({
				init: function(loaded, total) {},
				loaded: function(img, loaded, total) {},
				loaded_all: function(loaded, total) {}
			}, option);
			var total = imgArr.length;
			var loaded = 0;

			setting.init(0, total);
			for(var i in imgArr) {
				imgList.push($("<img />")
					.attr("src", imgArr[i])
					.load(function() {
						loaded++;
						setting.loaded(this, loaded, total);
						if(loaded == total) {
							setting.loaded_all(loaded, total);
						}
					})
				);
			}

		}
	});
})(jQuery);

调用方法:

$(function() {
    $.preload([
        "http://farm3.static.flickr.com/2661/3792282714_90584b41d5_b.jpg",
        "http://farm2.static.flickr.com/1266/1402810863_d41f360b2e_o.jpg"
    ], {
        init: function(loaded, total) {
            $("#indicator").html("Loaded: "+loaded+"/"+total);
        },
        loaded: function(img, loaded, total) {
            $("#indicator").html("Loaded: "+loaded+"/"+total);
            $("#full-screen").append(img);
        },
        loaded_all: function(loaded, total) {
            $("#indicator").html("Loaded: "+loaded+"/"+total+". Done!");
        }
    });
});

这个来自这里 http://ditio.net,貌似是作者的一个插件中的一段代码。
支持三个回调函数:加载前、单个图片加载完成、全部图片加载完成。原作者还给了个演示网页,看演示网页的源代码就明白了。

Read More

令人惊叹的伪元素效果收集

原文: A Whole Bounch of Amazing Stuff Pseudo Elements Can Do
翻译:http:haipi8.com/347
伪元素 :before 和 :after 能做出相当令人惊叹的效果。这两个伪元素可以给HTML中的任何元素增加两个可以自由操控的元素。这给了前端设计打开了无限的可能,而且不用添加影响语义的标签,以下是收集的令人惊奇的效果。

多重背景的画布

multiplecanvases

因为可以让伪元素绝对定位,你可以想象成给每个元素两个附加的层,Nicolas Gallagher 展示了很多这样的程序,包括多重边框,模拟CSS3多重背景以及等高列。

用一个元素做出更多的形状

shapes

上面的形状和其它很多的形状都可以用一个元素做出来,让它们更加实用了。相对于那些用1700个div做出的“纯CSS的咖啡杯”的巧妙设计,更实用
这里是一个六角星的例子:

#star-six {
         width: 0;
         height: 0;
         border-left: 50px solid transparent;
         border-right: 50px solid transparent;
         border-bottom: 100px solid red;
         position: relative;
 }
 #star-six:after {
         width: 0;
         height: 0;
         border-left: 50px solid transparent;
         border-right: 50px solid transparent;
         border-top: 100px solid red;
         position: absolute;
         content: "";
         top: 30px;
         left: -50px;
 }

【译注:http://jsbin.com/ibobex

打印样式中显示URL的链接地址

printpreview

@media print {
   a[href]:after {
     content: " (" attr(href) ") ";
   }
 }

【译注:http://jsbin.com/adesat

清除浮动

floatclear

不用添加多余的标签清除container的浮动,伪元素就可以做到。著名的“clearfix”就是这样做的。只要给标签加上更语意化的“group”类名就可以了。

.group:before,
 .group:after {
     content:"";
     display:table;
 }
 .group:after {
     clear:both;
 }
 .group {
     zoom:1; /* For IE 6/7 (trigger hasLayout) */
 }

模拟 float:center

float middle

有没有人用先不讨论,float属性真的没有center的值。浮动有left和right值,我们可以用伪元素占位划分出两列中间的空位,然后在那里放一张图片,可以模拟出这个效果

标出代码块所用的语言

codeblock

这个网站的代码块都用伪元素标出了语言

<pre rel="CSS"></pre>
pre:after {
   content: attr(rel);
   position: absolute;
   top: 22px;
   right: 12px;
 }

创造出一整套图标

icons

Nicolas Gallagher把创造形状的的想法提升到另一个层次,没有用图片,只用(大多数都是)两个元素加上伪元素做出一套复杂的图标

更有效地使用可用空间

media queries and pseudos

CSS给你的,CSS也能拿走(原文:What CSS giveth, CSS can taketh away.)。我的意思是通过media queries伪元素可以显示或者移除伪元素。也许你想在空间有限时只展示图标,空间充裕时显示描述更清晰的文字。

让排版更华丽

typographic flourish header

如果伪元素的内容是文字,它们将继承父元素的字体设置。如果你给它们设置了内容,就可以给他们设置样式,比如,不同的字体,不同的颜色,让你的标题更华丽。

h2 {
      text-align: center;
 }
 h2:before, h2:after {
     font-family: "Some cool font with glyphs", serif;
     content: "\00d7";  /* Some fancy character */
     color: #c83f3f;
 }
 h2:before {
     margin-right: 10px;
 }
 h2:after {
     margin-left: 10px;
 }

制作body border效果

body border

用普通的border left 和 border right, 然后给伪元素fixed定位在顶部和底部,可以做出“body border”效果,不实用任何专门的元素。

body:before, body:after {
     content: "";
     position: fixed;
     background: #900;
     left: 0;
     right: 0;
     height: 10px;
 }
 body:before {
     top: 0;
 }
 body:after {
     bottom: 0;
 }
 body {
     border-left: 10px solid #900;
     border-right: 10px solid #900;
 }

做一个闪闪发光的按钮

gleaming button

给伪元素设置 transparent white 或者 transparent gradient ,定位到按钮外面(用overflow:hidden;隐藏),鼠标经过时设置transition,就能让按钮在鼠标经过时有一道光闪过的效果。目前只能用Firefox 4或者5看,目前别的浏览器都不支持伪元素的transitions。
原作 Anton Trollbäck;Pseudoelementized 作者是 Nicolas Gallagher; 另外一个版本由我制作。

在链接上划过时,页面淡出效果

page fadeout

如果没有给元素设置相对定位,绝对定位的伪元素会相对于设置了相对定位的祖先元素定位。如果没有祖先元素设置相对定位,就会相对于跟元素定位。这样就能设置一个full-browser-window元素,把它保存到元素后面,鼠标经过时显示,就能做出“page fade out”效果了。

给有序列表的数字设置样式

ordered list

想过给有序列表的数字设置样式吗?最后你肯定是用不够优雅的方法,比如用span包裹内容,设置li的样式,然后移除span的样式。或者更疯狂的用背景图片,或者干脆自己写上数字。这些方法都不够酷,用伪元素做计数器这样就好多了。

Make data tables responsive

mobile table

大表格在小屏幕中看起来很糟糕,不是缩放,就是出现横向和纵向的滚动条,缩小后太小看不清。我们可以一起使用CSS media queries 和伪元素 Make data tables responsive 为小屏幕设置更有可读性的表格。

制作漂亮的tooltip

tooltip

用HTML5 data attribute, 然后把属性的值放到伪元素中并设置样式,就能用CSS做出个性化的tooltip.【译注:原文回复中的另外一个演示

Read More

学习使用css伪元素 :after, :before

本文来源:http://coding.smashingmagazine.com/2011/07/13/learning-to-use-the-before-and-after-pseudo-elements-in-css/

中文翻译:http://www.haipi8.com/css/331

如果你经常关注各种网页设计博客,你可能已经注意到了,:before 和 :after 伪元素已经在前端开发中得到相当多的关注,我们有很好的理由这样做,特别是伦敦的这位博客作者Nicolas Gallagher做的实验,让伪元素赢得不少曝光。

Pseudo-element-icons in Learning To Use The :before And :after Pseudo-Elements In CSS

Nicolas Gallagher 用伪元素把语意化的HTML创作出84个图标.

为了配合这次曝光(并从这种不断增长的趋势中获益),我把我认为已经相当全面的伪元素的东西拼到了一起。本文的目标是看过用伪元素做出的很酷的事情,但是在自己尝试之前,想知道这是什么CSS技术的人。

虽然CSS规范中包含了其它伪元素,但本文只关注 :before, :after。所以,方便起见,这里的“伪元素”特指这两个伪元素。

什么是伪元素?

伪元素,就像它的名字一样,创建一个假的元素,插入到特定元素之前或之后。

伪元素的伪“pseudo”是一个希腊词的音译,基本意思是“说谎,骗人的,假的”。因此这个名称很恰当,因为它们实际上不改变文档。而是插入一个幽灵般的元素,用户可以看见,也可以应用CSS样式。

基本语法

:before 和 :after 伪元素使用简单(不像其他CSS属性一样需要一大堆供应商前缀【译注:如-moz-,-webkit-等】)。下面是一个简单的例子

#example:before{
​ content: "#";
}

#example:after {
​ content: ".";
}

这个例子中有两点需要注意,首先我们用 #example:before 和 #example:after 指向同一个元素,但是它们才是伪元素。

其次,没有 content 属性(这个属性是 generated content module定义的),伪元素不会起作用。所以伪元素需要指向一个元素,不然就没有地方插入 content 的内容。

这个例子中,id 为 example 的元素 会有一个 “#”在前面,一个“.”在后面。

语法的一些注释

你也可以给 content 属性赋空值,使伪元素看上去就像没有内容的盒子,像这样:

#example:before {
​ content: "";
​ display:block;
​ width: 100px;
​ height:100px;
​ float:left;
}

但是不能删掉 content 属性。删掉伪元素就无法工作了,至少要保留空引号作为它的值。

你可能也见到过用两个冒号的伪元素(::before 和 ::after),这个以前讨论过,简单点说这两种语法没有什么区别,只是为了区别伪类(单冒号)和CSS3中的伪元素(双冒号)。

最后还有一点,技术上来说,也可以全局定义伪元素,不指向一个特定元素,像这样

:before {
​ content: "#";
}

上面的代码是合法的,但没什么用。它将给DOM中所有的元素之前加上“#”,甚至你删除<body>中的所有元素,仍然看到两个“#”:一个是在<html>之前,一个是在<body>之前。

插入的内容的特点

如前所述,插入的内容在页面的源代码不可见,只在CSS可见。

此外插入的元素默认是内联元素【译注:inline】(或者在HTML5中,归类于text-level semantics)。所以要给插入的元素设置height, padding, margin通常需要明确定义为块级元素【译注:block-level】。

下面简短的介绍如何设置伪元素的样式,从我的编辑器中看:

Styles-pseudo-elements in Learning To Use The :before And :after Pseudo-Elements In CSS

在这个例子中,我高亮显示了要给目标元素之前和之后插入的内容设置的样式。伪元素这种方式有点独特,因为在一个声明中同时插入了内容和样式。

还要注意CSS继承规则适用于插入的元素,例如你定义了<body>的字体 Helvetica, Arial, sans-serif, 伪元素也会和其他元素一样继承这些字体样式。

同样,伪元素也不会继承不应该继承的样式(比如 padding, margin)。

在谁前面或后面?

你预料 :before 和 :after 伪元素可能会插入内容到目标元素之前或之后,但是,正如上面提到的,不是这样的。

插入的内容是做为目标元素的子元素,但是置于目标元素其它内容的“前面”或“后面”。

为了证明这一点,看下面的代码。首先在HTML中。

<p>Other content.</p>

下面是插入伪元素的CSS:

p.box {
​ width:300px;
​ border:solid 1px white;
​ padding:20px;
}

p.box:before {
​ content: "#";
​ border:solid 1px white;
​ padding:2px;
​ margin:0 10px 0 0;
}

在HTML中,你只能看到一个class为box的段落,段落里有”Other content”(如果你查看页面的源代码,看到的是一样的内容),在CSS中给段落设置width, padding, border。
然后是伪元素,在这种情况下,“#”被插入到段落内容“之前”,随后给它设置border, padding, margin。

这是我浏览器中看到的结果:【译注:参看http://jsbin.com/asuhav

Learning To Use The :before And :after Pseudo-Elements In CSS

外层盒子是段落。白色边框的“#”符号被插入到段落“内容”之前,而不是段落之前。

插入非文本内容

之前简略的提到,你可以给content属性设置空字符串值或者设置文本值。基本上,你还有两个额外的选择作为content属性的值。

首先,你可以设置一个URL指向一个图片,就像设置CSS背景图像一样:

p:before {
​ content: url(image.jpg);
}

注意【译注:最外层】没有引号。如果URL包括在引号中,将会插入一个字符串“url(image.jpg);”,而不是插入图片。【译注:content: url(“image.jpg”); 这样好像是可以的】

当然也可以包含一个 Data URI ,就像设置CSS背景图像一样。

你也可以设置一个 attr(X) 形式的函数。根据规范会“返回一个字符串,选择的对象的X属性的值”。下面是一个例子:

a:after {
​ content: attr(href);
}

什么是 attr() 函数?他返回指定的属性的值作为字符串插入到伪元素中。

上面都代码会把页面中所有的<a>元素的herf值放到<a>后面。这可以用来设置打印样式,把所有链接的地址打印出来。

你也可以用这个属性获取元素的title属性,甚至 microdata 值,当然不是所有的例子都实用/可用。但有些情况下一些特定的属性作为伪元素的内容很实用。

虽然能够获取图片的title或者alt属性的值很实用,但不能这样用。请记住,伪元素必须是一个元素的子元素。图像,是voud(或者empty)元素,没有子元素,所以这里不起作用。这同样适用于其他void 元素,比如<input>。

可怕的浏览器支持

与任何蓄势待发的前端技术一样,第一个问题就是浏览器支持情况。但在这里不是太大的问题。【译注:考虑的中国的国情…】

支持 :before 和 :after 伪元素的浏览器:
Chrome 2+,
Firefox 3.5+ (3.0 部分支持),
Safari 1.3+,
Opera 9.2+,
IE8+ (有一些bug),
几乎所有的移动浏览器【译注:同样,国情决定了…】

唯一的真正问题(毫不奇怪),IE6 和 IE7 不支持。所以如果你的受众是前端领域(或者其他IE用户少的市场),你可以自由的使用伪元素。

伪元素不危险

幸运的是,不支持伪元素不会在成巨大的可用性问题。在大多数情况下,伪元素都是起装饰(或辅助)作用,在不支持的浏览器中不会出错。所以,如果你的受众有很多IE用户,也可以在一定程度上使用它们。

几个提醒

如前所述,伪元素的内容不会出现在DOM中。这些元素不是真正的元素。因此大部分辅助设备不能访问它们。所以不要用伪元素生成重要内容,造成可用性和可访问性问题。

另外一个提醒,Firebug等开发工具看不到伪元素生成的内容。因此,如果过度使用会造成可维护性问题,调试过程会慢很多。

(作者更新:留言中提到,Chrome developer tools 可以看到伪元素的样式,但是DOM中还是看不到内容)

以上涵盖了实战前需要的所有知识。同时,为进一步了解CSS伪元素,记得点开文章中的链接了解更多。

Read More

招聘 JavaScript 程序员时应该问什么问题

from: 一名开发

摘自:

http://stackoverflow.com/questions/1684917/what-questions-should-a-javascript-programmer-be-able-to-answer/1684945

==

通用:

  • 使用过类库吗? 最喜欢哪个? 为什么?
  • 自己有写过类库吗? 比如 DOM 的扩展。
  • 有使用过服务端 JavaScript 框架吗?
  • ECMAScript 和 JavaScript 的区别是什么?
  • 有用过 JavaScript 代码校验工具吗?
  • 有读过或推荐的 JavaScirpt 书籍吗?
  • 会为你的 JavaScript 代码写单元测试吗?

初/中级:

  • 为什么基本上所有对象都有 toString 方法?
  • 知道 Mozilla Firefox 用的是哪个解析器吗? 其他浏览器呢?
  • JavaScript 支持 lambda 函数吗?
  • 你用过或写过的最有用的 JavaScript 函数是什么?
  • JavaScript 有块级作用域吗?
  • 能解释下 Ajax/XMLHttpRequest 是如何工作的吗?
  • JavaScript 支持类继承吗?
  • 能写一个用了 with 表达式的代码片段吗?
  • 知道什么是 Greasemonkey 吗? 有用过吗?
  • 你认为 innerHTML 是魔鬼吗?
  • 什么是 JSON?

高级:

  • Can you give me an example of a generator?
  • JSONP 是如何工作的?
  • 请举个单例模式的例子。
  • 未定义和未声明之间有什么区别?
  • 有用 Raphaël 或 Canvas 元素做过动画吗?
  • 熟悉 Web Worker 吗?
  • 做过 profiling 吗? 都有用过哪些工具?
  • 有读过新的 ECMAScript 规范吗? 都有哪些新特性?

关于人:

  • 谁最初写了 ECMAScript? 知道他在哪工作,以及他的 title 是什么吗?
  • 写 jQuery 的那男孩叫什么?
  • 谁写了 JSLint?

兼容性:

  • 哪些浏览器支持标准的 addEventListener ?
  • 哪些浏览器对于 getElementById 的实现有问题? 比如它会返回 name 属性一致的元素。

==

补充:

  • 如何在没有定义 toString 方法的对象上调用 toString() ?
  • 在调用函数时使用 new 会发生什么?
  • 什么是作用域链?
  • 如何在函数里创建静态变量?
  • 如果给你一个类名的字符串,你如何实例化他?
  • 什么是 currying? 如何在 JavaScript 里用他?
  • 什么是匿名函数? 什么是 lambda 函数?
  • 什么是 ‘live’ 容器?  (应该是指 getElementsByTagName 等方法返回的元素)
  • var 为什么重要?
  • 如何调试 JavaScript?

 

Read More