DAY1 构建原型图

昨晚5点多睡觉,早上12点才起床,下午爬去参加了珠海的 GDG。主讲是 360 的一个高级网络工程师(我才知道珠海也有 360 的分部),还有一个腾讯的员工,讲的内容一般般,收获还是有一点的。晚上又折腾了一下 deepin 操作系统,打算还是暂时不碰 Linux。睡前先把首页的原型图大概切一下先。

首页原型图

明天再做详细的切图啦,今天实在太累了……GG思密达

准备开始一个新的项目 Oxygen轻音乐

今天顿生无聊,感觉陷入一个瓶颈,所以打算开一个新的项目——Oxygen轻音乐,比较简单的一个收藏自己喜欢的歌的网站,结合自己之前学过的东西进行整理,从中发现不足,进行下一步学习。同时也可以得到一些Nope.js的新函数的灵感,不废话,先进行一下需求分析和技术分析。

需求

  • 前端
    • 一个简单的播放器样式
    • 暂停/播放
    • 切歌
    • 音量调节
    • 循环播放
    • 歌词显示(动态 or 静态)
    • 歌曲列表的显示
    • 歌曲 & 歌手信息
  • 后台
    • 需要有提供更新歌曲信息的页面
    • 新增歌曲
    • 删除歌曲
    • 排序歌曲

技术

  • 前端
    • Javascript + CSS3 + HTML5
    • Grunt 负责打包
    • require.js 作模块管理
    • Backbone.js 作为 MVC 框架(优点:轻量级,适合SPA,Oxygen 切歌需要相对多的 dom 操作)
    • 暂定支持IE 9+, Chrome, Firefox, Opera, Edge
  • 后台
    • Node.js
    • hbs 模板引擎
    • MongoDB 数据库
    • express Web框架
    • 腾讯云作为资源存放和服务器托管(下行1M,如果速度不满足,改用七牛云)

样式

  • 参照一下 UI 设计图,并作修改

    http://www.ui.cn/detail/9499.html

  • 播放器背景使用高斯模糊对相应歌曲图片处理

  • 使用 Font Awesome 字体图标

一道有趣的JS题目的分析

今天在微博看到有人发了一道题目,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Foo() {
getName = function() {console.log(1);}
return this;
}
Foo.getName = function() {console.log(2);};
Foo.prototype.getName = function() {console.log(3);};
var getName = function() {console.log(4);};
function getName() {console.log(5);};
Foo.getName();//2
getName();//4
Foo().getName();//1
getName();//1
new Foo.getName();//2
new Foo().getName();//3
new new Foo().getName();//3
Foo.getName(),这里调用的方法是第5行代码的函数,这个没什么疑问。

getName(),这里调用的是第 7 行的函数,有人要问了,为什么不是第 8 行,这里涉及到JS解析器中一个function declaration hoisting(函数声明提升)的过程,在 JS 代码中,使用var x = function(){}叫作函数表达式,使用function x(){}叫作函数声明,在 JS 代码加载的过程中,函数声明会在第一时间加载到源代码树的顶部,所以即使你调用使用函数声明的函数比该函数的声明还早,也是无异常的。反之,函数表达式则要在 JS 加载到该行代码时,函数才算是真正可以使用。所以这道题里,第 8 行比第 7 行的函数声明早,所以第7行的函数表达式覆盖了第 8 行的函数内容。

Foo().getName(),这里执行 Foo() 后返回的 this 是 window,即全局对象,而 Foo() 内部的getName由于没有使用 var 去声明,故默认为 window 的 getName 属性,所以又再一次覆盖了原先的 getName(),所以打印是1。

getName(),由于上一行代码的执行已经导致了 window 下的 getName() 被覆盖,所以依旧打印1。

new Foo.getName(),这里 new 的是 getName() 而不是 Foo(),所以打印了2,与第 10 行的调用方法类似,不过这里还会返回一个 Foo.getName 的实例。

new Foo().getName(),这里首先 new Foo() 返回 Foo() 的实例对象,这里注意,构造函数中如果没有指明 return,那么默认返回构造函数的实例,如果指定了 return,那么返回指定的 return 的值(只能是引用类型的值,如{},[]等),这里指定了 return this,this 在 new 的情况下是实例本身,所以跟默认情况下是一致的,然后又调用了 getName(),这里调用的是原型中的函数,所以打印3。

new new Foo().getName(),先不看第一个 new,则后面的步骤跟上面一致,不过到了调用函数的时候,第一个 new 发生作用了,实例化了 getName(),不过这里的 getName() 依旧是原型中的函数,只不过将他实例化了,所以依旧打印3。

clearfix 闭合浮动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/** * For modern browsers */
.clearfix:before, .clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
}
/** * For IE 6/7 (trigger hasLayout) */
.clearfix {
*zoom: 1;
}

.clearfix:after {content:"."; display:block; height:0; visibility:hidden; clear:both; }
这里也是一种方法,content为一个.的原因是在 Firefox 里,空的 content 会有额外间隙,所以使用.,此外的height等属性只是为了隐藏 content 不影响布局。而最新的方法 display: table 的 content 可以为空,且不会产生间隙,所以额外的属性都可以去除。

在父元素使用overflow清除浮动的原理是,将父元素变为一个新的BFC,根据 BFC 的特点,BFC 可以包含浮动元素,所以,不仅仅是 overflow 能清除浮动,只要能触发父元素的BFC的方式都可以清除浮动,例如display: table-cell,float:left等等都可以触发BFC。

传统的加一个空的div去清除浮动的方法有一个缺点,当子元素是浮动元素且有外边距是,清除浮动以后,父元素的高度仅仅是包裹子元素的高度(不包括外边距的高度),那么子元素明显会被边距顶出了父元素外边,虽然父元素会被撑开一段高度,但是由于子元素外边距的存在,父元素无法完全包裹子元素。

关于BFC的知识,可浏览

http://www.cnblogs.com/leejersey/p/3991400.html

http://kayosite.com/remove-floating-style-in-detail.html

Function的类型判断

1. typeof

首先,使用 typeof 去判断一个函数是一种通用的做法,不过这种做法存在一种风险,即在低版本浏览器下会出现误判的现象,在 IE6/7/8 下,一些 JS 的核心函数是以 COM 对象去构造的函数,例如 document.getElementById,正常情况下,typeof 应该对其判断为 function,但是在 IE6/7/8 下,会将其判断为 object
在 Chrome 和 Safari 的旧版本中,正则表达式进行 typeof 的判断结果为 function,其他浏览器则返回 object,这会导致误判,所以使用 typeof 判断函数是一种不稳定的做法,存在一定的风险。

2. 使用Object.prototype.toString.call()

  • 使用 Object 的原型里的 toString 去判断类型也是一种方法,而且也稳定安全得多,其结果返回的格式是“[object NativeConstructorName]”
  • 和 typeof 一样存在的一个问题是其在 IE6/7/8 仍会对某些核心函数判断为 [object Object],这是 IE 特有的 JScript 所导致的,IE 中一些函数是以 COM 对象构造的。
  • 在这种方法下,正则表达式的返回就正确了,结果为 [object RegExp]

总结:在 IE9+ 和比较新的 Chrome、Safari 下,用 typeof 还是很方便的,而且效率比使用第二种方法快得多,在100万次的执行效率对比下,typeof 判断完消耗时间大概为4.5ms,而 Object.prototype.toString 则消耗高达50ms以上,其效率差距10几倍。以下代码可以判断是否使用 typeof 判断 Function:

1
2
3
4
5
if(typeof /./ != "function" && typeof Int8Array != "object") {
np.isFunction = function(func) {
return typeof func === "function" || false;
}
}

两列布局一列固定宽度一列自适应宽度实现

1.使用 margin + float 实现

HTML:

1
2
3
4
<div class="normal">
<div class="normal-right"></div>
<div class="normal-left"></div>
</div>

CSS:

1
2
3
4
5
6
7
8
9
10
11
.normal-right {
float: right;
width: 100px;
height: 300px;
background-color: blue;
}
.normal-left {
margin-right: 100px;
height: 300px;
background-color: yellow;
}

使用该种方法,浮动在右边的div必须写在左边div之前

2.使用 table + table-cell 实现

HTML:

1
2
3
4
<div class="table">
<div class="table-left"></div>
<div class="table-right"></div>
</div>

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.table {
display: table;
width: 100%;
margin-top: 50px;
}
.table-left {
display: table-cell;
height: 300px;
background-color: yellow;
}
.table-right {
display: table-cell;
width: 100px;
height: 300px;
background-color: blue;
}

diplay: table 在IE上仅支持IE 8及以上版本

3.使用 flex 布局实现

HTML:

1
2
3
4
<div class="flex">
<div class="flex-left"></div>
<div class="flex-right"></div>
</div>

CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.flex {
display: flex;
margin-top: 50px;
}
.flex-left {
width: 100%;
background-color: blue;
flex-shrink: 1;
height: 300px;
}
.flex-right {
width: 100px;
background-color: yellow;
flex-shrink: 0;
height: 300px;
}

flex 布局仅支持 IE 10+、Chrome 21+、Firefox 22+、Safari 6.1+、Opera 12.1+

Ajax的多种跨域方式

CORS(Cross-Origin Resource Sharing,跨源资源共享)

CORS 的实现相对简单,当我们使用 XMLHttpRequest 进行跨域时,会给request的地址加入一个 Origin 头部,其中包含请求页面的源信息(eg. Origin: http://www.baidu.com),如果服务器接收到 request 后,需要给 response 加上 Access-Control-Allow-Origin 头部(eg. Access-Control-Allow-Origin: http://www.baidu.com),当然,如果是公共资源,将地址改成*就可以允许所有源地址进行跨域访问。如果服务器没有加入和 Origin 头部地址对应的头部或者 Access-Control-Allow-Origin 允许的跨域地址不相符合(eg. Access-Control-Allow-Origin: http://www.google.com),那么浏览器将会驳回请求,导致跨域失败。

1.XDomainRequest

XDomainRequest(以下简称XDR)是IE部分实现CORS规范的一个类型,和 XMLHttpRequest(以下简称XHR)不同的是,XDR 有以下不同:

  • cookie 不会随请求发送,也不会随响应返回
  • 只能设置请求头部信息中的 Content-Type 字段
  • 不能访问响应头部信息
  • 只支持 GET 和 POST 请求
  • 浏览器支持:只有IE8+

2.XMLHttpRequest

使用 XMLHttpRequest(以下简称XHR)跨域和 XDR 跨域不同之处有以下几点:

  • 可以访问status
  • 可以访问statusText
  • 支持同步请求

同时,跨域 XHR 也有以下限制(每个浏览器安全策略不同,例如chrome可以对跨域的XHR使用setRequestHeader):

  • 不能使用 setRequestHeader() 设置自定义头部
  • 不能发送和接收cookie(可以通过设置 withCredentials 属性为 true 来允许发送cookie,同时服务器必须设置 Access-Control-Allow-Credentials: true)
  • 调用 getAllResponseHeaders() 方法总会返回字符串

3.Preflighted Requests

Preflighted Requests 是一种透明服务器验证机制,使用即可支持开发人员使用自定义头部、GET 或 POST 以外的方法以及不同类型的主体内容。上面提到跨域XHR不能使用 setRequestHeader() 设置自定义头部,如果使用了 setRequestHeader() 设置自定义头部(eg. xhr.setRequestHeader(“halo”, “loha”)),那么在发出正式请求之前,浏览器首先会发出一个 OPTION 方法的请求,其中包括以下头部内容:

  • Origin: 参照上面
  • Access-Control-Request-Method: 请求自身的方法,例如GET
  • Access-Control-Request-Headers: 自定义头部信息,多个头部以逗号分隔,例如halo

当服务器接收到OPTION方法请求后,返回以下头部信息:

  • Access-Control-Allow-Origin: 必须同源
  • Access-Control-Allow-Method: 必须包含OPTION请求中的方法
  • Access-Control-Allow-Headers: 必须包含 OPTION请求中的Header名
  • Access-Control-Max-Age: 设置该 Preflighted 可以缓存的时间

当OPTION成功请求并收到返回,浏览器将会检查response的头部信息,如果符合条件则正式发起请求,同时缓存此次 Preflighted 请求的结果。反之,如果不符合,那么真正的请求会失败。

4.JSONP

JSONP(JSON with padding, 填充式JSON)与 JSON 并无关系,JSONP 的跨域实现主要运用 <script> 不受同域限制的原理,在请求的 URL 后加入一个 callback 参数,该 callback 参数代表服务器需要返回的JS代码的函数名称(eg. <script src="http://localhost?callback=func">),“func”这个名称将是服务器返回 JS 代码的函数名称(eg. 以php为例,echo "func(123)"),当 <script> 接收到这段代码的时候,便会运行 func 函数,并把“123”作为数据代进去(前提是你在前面已经写了一个func函数),至此,JSONP 完成。不过 JSONP 有以下缺点:

  • 安全性无法保证
  • 只能使用 GET 方法请求

5.图像ping

图像ping 利用 <img> 去访问一个 URL,同时对该 <img>onloadonerror 事件进行监听,可以得知请求的成功与失败,但无法获取返回数据,多用于跟踪网页点击次数等。图像ping 有以下缺点:

  • 只能使用 GET 方法请求
  • 无法获取返回数据

6.iframe

想要操作 iframe 的内容,那么 iframe 和主页面必须同域。
使用 document.domain 可以修改iframe和主页面的域名为同一个,前提是两个页面处于同一主域名下(eg. p2p.a.com和www.a.com可以设置为a.com),这样就可以操作 iframe 里的 contentDocument 了
使用 location.hash(eg. 将a.com变为a.com#abc不会导致页面刷新),不过由于同域原因,要加多一个和主页面同域的页面才能成功修改主页面的 hash,主页面使用定时器检测 hash 变化。可以查看以下网址了解:

http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html

使用 window.name,同样需要第三个和主页面同域的页面做代理,window.name 可容纳2M的内容。具体方法查看:

http://www.cnblogs.com/rainman/archive/2011/02/21/1960044.html

使用 postMessage,参考

http://www.cnblogs.com/dolphinX/p/3464056.html

7.Web Sockets

以 node.js 为例,基于 socket.io 可以很容易建立 Web Sockets, HTML5 的 Web Sockets 使用的是 ws://wss:// 协议,这样可以减少 http://https:// 协议字节级的开销,带宽消耗小。具体实现请查阅Google。

双飞翼布局和圣杯布局

双飞翼布局源自淘宝UED,圣杯布局来自2006年 Matthew Levine 的一篇文章,目的都是为了实现三列布局(中间列自适应宽度,两边分别固定宽度),并且中间列优先加载。两种布局的原理可以应用到两列布局中。

开始之前说一个翻译问题,圣杯布局作者提到发明圣杯布局前的其中一个需求:allow any column to be the tallest
这里应该翻译为:允许任意列高度是最高的,而不是“允许任意列(在 HTML 中位置)在最高”。

相同点:

  • 都让三列布局左浮动
  • 实现三列布局
  • 中间列优先加载
  • 左右两列顺序不影响布局

异同点:

  • 圣杯布局利用布局父元素 container 左右 padding 出边距给予左右两列的空间放置,左右两列则通过 position 和margin 负边距插入到自身的位置。
  • 双飞翼布局直接在 container 加入一个 div 包裹住中间列,同时该 div 浮动,而被包裹住的中间列则 margin 左右为左右两列布局留出空白,这样做省了多个 CSS 属性(position,左右两列的 margin 负边距,container 的 padding 属性,左右两列的 left 或 right 属性),但是双飞翼则多了一个 div 标签。
  • 圣杯布局在 IE 6/7 下需要加入 hack 解决一个布局问题,双飞翼布局则不用。

在我看来我更喜欢双飞翼布局,用一个 div 标签换来减少多个属性和一个 hack 属性(在 IE 6/7 下圣杯布局需要加入 hack,但是如果 container 触发了 BFC 则该 hack 不用加入,例如 container 使用了 clearfix 或 overflow: hidden)。圣杯布局下需要加入的 hack 是左边的列的 left 属性必须是右边列的宽度的值,在正常浏览器布局下 left 属性的值则是自身的宽度的负值,这里提一个现象,在实现圣杯布局的时候我使用了 clearfix 闭合浮动而不是圣杯布局来源文章中的使用 footer 来清除浮动,导致在 IE 6/7 下会出现的异常布局消失了,我估计是触发了 BFC 的原因(我把 clearfix 去掉后使用了 overflow: hidden 出现同样的效果)。

1.双飞翼布局

演示地址:http://www.libinhong.com/demo/Flying

CSS:

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
body {
margin: 0;
padding: 0;
text-align: center;
font-family: Microsoft YaHei;
}
.fl {
float: left;
height: 300px;
}
.container-center {
width: 100%;
}
.container-center .center {
margin: 0 200px;
height: 100%;
background-color: rgb(143, 221, 172);
}
.container .left {
width: 200px;
margin-left: -100%;
background: rgb(205, 228, 128);
}
.container .right {
width: 200px;
margin-left: -200px;
background-color: rgb(94, 152, 185);
}
.clearfix:before, .clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1;
}

HTML:

1
2
3
4
5
6
7
<div class="container clearfix">
<div class="container-center fl">
<div class="center">中间自适应</div>
</div>
<div class="left fl">左边固定200px</div>
<div class="right fl">右边固定200px</div>
</div>

2.圣杯布局

演示地址:http://www.libinhong.com/demo/HolyGrails

CSS:

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
body {
margin: 0;
padding: 0;
text-align: center;
font-family: Microsoft YaHei;
}
.fl {
float: left;
height: 300px;
position: relative;
}
.container {
padding: 0 200px 0 150px;
}
.container .center {
width: 100%;
background-color: rgb(166, 212, 121);
}
.container .left {
width: 150px;
margin-left: -100%;
left: -150px;
/**left: 200px;*/ /** for IE6/7*/*/
background-color: rgb(136, 204, 194);
}
.container .right {
width: 200px;
margin-right: -200px;
background-color: rgb(228, 215, 92);
}
.clearfix:before, .clearfix:after {
content: "";
display: table;
}
.clearfix:after {
clear: both;
}
.clearfix {
*zoom: 1;
}

HTML:

1
2
3
4
5
<div class="container clearfix">
<div class="center fl">中间自适应</div>
<div class="left fl">左边固定150px</div>
<div class="right fl">右边固定200px</div>
</div>

演示效果:

演示效果

Stay folish<br><br>Stay hungry