今天做简单的冒泡排序时,由于需要用图示法来展示冒泡排序的过程。于是便对每一次数组位置调换记录了快照,然后在渲染。开始的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
var arr = [5,4,3,2,1];
var i = 0,
j = 0,
temp = 0;
len = arr.length;
snapshoot = [],
for (i=0; i < len; i++) {
for (j = len-1; j > i; j--) {
if (arr[j-1] > arr[j]) {
temp = arr[j-1];
arr[j-1] = arr[j];
arr[j] = temp;
// 记录快照
snapshoot.push(arr);
}
}
}
</script>

这里最后snapshoot最后的元素都是一个相同的数组:[1,2,3,4,5],即排序完成后的arr,是什么原因导致了这样的结果呢?因为snapshoot中的arr都指向同一个数组。

这里的解决方法多种多样,只要解除pushsnapshoot中元素的指向即可。
如:

1
snapshoot.push(JSON.parse(JSON.stringify(arr)));

抑或:

1
snapshoot.push(arr.concat());

原文地址https://css-tricks.com/centering-css-complete-guide/

CSS居中事件是CSS的难点之一。有人埋怨,为什么一个“居中”要这么难呢?而我认为,这个问题并不是那么复杂,而是因为在不同情况下,总是有多种不同的解决方式,但我们不知道该用哪种。

所以我写了一个决策树,希望能使这个问题更加简单一点。

水平居中

如果需要水平居中的元素是行内元素或是类行内元素(如textlinks

当需要将一个块级元素下的行内元素水平居中时,只需要使用如下代码:

1
2
3
.center-children {
text-align: center;
}

这对设置display属性为inlineinline-blockinline-tableinline-flex等属性的元素有效。

当需要水平居中一个块级元素时

我们可以通过将块级元素的margin-rightmargin-left属性设置为auto来居中它(这时,这个元素必须设定宽度width,否则宽度会为100%而不需要居中),常常我们会这样写:

1
2
3
.center-me {
margin: 0 auto;
}

居中的块级元素或其父元素不管是哪种宽度,这种方法都有效。
值得注意的是,这对浮动元素是无效的。但是,可以看看居中浮动元素的小技巧

当需要居中两个或两个以上的块级元素时

如果你需要在同一行内居中两个或两个以上的块级元素,最好是改变它们的display属性,以下是将display属性设为inline-blockflex的例子:

查看DEMO

如果你想居中从上到下依次排列的几个块级元素,那么当需要水平居中一个块级元素时依然有效。

垂直居中

CSS垂直居中需要一点小技巧。

如果需要垂直居中的元素是行内元素或是类行内元素(如textlinks

如果垂直居中元素只占一行

由于padding-toppadding-bottom相等,inlinetext元素即可表现为垂直居中。

1
2
3
4
.link {
padding-top: 30px;
padding-bottom: 30px;
}

如果由于某些原因不能使用padding属性,在已知该文本不会被包含的前提下,可以将该文本元素的line-heightheight设为相等,即可居中。

1
2
3
4
5
.center-text-trick {
height: 100px;
line-height: 100px;
white-space: nowrap;
}

如果垂直居中元素占多行

padding-toppadding-bottom设为相等也能让包含多行文本的元素居中。但是有时候这种方法也无济于事,那么这个元素或许是类table-cell元素或者就是table-cell元素,这种情况下,就和处理单行文本元素的方式不同了。

查看DEMO

如果table的方式过时了,或许你可以用flexbox,设置flex属性的父元素可以轻松地居中其子元素。

查看DEMO

1
2
3
4
5
6
.flex-center-vertically {
display: flex;
justify-content: center;
flex-direction: column;
height: 400px;
}

记住,之所以这里的父元素包含块都设置了高度height,是因为这和父元素的固定高度真的有很大关系(%、px等)。

如果以上的技巧都已经过时,你可以使用“伪元素”,在包含块中放一个100%高度的伪元素,通过这种方法,文本元素也可在包含块中居中。

查看DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.ghost-center {
position: relative;
}

.ghost-center::before {
content: " "
;

display: inline-block;
height: 100%;
width: 1%;
vertical-align: middle;
}
.ghost-center p {
display: inline-block;
vertical-align: middle;
}

当需要垂直居中一个块级元素时

垂直居中已知高度的块级元素

由于许多原因,常常不能确定网页布局中元素的高度,例如:以为宽度的变化,文本重新排列会改变元素的高度;改变文本的CSS属性会改变元素高度;文本长度的改变也会导致元素高度的改变;像image这种有固定长宽比的元素,调整大小后也会导致高度的改变。

但是,如果你知道高度,你可以使用如下方式垂直居中:

1
2
3
4
5
6
7
8
9
.parent {
position: relative;
}

.child {
position: absolute;
top: 50%;
height: 100px;
margin-top: -50px; /* account for padding and border if not using box-sizing: border-box; */
}

垂直居中未知高度的块级元素

这时候,我们仍然可以把元素向上移动其高度的一半来垂直居中它。

1
2
3
4
5
6
7
8
.parent {
position: relative;
}

.child {
position: absolute;
top: 50%;
transform: translateY(-50%);
}

也可以使用flexbox

见怪不惊,flexbox会容易得多。

1
2
3
4
5
.parent {
display: flex;
flex-direction: column;
justify-content: center;
}

水平垂直居中

你可以以任何方式组合以上的写法,来达到完美居中元素的目的。但我发现不外乎这三种情况。

如果元素有固定宽度和高度

将元素固定定位,lefttop设为50%,设置负外边距,margin-left设为width的一半,margin-top设为height的一半,兼容大多数浏览器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.parent {
position: relative;
}


.child {
width: 300px;
height: 100px;
padding: 20px;

position: absolute;
top: 50%;
left: 50%;

margin: -70px 0 0 -170px;
}

未知宽高的元素

如果宽高未知,你可以使用transform属性。

1
2
3
4
5
6
7
8
9
.parent {
position: relative;
}

.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

使用flexbox

你需要使用两个居中属性来水平垂直居中元素。

1
2
3
4
5
.parent {
display: flex;
justify-content: center;
align-items: center;
}

总结

完全可以用CSS来达到居中元素的目的。

原文链接http://stackoverflow.com/questions/211383/which-method-of-clearfix-is-best

清除浮动的方式多种多样,以下每种方案在不同情况下,都有它出色的地方。

重提clearfix

CSS Mojo中Thierry Koblentz单独写了一篇文章来重温clearfix,强烈建议摈弃display: table的写法,而改用display: block的写法,其优点是避免包含块之间的margin塌陷(collapse)。

最近的一种代码量更少的清除浮动方案是这样的:

1
2
3
4
5
.container:after {
content: ''
;

display: block;
clear: both;
}

“忘掉上面的写法吧”,让我们看看在现代浏览器上怎么写

CSS Mojo中Thierry Koblentz还指出,针对现代浏览器开发时,我们可以弃用zoom:before等属性/值,简化写法:

1
2
3
4
5
.container:after {
content: ''
;

display: table;
clear: both;
}

这种写法特意没有支持IE6/7……

提醒一句:如果你重新开始一个新项目,放心做吧,但是不要使用这种写法,虽然你没有支持旧版本的IE浏览器,但是可以避免margin塌陷。

代码更少的清除浮动

这是最近广为接受的一种方案Micro Clearfix by Nicolas Gallagher

1
2
3
4
5
6
7
8
9
10
11
.container:before,
.container:after {
content:""
;

display:table;
}
.container:after {
clear:both
;

}
.container {
zoom:1; /* For IE 6/7 (触发 hasLayout) */
}

overflow属性

当定位元素不显示在容器外时,这种方案通常是首选。

http://www.quirksmode.org/css/clearing.html,这篇文章解释了这种写法的一个常见问题,也就是在容器上设置width: 100%

1
2
3
4
5
.container {
overflow: hidden;
display: inline-block; /* 用来触发IE的hasLayout */
display: block; /* 覆盖上一条属性,重新设置为block */
}

触发IE浏览器的hasLayout不止有display这一个属性,其他的属性也可以做相同的工作。

1
2
3
4
5
.container {
overflow: hidden; /* 清除浮动 */
zoom: 1; /* 用来触发IE的hasLayout */
display: block; /* 元素必须为块级元素,如果只应用于块级元素,这条可不写 */
}

overflow清除浮动的另一种方案是使用下划线hack,IE浏览器会识别带有下划线的属性,但是其他的浏览器不可以,下面是利用zoom属性触发IE hasLayout的写法:

1
2
3
4
5
.container {
overflow: hidden;
_overflow: visible; /* for IE */
_zoom: 1; /* for IE */
}

尽管这样很有用,但理想方案是不使用hack。

:after 伪元素

这种旧的方案以使用杂乱的CSS为代价,但好处是它会允许定位元素超出容器的边界。

http://www.positioniseverything.net/easyclearing.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.container {
display: inline-block;
}

.container:after {
content: " "
;

display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
.container {
display: block;
}

使用行内样式,clear属性

一种快捷但是很乱的写法

1
<br style="clear:both" /> <!-- So dirty! -->

因为很多因素,这种方案很不理想:

不符合代码复用的思想,让你的代码看起来很不专业。
以后有其他的清除浮动方案可使用时,你必须回头来整理这些杂乱的html。

本文转载自海玉的博客{:target=”_blank”}
原文地址:http://www.hicss.net/use-margin-or-padding/

margin还是用padding这个问题是每个学习CSS进阶时的必经之路。

CSS边距属性定义元素周围的空间。通过使用单独的属性,可以对上、右、下、左的外边距进行设置。也可以使用简写的外边距属性同时改变所有的外边距。——W3School

边界(margin):元素周围生成额外的空白区。“空白区”通常是指其他元素不能出现且父元素背景可见的区域。——CSS权威指南

padding称呼为内边距,其判断的依据即边框离内容正文的距离,而我喜欢CSS权威指南解释的“补白”(或者叫“留白”),因为他很形象。补白(padding):补白位于元素框的边界与内容区之间。很自然,用于影响这个区域的属性是padding

关于什么时候用margin什么时候用padding,网上有许许多多的讨论,大多数似乎讨论到点上面,却又有些隔靴搔痒的感觉,总是答不到点上。而且marginpadding在许多地方往往效果都是一模一样,而且你也不能说这个一定得用margin那个一定要用padding,因为实际的效果都一样,你说margin用起来好他说padding用起来会更好,但往往争论无果。根据网上的总结归纳大致发现这几条还是比较靠谱的:

何时应当使用margin:
需要在border外侧添加空白时。
空白处不需要背景(色)时。
上下相连的两个盒子之间的空白,需要相互抵消时。如15px + 20px的margin,将得到20px的空白。
何时应当时用padding:
需要在border内测添加空白时。
空白处需要背景(色)时。
上下相连的两个盒子之间的空白,希望等于两者之和时。如15px + 20px的padding,将得到35px的空白。

个人认为:margin是用来隔开元素与元素的间距;padding是用来隔开元素与内容的间隔。margin用于布局分开元素使元素与元素互不相干;padding用于元素与内容之间的间隔,让内容(文字)与(包裹)元素之间有一段“呼吸距离”。

举个例子吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>用Margin还是用Padding</title>
<style>
.top{width:160px; height:50px; background:#ccf;}
.middle{width:160px; background:#cfc; border-top:1px solid #ccc;}
.middle .firstChild{margin-top:20px;}
.middle .secondChild{margin-top:15px;}
</style>

</head>
<body>
<div class="top"></div>
<div class="middle">
<div class="firstChild">我是firstChild,我只是想和我的父元素隔开点距离,这样看起来舒服。</div>
<div class="secondChild">我要和楼上隔开点的距离。恩,能与底边有点呼吸距离就更好了。</div>
</div>
</body>
</html>

我是firstChild,我只是想和我的父元素隔开点距离,这样看起来舒服。
我是secondChild,我要和楼上隔开点的距离。恩,能与底边有点呼吸距离就更好了。

上面这个效果看起来很不错,达到了我们需要实现的目标。然而,我们细细查看下这个代码,对照下我们上文所说的规则,firstChild用了margin-top:20px来隔开父元素与他的距离,secondChild也用margin-top:15来隔开他与firstChild的距离,咋看之下挺符合我们所说的margin是用来隔开元素与元素的间距。但是他符合我们所说的margin用于布局分开元素使元素与元素互不相干吗?

这里我想说的是NO,firstChild同middle属于一种父子元素关系,又是一种包裹元素与内容的关系,他们之间从拟人化的角度来讲,不应该是老死不相干的局面。我们再来看我们为什么要让firstChild与他的父元素隔开的距离,从表现的角度上来看,文字与边靠的太近,肯定不好看。让文字与元素边隔开的距离,既美观,又使得文字有了足够的“呼吸空间”,方便阅读,这恰恰符合padding用于元素与内容之间的间隔让内容(文字)与(包裹)元素之间有个“呼吸距离”。

我们再来看,firstChild使用margin-top引发了垂直外边距合并的隐患,middle如果不加一个类似border-top:1px solid #ccc的话标准浏览器下就会呈现子元素顶了父元素margin隐患(这是个垂直外边距合并问题,可以查看不要告诉我你懂margin,这篇文章内有详细介绍)。可见这个时候margin显然不是很好的选择。

我们来试着这么修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>用Margin还是用Padding</title>
<style>
.top{width:160px; height:50px; background:#ccf;}
.middle_2{width:160px; background:#cfc; padding:20px 0px;}
.middle_2 .firstChild{}
.middle_2 .secondChild{margin-top:15px;}
</style>

</head>
<body>
<div class="top"></div>
<div class="middle_2">
<div class="firstChild">我是firstChild,我只是想和我的父元素隔开点距离,这样看起来舒服</div>
<div class="secondChild">我是secondChild,我要和楼上隔开点的距离。恩,能与底边有点呼吸距离就更好了。</div>
</div>
</body>
</html>

我是firstChild,我只是想和我的父元素隔开点距离,这样看起来舒服
我是secondChild,我要和楼上隔开点的距离。恩,能与底边有点呼吸距离就更好了。

我们来看看这么写的好处吧:

  1. 外观依旧良好,结构清晰也没有破坏布局。
  2. 不会产生垂直外边距合并这样的问题。
  3. 书写规范、代码量减少、重用性好。

我们可以看到在middle_2中去除了不需要的border-top,改为更为实用的padding:20px 0,让middle_2中的内容有了足够的“呼吸空间”,以后还可以随时随地修改这个padding,让内容文字的“呼吸空间”增大或者缩小,随时随地只修改一个middle_2的padding就能搞定所有包裹元素与内部内容的规划。

请注意这里是父元素应用padding,使得与其内容产生间隙,这是符合我们翻译为“补白”精髓(所以我一直喜欢称padding为“补白”而不是内边距),而padding也恰恰是在这儿最能体检他的价值。这个例子把第一个元素的margin-top去除,在父元素中应用padding。反过来,你会想,既然margin-top不好用,那么我第一个元素用padding-top不是也能达到效果么。恭喜你,你已经前进了一步了,的确使用padding-top即让第一元素与外包裹元素产生了呼吸距离,而且也不会出现所谓的垂直外边距重叠问题, 但是我依旧不推荐你这么做。为什么呢?我们来设想这么一个情况吧,假如有一天,你这个模块要产生变动,新需求要删除这个firstChild,替换为otherChild,会怎么样呢?

新的需求要求我们新加一个otherChild,替换原来的firstChild:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>用Margin还是用Padding</title>
<style>
.top{width:160px; height:50px; background:#ccf;}
.middle_3{width:160px; background:#cfc;}
.middle_3 .otherChild{font-weight:bold; font-style: italic;}
.middle_3 .secondChild{margin-top:10px;}
</style>

</head>
<body>
<div class="top"></div>
<div class="middle_3">
<div class="otherChild">我是新来的otherChild,我也想和我的父元素隔开点距离,这样看起来舒服,咦?!为什么我是在顶部?</div>
<div class="secondChild">我是secondChild,我要和楼上隔开点的距离。恩,能与底边有点呼吸距离就更好了。</div>
</div>
</body>
</html>

我是新来的otherChild,我也想和我的父元素隔开点距离,这样看起来舒服,咦?!为什么我是在顶部?
我是secondChild,我要和楼上隔开点的距离。恩,能与底边有点呼吸距离就更好了。

发现问题了么?如果你把原先的firstChild给删除掉了,新来的元素根本就没有定义上边距或者上补白,那么他就会自然顶在头部,不是理想的效果。的确,你可以为了他新写一个css来让他距离头部多一点空隙,但是你该怎么写?直接改otherChild吗?如果其他页面里面也有otherChild那么你会把其他地方的otherChild布局打乱。恩,那么我用.middle_3 .otherChild{padding-top:10px;}怎么样可以吧。恩,可以可以,可是你不觉得这么累吗?每次修改,都要增加这一个多余的代码就为了简简单单的隔开点距离,久而久之,你的css文件代码会臃肿不堪,可移植性大大削弱。

每次开发的时候我一直对自己讲,你写的代码总有一天会被别的开发人员所替换、修改、更新。而一个优秀的前端写出的css不但在现在结构坚固并且还能为日后的开发人员提供方便。修改我的代码,改前改后的式样位置都一样,让之后的开发人员根本上避免接触到再次“修复”开发的机会,那才是一名真正前端的追求。这里你把包裹的div类似“封装”好一个环境,而且这个div内已经留有足够的内容的“呼吸空间”,你只需要改内容,内容所要考虑到得位置边距问题,外包的div元素早已经帮你预留好了,你用起来方便,今后改起来也方便,直接找到middle修改padding即可。

所谓大道万千,运用之妙存乎一心。该用margin的时候就大胆的用他,该用padding也不用退缩不前,实战中累积出来的经验往往是最有用的,而当你不确定是用margin好还是用padding,请在看看这个原则吧,或许你会有一个自己的答案。

可以利用栈将一个数字从一种数制转换成另一种数制。假设想将数字n转换为以b为基数的数字,实现转换的算法如下:

  1. 最高位为 n%b ,将此位压入栈。
  2. 使用 n/b 代替n。
  3. 重复步骤1和2,直到n等于0,且没有余数。
  4. 持续将栈内元素弹出,直到栈为空,依次将这些元素排列,就得到转换后的数字的字符串形式。
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<script>
function Stack() {
this.dataStore = [];
this.top = 0;
this.push = push;
this.pop = pop;
this.peek = peek;
this.clear = clear;
this.length = length;
}

function push(element) {
this.dataStore[this.top++] = element;
}

function peek() {
return this.dataStore[this.top-1];
}

function pop() {
return this.dataStore[--this.top];
}

function clear() {
this.top = 0;
}

function length() {
return this.top;
}



/* 数制转换函数 */
function mulBase(num, base) {
var s = new Stack();
do {
s.push(num%base);
num = Math.floor(num /= base);
} while(num > 0);
var converted = "";
while(s.length() > 0) {
converted += s.pop();
}
return converted;
}

alert(mulBase(10, 2));


</script>

export和import

ES6实现了模块功能,试图解决JavaScript代码上的依赖和部署上的问题,取代现有的CommonJsAMD规范,成为浏览器和服务器通用的模块解决方案。

模块功能有两个关键字: exportimportexport用于用户自定义模块,规定对外接口;import用于输入其它模块提供的功能,同时创造命名空间(namespace),防止函数名冲突。

ES6允许将独立的JS文件作为模块,也就是说,允许一个JavaScript脚本文件调用另一个脚本文件。最简单的模块就是一个JS文件,里面使用export关键字输出变量。

1
2
3
4
// profile.js
export var firstName = 'David';
export var lastName = 'Belle';
export var year = 1973;

上面是profile.js的内容,ES6将其视为一个模块,里面用export关键字输出了三个变量。export的写法,除了上面这样,还有另外一种,两者是等价的。

1
2
3
4
5
6
// profile.js
var firstName = 'David';
var lastName = 'Belle';
var year = 1973;

export {firstName, lastName, year};

使用export定义模块后,其他JS文件就可以通过import关键字加载这个模块(文件)。

1
2
3
4
5
import {firstName, lastName, year} from './profile';

function setHeader(element) {
element.textContent = firstName + ' ' + lastName;
}

上面的代码中import关键字接受一个对象(用大括号表示),里面指定要从其他模块导入的变量。大括号里面的变量名,必须与被导入模块对外接口的名称相同。

如果想为输入的属性或方法重新取一个名字,import语句要写成这样。

1
import {someMethod, another as newName} from './profile';

模块的整体加载

export关键字除了输出变量,还可以输出方法或类(class)。下面是circle.js文件的内容,它输出两个方法。

1
2
3
4
5
6
7
8
9
// circle.js

export function area(redius) {
return Math.PI * radius * radius;
}

export function circumference(radius) {
return 2 * Math.PI * radius;
}

然后,main.js引用这个模块。

1
2
3
4
5
6
// main.js

import {area, circumference} from './circle';

console.log("圆面积: " + area(4));
console.log("圆周长: " + circumference(14));

上面的写法是逐一指定要导入的方法。另一种写法是使用module关键字,整体导入。

1
2
3
4
5
6
// main.js

module circle from './circle';

console.log("圆面积: " + circle.area(4));
console.log("圆周长: " + circle.circumference(14));

module关键字后面跟一个变量,表示导入的模块定义在该变量上。

export default语句

如果不想为某个属性或方法指定输入的名称,可以使用export default语句。

1
2
3
4
5
// export-default.js

export default function foo() {
console.log('foo');
}

上面代码中的foo方法,就被称为该模块的默认方法。

在其他模块导入该模块时,import语句可以为默认方法指定任意名字。

1
2
3
4
5
// import-default.js

import customName from 'export-default';

customName(); // 'foo'

显然,一个模块只能有一个默认方法。

如果要输出默认属性,只需将值跟在export default之后即可。

1
export default 42;

模块的继承

模块之间也可以继承。

假设有一个circleplus模块,继承了circle模块。

1
2
3
4
5
6
7
8
9
// circleplus.js

export * from './circle';

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}

上面代码中的“export *”,表示输出circle模块的所有属性和方法,export default命令定义模块的默认方法。

这时,也可以将circle的属性或方法,改名后再输出。

1
2
3
// circleplus.js

export { area as circleArea } from 'circle';

上面代码表示,只输出circle模块的area方法,且将其改名为circleArea

加载上面模块的写法如下。

1
2
3
4
5
// main.js

module math from "circleplus";
import exp from "circleplus";
console.log(exp(math.E));

上面代码中的”import exp”表示,将circleplus模块的默认方法加载为exp方法。

维基百科对单例模式的介绍如下:

在应用单例模式时,生成单例的类必须保证只有一个实例的存在,很多时候整个系统只需要拥有一个全局对象,才有利于协调系统整体的行为。比如在整个系统的配置文件中,配置数据有一个单例对象进行统一读取和修改,其他对象需要配置数据的时候也统一通过该单例对象来获取配置数据,这样就可以简化复杂环境下的配置管理。

实现一、最简单的字面量写法

1
2
3
4
5
6
7
8
9
10
<script type="text/javascript">
var singleton = {
attr: 1,
method: function(){ return this.attr;}
}
var t1 = singleton;
var t2 = singleton;

alert(t1 === t2); // true
</script>

十分简单,并且非常使用,不足之处在于没有什么封装性,所有的属性方法都是暴露的。对于一些需要使用私有变量的情况就显得心有余而力不足了。当然在对于 this 的问题上也是有一定弊端的。

实现二、构造函数内部判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script type="text/javascript">
function Construct(){
// 确保只有单例
if( Construct.unique !== undefined ){
return Construct.unique;
}

// 其他代码
this.name = "NYF";
this.age="24";
Construct.unique = this;
}
var t1 = new Construct() ;
var t2 = new Construct() ;
</script>

也是非常简单,无非就是提出一个属性来做判断,但是该方式也没有安全性,一旦我在外部修改了Construct的unique属性,那么单例模式也就被破坏了。

实现三、闭包方式

对于打着”灵活”牌子的JS来说,任何问题都能找到 n 种答案,只不过让我自己去掂量孰优孰劣而已,下面就简单的举几个使用闭包实现单例模式的方法,无非也就是将创建了的单例缓存而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript">
var single = (function()
{
var unique;
function Constructor() {
this.name = "NYF";
this.age="24";
}
unique = new Constructor();
return unique;

})();
var t1 = single;
var t2 = single;

alert(t1 === t2);
</script>

只要 每次讲 var t1 = single; var t2 = single;即可。 与对象字面量方式类似。不过相对而言更安全一点,当然也不是绝对安全。

如果希望会用调用 single() 方式来使用,那么也只需要将内部的 return 改为

1
2
3
return function(){
return unique;
}

以上方式也可以使用 new 的方式来进行(形式主义的赶脚)。当然这边只是给了闭包的一种例子而已,也可以在 Construct 中判断单例是否存在 等等。 各种方式在各个不同情况做好选着即可。

参考原文http://my.oschina.net/humaotegong/blog/410666?p=1

点击此处查看DEMO演示

今天在做大众点评在线笔试题目的时候,遇到一个很有趣的编程题目,实现黑客电影中的字符串效果。

对!就是图中这样……当时是直接在提交文本框上写的,也没有调试机会,于是肯定会有很bug,晚上闲来无事,重新实现一下~

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<style type="text/css">
body {
background: #000;
color: #fff;
}

.rain {
position: absolute;
top: 0;
left: 0;
font-size: 20px;
background: lightgreen;
width: 25px;
box-shadow: 2px 2px 4px green;
}

</style>

</head>
<body>
<!-- <div class="rain"></div> -->
<script type="text/javascript">
function generateChar() {
var strLen = Math.floor(Math.random()*100);
var i = 0;
var choose = 0;
var resCode = 0;
var str = '';
for (i = 0; i < strLen; i++) {
choose = Math.floor(Math.random()*3);
switch (choose) {
case 0:
resCode = Math.floor(Math.random()*10 + 48);
break;
case 1:
resCode = Math.floor(Math.random()*26 + 65);
break;
case 2:
resCode = Math.floor(Math.random()*26 + 97);
break;
}
str += String.fromCharCode(resCode)+'<br>';
}
return str;
}

function animate(obj) {
var cWidth = document.documentElement.clientWidth-10;
var cHeight = document.documentElement.clientHeight;
// alert(cHeight);
var startLeft = Math.floor(Math.random()*cWidth);
var startTop = 0;
obj.style.left = startLeft + 'px';
obj.style.top = startTop + 'px';
var speed = 10;

obj.timer = setInterval(function(){
startTop += speed;
obj.style.top = startTop + 'px';
if (startTop > cHeight-obj.offsetHeight) {
clearInterval(obj.timer);
document.body.removeChild(obj);
}
}, 30);
}

setInterval(function(){
var obj = document.createElement('div');
obj.className = 'rain';
obj.innerHTML = generateChar();
document.body.appendChild(obj);
animate(obj);
}, 50);

</script>

</body>
</html>

插入排序

算法简介

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

算法描述和实现

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中从后向前扫描;
如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后;
重复步骤2~5。
JavaScript代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function insertionSort(array) {
if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
for (var i = 1; i < array.length; i++) {
var key = array[i];
var j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
return array;
} else {
return 'array is not an Array!';
}
}

算法分析

最佳情况:输入数组按升序排列。T(n) = O(n)
最坏情况:输入数组按降序排列。T(n) = O(n2)
平均情况:T(n) = O(n2)

二分插入排序

算法简介

二分插入(Binary-insert-sort)排序是一种在直接插入排序算法上进行小改动的排序算法。其与直接插入排序算法最大的区别在于查找插入位置时使用的是二分查找的方式,在速度上有一定提升。

算法描述和实现

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中二分查找到第一个比它大的数的位置;
将新元素插入到该位置后;
重复上述两步。
JavaScript代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function binaryInsertionSort(array) {
if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
for (var i = 1; i < array.length; i++) {
var key = array[i], left = 0, right = i - 1;
while (left <= right) {
var middle = parseInt((left + right) / 2);
if (key < array[middle]) {
right = middle - 1;
} else {
left = middle + 1;
}
}
for (var j = i - 1; j >= left; j--) {
array[j + 1] = array[j];
}
array[left] = key;
}
return array;
} else {
return 'array is not an Array!';
}
}

算法分析

最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)

选择排序

算法简介

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

算法描述和实现

n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

初始状态:无序区为R[1..n],有序区为空;
第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
n-1趟结束,数组有序化了。
JavaScript代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function selectionSort(array) {
if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
var len = array.length, temp;
for (var i = 0; i < len - 1; i++) {
var min = array[i];
for (var j = i + 1; j < len; j++) {
if (array[j] < min) {
temp = min;
min = array[j];
array[j] = temp;
}
}
array[i] = min;
}
return array;
} else {
return 'array is not an Array!';
}
}

算法分析

最佳情况:T(n) = O(n2)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)

冒泡排序

算法简介

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

算法描述和实现

具体算法描述如下:

比较相邻的元素。如果第一个比第二个大,就交换它们两个;
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
针对所有的元素重复以上的步骤,除了最后一个;
重复步骤1~3,直到排序完成。
JavaScript代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function bubbleSort(arr){
//外层循环,共要进行arr.length次求最大值操作
for(var i=0;i<arr.length;i++){
//内层循环,找到第i大的元素,并将其和第i个元素交换
for(var j=i;j<arr.length;j++){
if(arr[i]<arr[j]){
//交换两个元素的位置
var temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
}

快速排序

算法简介

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

算法描述和实现

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
JavaScript代码实现:

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
var quickSort = function(arr) {

  if (arr.length <= 1) { return arr; }

  var pivotIndex = Math.floor(arr.length / 2);

  var pivot = arr.splice(pivotIndex, 1)[0];

  var left = [];

  var right = [];

  for (var i = 0; i < arr.length; i++){

    if (arr[i] < pivot) {

      left.push(arr[i]);

    } else {

      right.push(arr[i]);

    }

  }

  return quickSort(left).concat([pivot], quickSort(right));

};

堆排序

算法简介

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

算法描述和实现

具体算法描述如下:

将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
JavaScript代码实现:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*方法说明:堆排序
@param array 待排序数组*/

function heapSort(array) {
if (Object.prototype.toString.call(array).slice(8, -1) === 'Array') {
//建堆
var heapSize = array.length, temp;
for (var i = Math.floor(heapSize / 2); i >= 0; i--) {
heapify(array, i, heapSize);
}

//堆排序
for (var j = heapSize - 1; j >= 1; j--) {
temp = array[0];
array[0] = array[j];
array[j] = temp;
heapify(array, 0, --heapSize);
}
} else {
return 'array is not an Array!';
}
}
/*方法说明:维护堆的性质
@param arr 数组
@param x 数组下标
@param len 堆大小*/

function heapify(arr, x, len) {
if (Object.prototype.toString.call(arr).slice(8, -1) === 'Array' && typeof x === 'number') {
var l = 2 * x, r = 2 * x + 1, largest = x, temp;
if (l < len && arr[l] > arr[largest]) {
largest = l;
}
if (r < len && arr[r] > arr[largest]) {
largest = r;
}
if (largest != x) {
temp = arr[x];
arr[x] = arr[largest];
arr[largest] = temp;
heapify(arr, largest, len);
}
} else {
return 'arr is not an Array or x is not a number!';
}
}

算法分析

最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)

归并排序

算法简介

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。归并排序是一种稳定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

算法描述和实现

具体算法描述如下:

把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
JavaScript代码实现:

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
function mergeSort(array, p, r) {
if (p < r) {
var q = Math.floor((p + r) / 2);
mergeSort(array, p, q);
mergeSort(array, q + 1, r);
merge(array, p, q, r);
}
}
function merge(array, p, q, r) {
var n1 = q - p + 1, n2 = r - q, left = [], right = [], m = n = 0;
for (var i = 0; i < n1; i++) {
left[i] = array[p + i];
}
for (var j = 0; j < n2; j++) {
right[j] = array[q + 1 + j];
}
left[n1] = right[n2] = Number.MAX_VALUE;
for (var k = p; k <= r; k++) {
if (left[m] <= right[n]) {
array[k] = left[m];
m++;
} else {
array[k] = right[n];
n++;
}
}
}

算法分析

最佳情况:T(n) = O(n)
最差情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)

桶排序

算法简介

桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。

算法描述和实现

具体算法描述如下:

设置一个定量的数组当作空桶;
遍历输入数据,并且把数据一个一个放到对应的桶里去;
对每个不是空的桶进行排序;
从不是空的桶里把排好序的数据拼接起来。
JavaScript代码实现:

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
31
32
33
34
/*方法说明:桶排序
@param array 数组
@param num 桶的数量*/
function bucketSort(array, num) {
if (array.length <= 1) {
return array;
}
var len = array.length, buckets = [], result = [], min = max = array[0], regex = '/^[1-9]+[0-9]*$/', space, n = 0;
num = num || ((num > 1 && regex.test(num)) ? num : 10);
for (var i = 1; i < len; i++) {
min = min <= array[i] ? min : array[i];
max = max >= array[i] ? max : array[i];
}
space = (max - min + 1) / num;
for (var j = 0; j < len; j++) {
var index = Math.floor((array[j] - min) / space);
if (buckets[index]) { // 非空桶,插入排序
var k = buckets[index].length - 1;
while (k >= 0 && buckets[index][k] > array[j]) {
buckets[index][k + 1] = buckets[index][k];
k--;
}
buckets[index][k + 1] = array[j];
} else { //空桶,初始化
buckets[index] = [];
buckets[index].push(array[j]);
}
}
while (n < num) {
result = result.concat(buckets[n]);
n++;
}
return result;
}

算法分析

桶排序最好情况下使用线性时间O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。

计数排序

算法简介

计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。

算法描述和实现

具体算法描述如下:

找出待排序的数组中最大和最小的元素;
统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
JavaScript代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function countingSort(array) {
var len = array.length, B = [], C = [], min = max = array[0];
for (var i = 0; i < len; i++) {
min = min <= array[i] ? min : array[i];
max = max >= array[i] ? max : array[i];
C[array[i]] = C[array[i]] ? C[array[i]] + 1 : 1;
}
for (var j = min; j < max; j++) {
C[j + 1] = (C[j + 1] || 0) + (C[j] || 0);
}
for (var k = len - 1; k >=0; k--) {
B[C[array[k]] - 1] = array[k];
C[array[k]]--;
}
return B;
}

算法分析

当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。

文章来源于 http://blog.jobbole.com/76339/

明天下午面试微店的前端开发职位,有点紧张~~

刚刚正好看到js中的内存泄露,所以来整理一番。

给DOM对象添加的属性是对一个js对象的引用。

1
2
var MyObject = {}; 
document.getElementById('myDiv').myProp = MyObject;

解决方法:
在window.onunload事件中写上: document.getElementById('myDiv').myProp = null;

DOM对象和JS对象相互引用。

onunload事件中写上: document.getElementById('myDiv').myProp = null;

给DOM对象用attachEvent()绑定事件。

在onunload事件中写上: element.detachEvent(‘onclick’, doClick);

反复重写同一个属性。

避免这种情况发生就是啦!

由外到内使用appendChild()方法。

由内到外执行即可。