SegmentFault 52lidan最新的文章
2014-06-20T15:24:40+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
javascript replace方法
https://segmentfault.com/a/1190000000584042
2014-06-20T15:24:40+08:00
2014-06-20T15:24:40+08:00
52lidan
https://segmentfault.com/u/f2e
5
<p>javascript字符串方法replace还是比较强大的,做个笔记总结。</p>
<h3>第一个参数</h3>
<p>replace的第一个参数为字符串或者正则表达式。第二个参数为替换符,或者一个回调函数。</p>
<pre><code>javascript</code><code>'aadbbbd'.replace('a','1')
//"1adbbbd"
'aadbbbd'.replace(/a/,'1')
//"1adbbbd"
</code></pre>
<h3>第二个参数</h3>
<p>1.<strong>第二个参数为特殊符号</strong></p>
<p><code>$1、$2、…、$99</code>:与正则表达式中的第1~99个子表达式相匹配的文本。其实也可以在1-9之间,加上0前缀。<br>
该符号针对的是分组,所以,没有分组的话,会被当做<code>$n</code>字符来做替换。</p>
<p><code>$&</code>:(美元符号+连字符):与正则表达式相匹配的子字符串。</p>
<p>$`:(美元符号+切换技能键):位于匹配子字符串左侧的文本。</p>
<p><code>$'</code>:(美元符号+单引号):位于匹配子字符串右侧的文本。</p>
<p><code>$$</code>:表示$符号。</p>
<p>代码示例:</p>
<pre><code>javascript</code><code>'中国人民'.replace(/(中国)/g,'($1)')
//"(中国)人民"
'cdab'.replace(/(ab)/g,'$`')
//"cdcd"
'abcd'.replace(/(ab)/g,"$'")
//"cdcd"
'abcdabcd'.replace(/(ab)/g,"[$&]")
//"[ab]cd[ab]cd"
'$1$2wa,test'.replace(/[a-zA-z]/g,'$$');
//"$1$2$$,$$$$"
</code></pre>
<p>如果有10个分组,但是我想在匹配第一个分组的后面加个0,怎么办?<br>
也就是说,怎么让js去做10和1的区分?</p>
<p>用事实说话:</p>
<pre><code>javascript</code><code>'abcdefghijkkkk'.replace(/(a)/g,"$10")
//"a0bcdefghijkkkk"
'abcdabcd'.replace(/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)/g,"$10")
//"jkkkk"
</code></pre>
<p>这个有点为难了,但是却难不倒函数啊!</p>
<p>2.<strong>第二个参数为函数</strong></p>
<p>如果第二个参数传入函数,那么,正则表达式没有定义捕获组的情况下,默认为三个参数,分别为:matched(匹配子串),匹配下标,原串(originalText)。否则,第2至n个参数对应为捕获组匹配项,倒数的两个参数为:匹配下标,原串(originalText)。</p>
<p><strong>函数的匹配返回值,作为每次的匹配替换值</strong>。</p>
<pre><code>javascript</code><code>'123'.replace(/\d/g,function(m1,m2,m3){
return m3+'#'
})
//"123#123#123#"
'abcdedddabc12323abc'.replace(/[abc]/g,function(matched,index,originalText){
return matched+'~'
})
//"a~b~c~deddda~b~c~12323a~b~c~"
'abcdedddabc12323abc'.replace(/[abc]/g,function(matched,index,originalText){
return '~'
})
//"~~~deddd~~~12323~~~"
'abcdeabc'.replace(/[ab]/g,function(matched,index,originalText){
return '['+index+']';
})
//"[0][1]cde[5][6]c"
</code></pre>
<p>接下来,我们来做个题目,要求对一个串的重复部分进行替换处理,比如:<code>abcabcaabbbdd</code>,该串中<code>abc</code>紧接着<code>abc</code>,<code>a</code>后紧接着<code>a</code>,无论重复多少次,我们都用<code>#</code>替换掉。</p>
<pre><code>javascript</code><code>//因为有一个捕获组,所以,需要再传递1参数 $1
'abcaabbccccdddabcabcef'.replace(/(\w+)\1+/g,function(matched,$1,index,originalText){
return '#';
})
//"abc#####ef"
</code></pre>
<p>如果只是保留非重复部分,也就是紧连接的<code>aaa</code>,只需要保留<code>a</code>即可。那么我们可以这么改:</p>
<pre><code>javascript</code><code>'abcaabbccccdddabcabcef'.replace(/(\w+)\1+/g,function(matched,$1,index,originalText){
return $1;
})
//"abcabccdabcef"
</code></pre>
淘宝商品分类小动画细节
https://segmentfault.com/a/1190000000575112
2014-06-11T12:19:51+08:00
2014-06-11T12:19:51+08:00
52lidan
https://segmentfault.com/u/f2e
5
<p>这是<a rel="nofollow" href="%5Bhttp://list.taobao.com/browse/cat-0.htm?spm=1.7274553.1997563209.1.bmFsDm&taobao_from=6%5D">淘宝商品分类</a>的一个bar,当滚动页面时,该条就会成为fixed状态。此时,鼠标悬浮上去,就会从底部向上有个描述圆往上移动的动画,看起来不错。</p>
<p><img src="http://segmentfault.com/img/bVczK2" alt="淘宝商品分类条"></p>
<p>粗略一想,这个用css3来实现,岂不是很爽,transition只是改动top值即可。</p>
<p><strong>demo</strong>:</p>
<p><a rel="nofollow" href="http://jsfiddle.net/xmlovecss/cFur5/">http://jsfiddle.net/xmlovecss/cFur5/</a></p>
<p>但是,如果仔细观察的话,这里有个细节。淘宝的小动画,描述圆在向上移动的过程中,就好像是被同样大小的圆形遮罩住了,所以,它显得更加自然,就好像从窗子底部偷偷爬上来似的。</p>
<p>哦,好像明白了什么。查看源码,发现,淘宝用了一个图片背景,圆心是透明的,其它地方同背景色。如下图:</p>
<p><img src="http://segmentfault.com/img/bVczMh" alt="源代码截图"></p>
<p>难怪它的结构多了个shadow标签。</p>
<p>好,我们加入shadow层,并设置层叠值为最高,这样就能实现类似的效果了(css能画出来这样的图片么?)。</p>
<p>最后,我们还想给其它不支持transition属性的浏览器,使用js来实现这样的效果,怎么办捏?</p>
<pre><code class="lang-javascript"> if('transition' in document.body.style){
//support
}else {
//animateCallback
}
</code></pre>
<p>你真以为这么搞就行啦,不行。样式对我们的js动画有影响。也就是,悬浮到图标上时的样式:</p>
<pre><code class="lang-css">.box:hover>.info {
top:0;
}
</code></pre>
<p>支持这个选择器的浏览器(IE7,8,9)不支持transition。在初次悬浮时,该样式会突然把top变为0,这样就影响了js的动画,egg pain。</p>
<p><strong>demo</strong>:</p>
<p><a rel="nofollow" href="http://jsfiddle.net/xmlovecss/N7HpU/">http://jsfiddle.net/xmlovecss/N7HpU/</a></p>
<p>淘宝没有用css3的transition,只是用了js,看来这样也好。</p>
javascript 执行环境,变量对象,作用域链
https://segmentfault.com/a/1190000000533094
2014-06-04T18:36:10+08:00
2014-06-04T18:36:10+08:00
52lidan
https://segmentfault.com/u/f2e
20
<h2>前言</h2>
<p>这几天在看《javascript高级程序设计》,看到执行环境和作用域链的时候,就有些模糊了。书中还是讲的不够具体。<br>通过上网查资料,特来总结,以备回顾和修正。</p>
<p>要讲的依次为:</p>
<ul>
<li><p>EC(执行环境或者执行上下文,Execution Context)</p></li>
<li><p>ECS(执行环境栈Execution Context Stack)</p></li>
<li><p>VO(变量对象,Variable Object)|AO(活动对象,Active Object)</p></li>
<li><p>scope chain(作用域链)和[[scope]]属性</p></li>
</ul>
<p><!--more--></p>
<h2>EC</h2>
<p>每当控制器到达ECMAScript可执行代码的时候,控制器就进入了一个执行上下文(好高大上的概念啊)。</p>
<p>javascript中,EC分为三种:</p>
<ul>
<li><p>全局级别的代码 – 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的就是这个环境。</p></li>
<li><p>函数级别的代码 – 当执行一个函数时,运行函数体中的代码。</p></li>
<li><p>Eval的代码 – 在Eval函数内运行的代码。</p></li>
</ul>
<p>EC建立分为两个阶段:进入执行上下文和执行阶段。</p>
<p>1.进入上下文阶段:发生在函数调用时,但是在执行具体代码之前(比如,对函数参数进行具体化之前)<br>2.执行代码阶段:变量赋值,函数引用,执行其他代码。</p>
<p>我们可以将EC看做是一个对象。</p>
<pre><code class="javascript">EC={
VO:{/* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */},
this:{},
Scope:{ /* VO以及所有父执行上下文中的VO */}
}</code></pre>
<h2>ECS</h2>
<p>一系列活动的执行上下文从逻辑上形成一个栈。栈底总是全局上下文,栈顶是当前(活动的)执行上下文。当在不同的执行上下文间切换(退出的而进入新的执行上下文)的时候,栈会被修改(通过压栈或者退栈的形式)。</p>
<p>压栈:全局EC-->局部EC1-->局部EC2-->当前EC<br>出栈:全局EC<--局部EC1<--局部EC2<--当前EC</p>
<p>我们可以用数组的形式来表示环境栈:</p>
<pre><code class="javascript">ECS=[局部EC,全局EC];</code></pre>
<p>每次控制器进入一个函数(哪怕该函数被递归调用或者作为构造器),都会发生压栈的操作。过程类似javascript数组的push和pop操作。</p>
<p>当javascript代码文件被浏览器载入后,默认最先进入的是一个全局的执行上下文。当在全局上下文中调用执行一个函数时,程序流就进入该被调用函数内,此时引擎就会为该函数创建一个新的执行上下文,并且将其压入到执行上下文堆栈的顶部。浏览器总是执行当前在堆栈顶部的上下文,一旦执行完毕,该上下文就会从堆栈顶部被弹出,然后,进入其下的上下文执行代码。这样,堆栈中的上下文就会被依次执行并且弹出堆栈,直到回到全局的上下文。</p>
<h2>VO|AO</h2>
<h3>VO</h3>
<p>每一个EC都对应一个变量对象VO,在该EC中定义的所有变量和函数都存放在其对应的VO中。</p>
<p>VO分为全局上下文VO(全局对象,Global object,我们通常说的global对象)和函数上下文的AO。</p>
<pre><code class="javascript">VO: {
// 上下文中的数据 (变量声明(var), 函数声明(FD), 函数形参(function arguments))
}</code></pre>
<p>1.进入执行上下文时,VO的初始化过程具体如下:</p>
<ul>
<li><p>函数的形参(当进入函数执行上下文时)<br>—— 变量对象的一个属性,其属性名就是形参的名字,其值就是实参的值;对于没有传递的参数,其值为undefined</p></li>
<li><p>函数声明(FunctionDeclaration, FD) —— 变量对象的一个属性,其属性名和值都是函数对象创建出来的;如果变量对象已经包含了相同名字的属性,则替换它的值</p></li>
<li><p>变量声明(var,VariableDeclaration) —— 变量对象的一个属性,其属性名即为变量名,其值为undefined;如果变量名和已经声明的函数名或者函数的参数名相同,则不会影响已经存在的属性。</p></li>
</ul>
<p>注意:该过程是有先后顺序的。</p>
<p>2.执行代码阶段时,VO中的一些属性undefined值将会确定。</p>
<h3>AO</h3>
<blockquote><p>在函数的执行上下文中,VO是不能直接访问的。它主要扮演被称作活跃对象(activation object)(简称:AO)的角色。</p></blockquote>
<p>这句话怎么理解呢,就是当EC环境为函数时,我们访问的是AO,而不是VO。</p>
<pre><code class="javascript">VO(functionContext) === AO;</code></pre>
<p>AO是在进入函数的执行上下文时创建的,并为该对象初始化一个arguments属性,该属性的值为Arguments对象。</p>
<pre><code class="javascript">AO = {
arguments: {
callee:,
length:,
properties-indexes: //函数传参参数值
}
};</code></pre>
<p>FD的形式只能是如下这样:</p>
<pre><code class="javascript">function f(){
}</code></pre>
<h3>示例</h3>
<p><strong>VO示例</strong>:</p>
<pre><code class="javascript">alert(x); // function
var x = 10;
alert(x); // 10
x = 20;
function x() {};
alert(x); // 20</code></pre>
<p>进入执行上下文时,</p>
<pre><code class="javascript">ECObject={
VO:{
x:<reference to FunctionDeclaration "x">
}
};</code></pre>
<p>执行代码时:</p>
<pre><code class="javascript">ECObject={
VO:{
x:20 //与函数x同名,替换掉,先是10,后变成20
}
};</code></pre>
<p>对于以上的过程,我们详细解释下。</p>
<p>在进入上下文的时候,VO会被填充函数声明; 同一阶段,还有变量声明“x”,但是,正如此前提到的,变量声明是在函数声明和函数形参之后,并且,变量声明不会对已经存在的同样名字的函数声明和函数形参发生冲突。因此,在进入上下文的阶段,VO填充为如下形式:</p>
<pre><code class="javascript">VO = {};
VO['x'] = <引用了函数声明'x'>
// 发现var x = 10;
// 如果函数“x”还未定义
// 则 "x" 为undefined, 但是,在我们的例子中
// 变量声明并不会影响同名的函数值
VO['x'] = <值不受影响,仍是函数></code></pre>
<p>执行代码阶段,VO被修改如下:</p>
<pre><code class="javascript">VO['x'] = 10;
VO['x'] = 20;</code></pre>
<p>如下例子再次看到在进入上下文阶段,变量存储在VO中(因此,尽管else的代码块永远都不会执行到,而“b”却仍然在VO中)</p>
<pre><code class="javascript">
if (true) {
var a = 1;
} else {
var b = 2;
}
alert(a); // 1
alert(b); // undefined, but not "b is not defined"</code></pre>
<p><strong>AO示例</strong>:</p>
<pre><code class="javascript">function test(a, b) {
var c = 10;
function d() {}
var e = function _e() {};
(function x() {});
}
test(10); // call</code></pre>
<p>当进入<code>test(10)</code>的执行上下文时,它的AO为:</p>
<pre><code class="javascript">testEC={
AO:{
arguments:{
callee:test
length:1,
0:10
},
a:10,
c:undefined,
d:<reference to FunctionDeclaration "d">,
e:undefined
}
};</code></pre>
<p>由此可见,在建立阶段,VO除了arguments,函数的声明,以及参数被赋予了具体的属性值,其它的变量属性默认的都是undefined。函数表达式不会对VO造成影响,因此,<code>(function x() {})</code>并不会存在于VO中。</p>
<p>当执行<code>test(10)</code>时,它的AO为:</p>
<pre><code class="javascript">testEC={
AO:{
arguments:{
callee:test,
length:1,
0:10
},
a:10,
c:10,
d:<reference to FunctionDeclaration "d">,
e:<reference to FunctionDeclaration "e">
}
};</code></pre>
<p>可见,只有在这个阶段,变量属性才会被赋具体的值。</p>
<h2>作用域链</h2>
<p>在执行上下文的作用域中查找变量的过程被称为标识符解析(indentifier resolution),这个过程的实现依赖于函数内部另一个同执行上下文相关联的对象——作用域链。作用域链是一个有序链表,其包含着用以告诉JavaScript解析器一个标识符到底关联着哪一个变量的对象。而每一个执行上下文都有其自己的作用域链Scope。</p>
<p>一句话:作用域链Scope其实就是对执行上下文EC中的变量对象VO|AO有序访问的链表。能按顺序访问到VO|AO,就能访问到其中存放的变量和函数的定义。</p>
<p>Scope定义如下:</p>
<pre><code class="javascript">Scope = AO|VO + [[Scope]]</code></pre>
<p>其中,AO始终在Scope的最前端,不然为啥叫活跃对象呢。即:</p>
<pre><code class="javascript">Scope = [AO].concat([[Scope]]);</code></pre>
<p>这说明了,作用域链是在函数创建时就已经有了。</p>
<p>那么[[Scope]]是什么呢?</p>
<blockquote><p>[[Scope]]是一个包含了所有上层变量对象的分层链,它属于当前函数上下文,并在函数创建的时候,保存在函数中。</p></blockquote>
<p>[[Scope]]是在函数创建的时候保存起来的——静态的(不变的),只有一次并且一直都存在——直到函数销毁。 比方说,哪怕函数永远都不能被调用到,[[Scope]]属性也已经保存在函数对象上了。</p>
<pre><code class="javascript">var x=10;
function f1(){
var y=20;
function f2(){
return x+y;
}
}</code></pre>
<p>以上示例中,f2的[[scope]]属性可以表示如下:</p>
<pre><code class="javascript">f2.[[scope]]=[
f2OuterContext.VO
]</code></pre>
<p>而<code>f2</code>的外部EC的所有上层变量对象包括了<code>f1</code>的活跃对象f1Context.AO,再往外层的EC,就是global对象了。<br>所以,具体我们可以表示如下:</p>
<pre><code class="javascript">f2.[[scope]]=[
f1Context.AO,
globalContext.VO
]</code></pre>
<p>对于EC执行环境是函数来说,那么它的Scope表示为:</p>
<pre><code class="javascript">functionContext.Scope=functionContext.AO+function.[[scope]]</code></pre>
<p>注意,以上代码的表示,也体现了[[scope]]和Scope的差异,Scope是EC的属性,而[[scope]]则是函数的静态属性。</p>
<p>(由于AO|VO在进入执行上下文和执行代码阶段不同,所以,这里及以后Scope的表示,我们都默认为是执行代码阶段的Scope,而对于静态属性[[scope]]而言,则是在函数声明时就创建了)</p>
<p>对于以上的代码EC,我们可以给出其Scope的表示:</p>
<pre><code class="javascript">exampelEC={
Scope:[
f2Context.AO+f2.[[scope]],
f1.context.AO+f1.[[scope]],
globalContext.VO
]
}</code></pre>
<p>接下来,我们给出以上其它值的表示:</p>
<ul><li><p>globalContext.VO</p></li></ul>
<pre><code class="javascript">globalContext.VO={
x:10,
f1:<reference to FunctionDeclaration "f1">
}</code></pre>
<ul><li><p>f2Context.AO</p></li></ul>
<pre><code class="javascript">f2Context.AO={
f1Context.AO={
arguments:{
callee:f1,
length:0
},
y:20,
f2:<reference to FunctionDeclaration "f2">
}
}</code></pre>
<ul><li><p>f2.[[scope]]</p></li></ul>
<pre><code class="javascript">f2Context.AO={
f1Context.AO:{
arguments:{
callee:f1,
length:0
},
y:20,
f2:<reference to FunctionDeclaration "f2">
},
globalContext.VO:{
x:10,
f1:<reference to FunctionDeclaration "f1">
}
}</code></pre>
<ul><li><p>f1Context.AO</p></li></ul>
<pre><code class="javascript">f1Context.AO={
arguments:{
callee:f1,
length:0
},
y:20,
f2:<reference to FunctionDeclaration "f2">
}</code></pre>
<ul><li><p>f1.[[scope]](f1的所有上层EC的VO)</p></li></ul>
<pre><code class="javascript">f1.[[scope]]={
globalContext.VO:{
x:undefined,
f1:undefined
}
}</code></pre>
<p>好,我们知道,作用域链Scope呢,是用来有序访问VO|AO中的变量和函数,对于上面的示例,我们给出访问的过程:</p>
<ul><li><p>x,f1</p></li></ul>
<pre><code class="javascript">- "x"
-- f2Context.AO // not found
-- f1Context.AO // not found
-- globalContext.VO // found - 10</code></pre>
<p>f1的访问过程类似。</p>
<ul><li><p>y</p></li></ul>
<pre><code class="javascript">- "y"
-- f2Context.AO // not found
-- f1Context.AO // found -20</code></pre>
<p>我们发现,在变量和函数的访问过程,并没有涉及到[[scope]],那么[[scope]]存在的意义是什么呢?</p>
<p>这个还是看下一篇文章吧。</p>
<h2>总结</h2>
<ol>
<li><p>EC分为两个阶段,进入执行上下文和执行代码。</p></li>
<li><p>ECStack管理EC的压栈和出栈。</p></li>
<li><p>每个EC对应一个作用域链Scope,VO|AO(AO,VO只能有一个),this。</p></li>
<li><p>函数EC中的Scope在进入函数EC时创建,用来有序访问该EC对象AO中的变量和函数。</p></li>
<li><p>函数EC中的AO在进入函数EC时,确定了Arguments对象的属性;在执行函数EC时,其它变量属性具体化。</p></li>
<li><p>函数的[[scope]]属性在函数创建时就已经确定,并保持不变。</p></li>
</ol>
<h2>参考</h2>
<ul><li><p><a href="https://link.segmentfault.com/?enc=gzxDMQOJPPKJmWGLiRPKGQ%3D%3D.xyxfPihynQXWpNmvIxzKiidddYszl5zQviwXWqtOsSe%2B75lyBM%2FawsWDsO%2FNql39yja3hDVhI0Ad%2FfsiUHpIdDAO58%2FijW0GgCYxeWjsc0bksZcdO8zzJ9aHKh0nmoOGTk8j27JFborcGC3iO72PhBHSckQgvDt4QE7RK508ZH7v1WLtkKks7qzQHR3TwCTf" rel="nofollow">深入理解javascript之执行上下文(execution context)</a></p></li></ul>
javascript原型概念(一)
https://segmentfault.com/a/1190000000532556
2014-06-04T11:35:07+08:00
2014-06-04T11:35:07+08:00
52lidan
https://segmentfault.com/u/f2e
2
<p>这里,我们列出原型的几个概念,如下:</p>
<ul>
<li>prototype属性</li>
<li>[[prototype]]</li>
<li><code>__proto__</code></li>
</ul>
<p><!--more--></p>
<h2>prototype属性</h2>
<p>只要创建了一个函数,就会为该函数创建一个<code>prototype</code>属性,指向该函数的原型对象。实例对象是不会拥有该属性的。<br>默认情况下,该<code>原型对象</code>也会获得一个<code>constructor</code>属性,该属性包含一个指针,指向<code>prototype</code>属性所在的函数。</p>
<pre><code class="javascript">function Person() {}// 只是一个函数而已
Person.prototype.constructor === Person</code></pre>
<h2>[[prototype]]和<code>__proto__</code>
</h2>
<p>javascript中,不可以访问的内部属性都是用<code>[[propertyName]]</code>这种形式来表示的,比如还有枚举属性[[Enumberable]]。</p>
<p><code>[[prototype]]</code>属性只能是<strong>对象</strong>可以拥有的属性。比如实例化的对象以及<code>原型对象</code>,而不是构造函数。这个属性指向拥有其属性的对象的构造函数的原型对象。注意,此处的<code>构造函数</code>指的是使用<code>new</code>方式的构造函数。并不因为更改了原型对象上的<code>constructor</code>属性而改变。</p>
<p>比如:</p>
<pre><code class="javascript">function Person() {}
Person.prototype.constructor = {}; // 此处修改了Person原型的构造函数指向
let p = new Person();
p.__proto__ === Person.prototype; // true</code></pre>
<p><code>__proto__</code>是个啥呢,就是对<code>[[propertyName]]</code>的实现。也就是说,你可以在支持该实现的浏览器下(FF,chrome,safari),去访问对象的构造函数的原型对象。比如:</p>
<pre><code class="javascript">
var Person = function(name) {
this.name = name;
};
var p1 = new Person();
p1.__proto__=== Person.prototype; // true
Person.prototype = {};
var p2 = new Person();
p2.__proto__ === Object.prototype; // false
</code></pre>
<p>当然,<code>__proto__</code>只是浏览器的私有实现,目前ECMAScript标准实现方法是<code>Object.getPrototypeOf(object)</code>。</p>
<pre><code class="javascript">
var Person = function(name) {
this.name = name;
};
var p1 = new Person();
Object.getPrototypeOf(p1) === Person.prototype; // true
Person.prototype = {};
var p2 = new Person();
Object.getPrototypeOf(p2) === Object.prototype; // false
</code></pre>
<p>另外一种判断实例对象和其原型对象存在指向关系(由实例的[[prototype]]指向其构造函数的原型对象)的方法是:<code>isPrototypeOf</code>。比如:</p>
<pre><code class="javascript">Person.prototype.isPrototypeOf(p1); // true</code></pre>
<p>由于函数和<code>原型对象</code>也是一个对象,所以,它自然而然也拥有<code>[[prototype]]</code>属性。</p>
<p>弄清楚了这些概念,原型链,继承等存在的一些问题,都不是问题了。</p>
<p><img src="/img/bVbcBND?w=1088&h=1202" alt="clipboard.png" title="clipboard.png"></p>
jQuery deffered和promise对象方法
https://segmentfault.com/a/1190000000523676
2014-05-29T18:24:52+08:00
2014-05-29T18:24:52+08:00
52lidan
https://segmentfault.com/u/f2e
5
<h2>一、前言</h2>
<p>为了让前端们从回调的地狱中回到天堂,jQuery也引入了Promise的概念。Promise是一种令代码异步行为更加优雅的抽象,有了它,我们就可以像写同步代码一样去写异步代码。jQuery从1.5版本开始实现了<code>CommonJS</code> <a rel="nofollow" href="http://wiki.commonjs.org/wiki/Promises/A">Promise/A</a>规范这一重量级方案,不过没有严格按照规范进行实现,有一些API上的差异。</p>
<p>好,让我们来看看他们的特性吧(<strong>本文示例基于jQuery 1.8版本以上</strong>)。</p>
<h2>二、示例</h2>
<p>以前写动画时,我们通常是这么干的:</p>
<pre><code class="lang-javascript">$('.animateEle').animate({
opacity:'.5'
}, 4000,function(){
$('.animateEle2').animate({
width:'100px'
},2000,function(){
// 这样太伤了
$('.animateEle3').animate({
height:'0'
},2000);
});
});
</code></pre>
<p>假如这么使用回调的话,那就太伤了。幸好,还有一些现成的Promise解决方案来优雅地解决这种问题。</p>
<p>我们看看jQuery提供的解决办法。</p>
<pre><code class="lang-javascript">var animate1 = function() {
return $('.animateEle1').animate({opacity:'.5'},4000).promise();
};
var animate2 = function() {
return $('.animateEle2').animate({width:'100px'},2000).promise();
};
var animate3 = function(){
return $('.animateEle3').animate({height:'0'},2000).promise();
};
// so easy,有木有,so clear,有木有
$.when(animate1()).then(animate2).then(animate3);
</code></pre>
<p>很明显,更改后的代码更易懂易读了。</p>
<p>但是,上面的代码,有些细节的东西并没有透露,一不小心,就容易出错,得不到我们想要的顺序完成动画的效果。下面让我们来全面理解jQuery提供的Promise和<code>deferred</code>对象的方法,看看到底如何使用。</p>
<h2>三、promise和deffered对象方法</h2>
<p>Promise对象其实就是<code>deferred</code>对象的特例,因为Promise对象不能更改异步状态,而<code>deferred</code>对象可以。这点在他们的方法设计上,有着明显的体现。</p>
<h3>1.promise对象方法</h3>
<p>通常,对于DOM,动画,ajax相关方法,我们都可以使用Promise方法。调用Promise方法,返回的是Promise对象。可以链式调用Promise方法。</p>
<p><strong>promise对象常见的方法有三个</strong>:<code>done</code>,<code>fail</code>,<code>then</code>。</p>
<p>其它的方法就不要去记了,jQuery这里的接口方法太多了,在我看来挺啰嗦的,就跟早期的事件方法绑定一样,<code>live</code>,<code>delegate</code>,<code>bind</code>,最终不是都归为<code>on</code>来管了么。</p>
<p>代码示例,如下:</p>
<p>1.DOM使用Promise方法:</p>
<pre><code class="lang-javascript">var box=$('#box');
box.promise().done(function(ele){
console.log(ele);//jQuery box
});
</code></pre>
<p>2.Ajax使用Promise方法(默认返回一个Promise对象,所以可以不必显式调用Promise方法):</p>
<pre><code class="lang-javascript">$.post('/',{}).done(function(data){
console.log('请求成功');
}).fail(function(){
console.log('请求错误');
});
</code></pre>
<p>动画示例已有,就不重复列出了。</p>
<h3>2.deferred对象方法</h3>
<p>对于<code>deferred</code>对象呢,也就是使用<code>$.Deferred()</code>方法,以及<code>$.when()</code>等方法创造出来的对象,有如下的常用方法:</p>
<ul>
<li>
<code>resolve</code>,<code>reject</code>,<code>notify</code>;</li>
<li>
<code>done</code>,<code>fail</code>,<code>progress</code>;</li>
</ul>
<p>另外还有<code>promise</code>、<code>then</code>和<code>always</code>方法。</p>
<p>之所以这么排版,是因为他们是对应的,也就是说:<code>resolve</code>方法会触发<code>done</code>的回调执行,<code>reject</code>会触发<code>fail</code>的回调,<code>notify</code>会触发<code>progress</code>的回调。</p>
<p>直接看代码:</p>
<pre><code class="lang-javascript">var wait = function(ms) {
var dtd = $.Deferred();
setTimeout(dtd.resolve, ms);
// setTimeout(dtd.reject, ms);
// setTimeout(dtd.notify, ms);
return dtd.promise(); //此处也可以直接返回dtd
};
wait(2500).done(function() {
console.log('haha,师太,你可让老衲久等了');
}).fail(function() {
console.log('失败了');
}).progress(function(res) {
console.log('等待中...');
});
</code></pre>
<p>我们看到了,上面的代码中,在<code>wait</code>函数中,返回的是个Promise对象,而不是<code>deferred</code>对象。</p>
<p>要知道,Promise对象是没有<code>resolve</code>,<code>reject</code>,<code>notify</code>等方法的,也就意味着,你无法针对Promise对象进行状态更改,只能在<code>done</code>或<code>fail</code>中进行回调配置。所以,你如果这么调用<code>wait(2500).resolve()</code>将会报错,因为<code>wait(2500)</code>返回的是个Promise对象,不存在<code>resolve</code>方法。</p>
<p>但是,这么做,有个好处,我们把<code>dtd</code>这个<code>deferred</code>对象放在了<code>wait</code>函数中,作为了局部变量,避免了全局的污染;进一步通过Promise方法,转化<code>dtd</code>这个<code>deferred</code>对象为Promise对象,避免了函数<code>wait</code>外部可能发生的状态更改(假如我们确实有这个需求)。</p>
<p>比如:</p>
<pre><code class="lang-javascript">var wait = function(ms) {
var dtd = $.Deferred();
setTimeout(dtd.resolve, ms);
// setTimeout(dtd.reject, ms);
// setTimeout(dtd.notify, ms);
return dtd; //此处也可以直接返回dtd
};
wait(2500).reject().fail(function(){
console.log('失败了...............');
});
</code></pre>
<p>我们在外部更改了<code>wait</code>返回的<code>deferred</code>对象的状态,这样必然触发该对象的<code>fail</code>回调函数。</p>
<p>对于<code>always</code>方法,从字面意思上就很容易理解,<code>deferred</code>对象无论是<code>resolve</code>还是<code>reject</code>,都会触发该方法的回调。</p>
<h3>3.其它共性</h3>
<p>此处讲讲<code>then</code>和<code>$.when</code>方法的使用。它们对Promise对象也适用。</p>
<ul>
<li>
<code>$.when</code>方法接受多个<code>deferred</code>对象或者纯javascript对象,返回Promise对象。</li>
<li>
<code>then</code>方法依次接受三个回调,分别为<code>deferred</code>对象<code>resolve</code>,<code>reject</code>,<code>notify</code>后触发的回调,返回一个Promise对象。注意,必须传入函数,而该函数只有返回一个Promise对象,才能够让异步事件按照预期顺序来执行。</li>
</ul>
<p>我们来看看最开始的动画示例代码,<code>$.when(animate1()).then(animate2).then(animate3)</code>,<code>$.when</code>方法中接受了一个<code>animate1</code>的函数执行结果,也就是得到了一个Promise对象,而后的<code>then</code>中,则只是接受了一个变量名,这样得到的结果是一个匿名的函数体,而该函数中返回的是Promise对象。正好符合了我们对<code>then</code>接受参数的要求。</p>
<p>假如我们把执行语句改成:<code>$.when(animate1()).then(animate2()).then(animate3())</code>,这样造成的结果就是三个动画同步执行了。与<code>$.when(animate1(),animate2(),animate3())</code>无异。</p>
<p>既然<code>then</code>是如此要求,那么与<code>then</code>方法类似的<code>done</code>,<code>fail</code>,<code>progress</code>也是一样的。</p>
<h2>四、参考文章</h2>
<p>因为jQuery deffered和promise对象方法使用起来比较繁琐,接口太多,同样一件事儿,你可以有好几种写法。所以,某些接口方法可能会被废弃。若要使用其它方法,请去官网参考。</p>
<ul>
<li><a rel="nofollow" href="http://blog.allenm.me/2012/01/jquery_deferred_promise_method/">jQuery deferred 对象的 promise 方法</a></li>
<li><a rel="nofollow" href="http://chauvetxiao.com/bo-blog/read.php?33">Promise对象和Deferred对象</a></li>
</ul>
javascript 类数组
https://segmentfault.com/a/1190000000415572
2014-02-21T17:13:24+08:00
2014-02-21T17:13:24+08:00
52lidan
https://segmentfault.com/u/f2e
10
<p>在线的<a href="https://link.segmentfault.com/?enc=PSP6mhu4DKLhZQ2nqPPrSA%3D%3D.8Ad14jnbpeqZoWrXhWKM0YZYbjhvOVZMN6aBd1O8cHtLasp3xRCwLUzvi85BGeqkzDaV%2Bgvp0GvlwdP3xM7SSzp%2B5UzNHZ3M18wktJsmyQT%2BRc8zk8crLRKav5P9nmPlgzuzwtbjlLa7WsxaqF15Zg%3D%3D" rel="nofollow">《javascript权威指南》</a>有对该概念的解释。</p>
<p>那么,什么是javascript 类数组呢?</p>
<h2>定义:</h2>
<ul>
<li>拥有<code>length</code>属性,<code>length-0</code>可隐式转换为number类型,并且不大于<code>Math.pow(2,32)</code>(比如:<code>22.33</code>和<code>'022'</code>都满足条件)</li>
<li><del>不具有数组所具有的方法</del></li>
</ul>
<h3>类数组示例:</h3>
<pre><code class="javascript">var a = {'1':'gg','2':'love','4':'meimei',length:5};
Array.prototype.join.call(a,'+');//'+gg+love++meimei'</code></pre>
<h3>非类数组示例:</h3>
<pre><code class="javascript">var c = {'1':2};</code></pre>
<p>没有<code>length</code>属性,所以就不是类数组。</p>
<p>javascript中常见的类数组有<code>arguments</code>对象和DOM方法的返回结果。<br>比如 <code>document.getElementsByTagName()</code>。</p>
<h2>类数组判断</h2>
<p>《javascript权威指南》上给出了代码用来判断一个对象是否属于“类数组”。如下:</p>
<pre><code class="javascript">// Determine if o is an array-like object.
// Strings and functions have numeric length properties, but are
// excluded by the typeof test. In client-side JavaScript, DOM text
// nodes have a numeric length property, and may need to be excluded
// with an additional o.nodeType != 3 test.
function isArrayLike(o) {
if (o && // o is not null, undefined, etc.
typeof o === 'object' && // o is an object
isFinite(o.length) && // o.length is a finite number
o.length >= 0 && // o.length is non-negative
o.length===Math.floor(o.length) && // o.length is an integer
o.length < 4294967296) // o.length < 2^32
return true; // Then o is array-like
else
return false; // Otherwise it is not
}</code></pre>
<p>书上给的示例代码判断条件过于严苛,比如以下情形的类数组就无法通过这段代码的校验:</p>
<pre><code class="javascript">var arrLike1 = { length: 1.2 };
var arrLike2 = { length: -10 };
var arrLike3 = { length: '10' };</code></pre>
<p>当然,除却人为“造作”因素,正常的<code>length</code>应当是正整数。</p>
<p>我们可以对照一下:<a href="https://link.segmentfault.com/?enc=HRFJJq35FLtA9eFCWEVPLg%3D%3D.Es0WlpOn0m7fBfWfr7qlTCzEfmOHzWrLq%2BwZ9A%2F6IGxP07l7OlxrA85aciTQO8yNcflzn7oeg%2BpvSwCHueWxSZJBJFtfg%2FVsi9W5rwDLO5ezNCc0UbijbT%2Fi2GfmHyAQt2iuxV%2Fsm%2Bx0%2BZEFY7ulOQ%3D%3D" rel="nofollow">MDN Array.from 的polyfill</a> , 这个方法中对<code>length</code>的判断可以看一下。</p>
<pre><code class="javascript">var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1; //此处length应当为Math.pow(2, 32) - 1
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};</code></pre>
<h2>类数组表现</h2>
<p>之所以成为“类数组”,就是因为和“数组”类似。不能直接使用数组方法,但你可以像使用数组那样,使用类数组。</p>
<pre><code class="javascript">var a = {'0':'a', '1':'b', '2':'c', length:3}; // An array-like object
Array.prototype.join.call(a, '+'); // => 'a+b+c'
Array.prototype.slice.call(a, 0); // => ['a','b','c']: true array copy
Array.prototype.map.call(a, function(x) {
return x.toUpperCase();
}); // => ['A','B','C']:</code></pre>
<h2>类数组对象转化为数组</h2>
<p>有时候处理类数组对象的最好方法是将其转化为数组。<br>有两种实现方法:</p>
<h3>1.数组<code>slice</code>方法借用</h3>
<pre><code class="javascript">Array.prototype.slice.call(arrLike)</code></pre>
<h3>2.Array.from</h3>
<pre><code class="javascript">Array.from(arrLike)</code></pre>
<p>然后就可以直接使用数组方法啦。</p>
<pre><code class="javascript">var a = { '0': 1, '1': 2, '2': 3, length: 3 };
var arr = Array.prototype.slice.call(a); //arr = [ 1, 2, 3 ]</code></pre>
<p><del><strong>ps</strong>: 两个处理方法存在细节差异,比如处理<code>{length: 1}</code>这个对象时,结果就不一样,<code>Array.from</code>处理结果是长度为1并且填充值为<code>undefined</code>,而<code>Array.prototype.slice</code>处理结果则相同于<code>new Array(1)</code>。</del></p>
<p><del>对于IE9以前的版本(DOM实现基于COM),我们可以使用<code>makeArray</code>来实现。</del></p>
<pre><code class="javascript">// 伪数组转化成数组
var makeArray = function(obj) {
if (!obj || obj.length === 0) {
return [];
}
// 非伪类对象,直接返回最好
if (!obj.length) {
return obj;
}
// 针对IE8以前 DOM的COM实现
try {
return [].slice.call(obj);
} catch (e) {
var i = 0,
j = obj.length,
res = [];
for (; i < j; i++) {
res.push(obj[i]);
}
return res;
}
};</code></pre>
<h2>参考:</h2>
<p>1.<a href="https://link.segmentfault.com/?enc=z0dmHyTa2p9bPGjbh4TEJQ%3D%3D.MlNmY0q5vH74TVuzps%2FHuKMALRozK03ZuIX3d%2Bngw%2BvLaiQKcMMgm4sIPJNdB55Av3GlH0oBWXbkHF8W2X%2FOrpjquyBEa9nlYODRVUyfOTIgILHTnP7UtQhmYIsZ0MTnAGPiLBnXwsIqqOHnZgjYPg%3D%3D" rel="nofollow">https://www.inkling.com/read/...</a><br>2.<a href="https://link.segmentfault.com/?enc=3v07oKBOqd3UqpdQ%2F%2BYFCQ%3D%3D.OSZFHCRjXaKg4Zbmf5jGRSUg4xlIxAUbR6Jbicoq3UerRDWWWRSxqZiLwpL9IrHG" rel="nofollow">JavaScript 的怪癖 8:“类数组对象”</a></p>
gruntfile文件基本配置(脚手架)
https://segmentfault.com/a/1190000000413194
2014-02-19T17:07:09+08:00
2014-02-19T17:07:09+08:00
52lidan
https://segmentfault.com/u/f2e
0
<h2>一、入口函数</h2>
<pre><code>javascript</code><code>module.exports = function(grunt){
//在这里配置你的grunt任务
}
</code></pre>
<h2>二、packagjson导入</h2>
<pre><code>javascript</code><code>module.exports = function(grunt){
grunt.initConfig({
pkg: grunt.file.readJSON('package.json')
});
}
</code></pre>
<h2>三、任务配置</h2>
<p>grunt的任何插件,配置接口形式都是一致的。都是在任务对象中包含有options配置对象和打包的配置。比如:</p>
<pre><code>javascript</code><code>concat: {
options: {
},
dist: {
}
}
</code></pre>
<p>其中<code>options</code>对象中是改任务插件提供的配置参数,具体设置需要参考对应插件文档。</p>
<h3>多目标配置</h3>
<p>如果要实现一个任务,多个目标的配置呢?</p>
<pre><code>javascript</code><code>concat: {
foo: {
// 这里是concat任务'foo'目标的选项和文件
options:{},
dist:{}
},
bar: {
// 这里是concat任务'bar'目标的选择和文件
options:{},
dist:{}
}
}
</code></pre>
<p>指定一个像<code>grunt concat:foo</code>或者<code>grunt concat:bar</code>的任务和目标只会处理指定的任务目标配置,而运行<code>grunt concat</code>将遍历所有的(定义在concat任务中的)目标并依次处理。注意,如果一个任务使用<code>grunt.renameTask</code>重命名过,Grunt将在配置对象中查找新的任务名称属性。</p>
<p><strong>注意,不是所有的grunt 任务模块都支持多目标任务配置,所以,请看对应模块的官方文档</strong></p>
<h3>options配置</h3>
<p>同一任务的<code>options</code>配置存在全局配置和局部配置。局部配置可以覆盖全局配置。</p>
<pre><code>javascript</code><code>grunt.initConfig({
concat: {
options: {
// 这里是任务级的Options,覆盖任务的默认值
},
foo: {
options: {
// 这里是'foo'目标的options,它会覆盖任务级的options.
}
},
bar: {
// 没有指定options,这个目标将使用任务级的options
}
}
});
</code></pre>
<h3>简洁文件配置</h3>
<p>1.单独任务</p>
<p>a.单个任务(concat)单个目标(bar)单个src-dest映射</p>
<pre><code>javascript</code><code>concat: {
bar: {
src: ['src/bb.js', 'src/bbb.js'],
dest: 'dest/b.js'
}
}
</code></pre>
<p>b.单个任务单个目标,多个src-dest映射,需要用到特定的<code>files</code>数组来包裹多个src-dest 对象</p>
<pre><code>javascript</code><code>concat: {
files: [{
src: ['src/aa.js', 'src/aaa.js'],
dest: ['src/a.js']
}, {
src: ['src/bb.js', 'src/bbb.js'],
dest: ['src/b.js']
}]
}
</code></pre>
<p>2.多个任务配置</p>
<p>a.多任务,单目标配置</p>
<pre><code>javascript</code><code>concat: {
bar: {
src: ['src/bb.js', 'src/bbb.js'],
dest: 'dest/b.js'
}
},
uglify: {
bar: {
src: ['dest/b.js'],
dest: 'dest/b.min.js'
}
}
</code></pre>
<p>b.多任务,多个目标配置(略...)</p>
<h3>动态文件对象构建</h3>
<p>如果想针对某个文件夹下面的所有子目录及某种格式文件进行任务处理,那么,就不必如同以上那种指定文件名来进行构建了,这里就需要动态构建。</p>
<pre><code>javascript</code><code>cssmin: {
files: [{
expand: true,
cwd: './dev/css/',//文件目标位置
src: ['**/*.css', '!*.min.css'], //文件匹配
dest: './build/css/', //打包到build的css目录中
ext: '.css' //扩展格式
}]
}
</code></pre>
<h2>四、任务安装和注册</h2>
<h3>模块安装</h3>
<p>模块安装使用<code>grunt.loadNpmTasks(taskName)</code>方法,比如安装<code>grunt-contrib-concat</code>,就这么调用:</p>
<pre><code>javascript</code><code>grunt.loadNpmTasks('grunt-contrib-concat')
</code></pre>
<h3>任务注册</h3>
<p>grunt默认使用<code>default</code>作为任务名。例如:</p>
<pre><code>javascript</code><code>grunt.registerTask('default', ['requirejs', 'concat', 'csscomb', 'imagemin']);
</code></pre>
<p>当我执行<code>grunt</code>命令时,就会把'requirejs', 'concat', 'csscomb', 'imagemin'这些任务全部执行。</p>
<p>当然,你也可以把'default'改成其他名称,比如'mytask'。</p>
<h3>任务执行</h3>
<p>若是你应经使用<code>npm</code>安装了任务需要的模块到本地(或者全局),那么直接执行<code>grunt yourTaskName</code>命令即可。</p>
<p>否则,你需要先执行<code>npm install</code>命令,这样会将<code>package.json</code>中的模块安装到当前开发区,然后再执行grunt命令。</p>
<p>也可以初始建立一个空的<code>package.json</code>文件,然后安装想要的模块,<code>package.json</code>会自动更新你已经安装的模块的配置信息。</p>
<p>执行顺序依照<code>grunt.registerTask</code>中的参数数组中指定的顺序。</p>
<h2>五、参考资料:</h2>
<p><a rel="nofollow" href="http://www.gruntjs.org/article/configuring_tasks.html">http://www.gruntjs.org/article/configuring_tasks.html</a></p>
前端优化:RequireJS Optimizer 的使用和配置方法(二)
https://segmentfault.com/a/1190000000395435
2014-01-22T15:53:08+08:00
2014-01-22T15:53:08+08:00
52lidan
https://segmentfault.com/u/f2e
2
<h2>前言</h2>
<p>上一篇文章粗略介绍了<code>r.js</code>的使用和示例。但是,仔细的人就会发现,<code>build.js</code>配置太尼玛简单了,有没有问题啊?</p>
<p>有问题,哈哈,当我在正式的开发中准备下手使用时,发现了问题。</p>
<p>问题类似于此:<a rel="nofollow" href="https://github.com/jrburke/requirejs/issues/308">r.js打包问题</a></p>
<h2>参数说明</h2>
<p><img src="http://segmentfault.com/img/bVbO4V" alt="参数说明"></p>
<p>有一点疑问,<code>appDir</code>究竟是指的哪个根目录呢?我把<code>r.js</code>和<code>build.js</code>放在同一级,个人猜测是相对于<code>r.js</code>的目录而言,和<code>Gruntfile.js</code>是一个道理。</p>
<p>如果你要在<code>grunt</code>中使用的话,可以使用<code>grunt-contrib-requirejs</code>插件。</p>
<h2>buld.js配置</h2>
<p>问题就在于,<code>js</code>中的<code>require</code> 配置,<code>r.js</code>并不管。所以,打包就会失败。</p>
<p>解决办法就是在<code>build.js</code>中进行重定义。</p>
<p>注意到,<code>build.js</code>中有个参数<code>mainConfigFile</code>,这个就是用来进行重新定义模块的配置js文件。</p>
<p>当然,我们照样可以不用它,直接写到<code>build.js</code>中,参考我项目中的一个配置:</p>
<pre><code class="lang-javascript">({
appDir: "./",
baseUrl: "js",
dir: "../build",
paths: {
'jquery':'libs/jquery-1.8.2',
'easyDialog':'utils/easyDialog',
'easySwitch':'utils/easySwitch',
'easyValidator':'utils/easyValidator',
'miniNotification':'utils/miniNotification',
'scoreToRank':'utils/scoreToRank',
'score-intro':'app/score-intro',
'convert-center':'app/convert-center'
},
shim:{
'easyDialog': ['jquery'],
'easySwitch':['jquery'],
'easyValidator':['jquery'],
'miniNotification':['jquery']
},
modules: [{
name: 'score-intro'
},{
name: 'convert-center'
}]
})
</code></pre>
<p><code>libs</code>下面的都是公用js,<code>utils</code>文件下是插件及其它模块,<code>app</code>下是启动文件,他们都会依赖于<code>libs</code>和<code>utils</code>中的某个文件。</p>
<p>这样呢,打包就成功了。</p>
<p><em>注意:</em>paths和shim中,文件名称保持和写在js中的require.config中的名称保持一致。</p>
<pre><code class="lang-javascript">require.config({
baseUrl: 'js/',
paths: {
'jquery': 'libs/jquery-1.8.2',
'scoreToRank': 'utils/scoreToRank'
}
});
</code></pre>
<p>这样,运行命令<code>node r.js -o build.js</code>,<code>app</code>文件中的js依赖全都拼合了进来。</p>
<p>ps:有疑问及其它问题,请联系本人。</p>
<h2>注意事项:</h2>
<p>使用grunt的童鞋,可以使用<code>grunt-contrib-requirejs</code>。</p>
<p>1.css的压缩,不需要再使用<code>grunt-contrib-cssmin</code>,否则会有打包bug,<code>grunt-contrib-rquirejs</code>可以压缩css;</p>
<p>2.若要合并<code>rquier.js</code>和其配置文件,一旦更改了配置文件(目前bug是删除了某个js模块),也要更改合并的文件,否则,打包就会报错。(没道理啊,配置文件更改了,合并模块<code>concat</code>竟然没有检查更新机制么?)</p>
<p><a rel="nofollow" href="https://github.com/xiaomingming/requirejs-package">项目示例</a></p>
<h2>参考:</h2>
<p><a rel="nofollow" href="http://nomospace.github.io/posts/r.js-example.build.js.html">r.js 配置文件 example.build.js 不完整注释</a></p>
前端优化:RequireJS Optimizer 的使用和配置方法(一)
https://segmentfault.com/a/1190000000394849
2014-01-22T11:46:44+08:00
2014-01-22T11:46:44+08:00
52lidan
https://segmentfault.com/u/f2e
5
<h2>前言</h2>
<p>前端<code>javascript</code>文件越来越多了,依赖加载,文件合并的问题也就随之出现。好在有基于<code>AMD</code>规范的<code>requirejs</code>和国产基于<code>CMD</code>规范的<code>seajs</code>可以管理依赖。但是,使用这样的js来管理js模块的依赖,就会导致页面js文件请求就会变多,为了减少文件请求,一般的文件拼合工具就不起作用了,比如<code>grunt</code>的<code>concat</code>工具。</p>
<p>好在<code>requirejs</code>有<code>r.js</code>来解决这个问题。而且使用也简单,容易上手。</p>
<p>PS:之所以没选择优秀的<code>seajs</code>,是因为<code>spm</code>打包工具实在是难以上手。</p>
<h2>开始上手</h2>
<p>在开始之前,我们假定你已经掌握了<code>requirejs</code>的使用,若是不懂,可以看看阮一峰老师的<a href="https://link.segmentfault.com/?enc=x1Tdv3rNVKQEVoZVwdlqew%3D%3D.EtZOWjdTpXnVnsrijdMIxljhegDF4RTDYpd0VP8%2FMz1vNUxG5RVBepmmMNG9JU9lPx9qX0tM59C3aez9szqkQg%3D%3D" rel="nofollow">相关文章</a>,写得很好。另外,你需要有<code>node</code>环境和<code>git</code>,那么这些条件都准备充足了,神马都好办了。</p>
<p>尽管官方英文文档也有API,但是,写得比较啰嗦,在命令行敲配置,实在是很二逼的做法啊!想不通为何不直接写使用<code>build.js</code>来进行任务配置。</p>
<p>好,先看看我自己建立的测试文档,目录如下图:</p>
<p><img src="http://segmentfault.com/img/bVbOTb" alt="demo目录" title="demo目录"></p>
<p>目录中,<code>r.js</code>和<code>require.js</code>两者缺一不可。</p>
<p>abc 代码如下:</p>
<p><strong>a.js:</strong></p>
<pre><code class="javascript">define({
color:'red'
});</code></pre>
<p><strong>b.js:</strong></p>
<pre><code class="javascript">require.config({
baseUrl: 'js'
});
define(['a'], function(a) {
console.log('b.js:' + a.color);
return {
color: a.color,
width: '120px'
};
});</code></pre>
<p><strong>c.js:</strong></p>
<pre><code class="javascript">require.config({
baseUrl:'js'
});
define(['b'],function(b){
console.log('run c.js :'+b.color+','+b.width);
});</code></pre>
<p>从代码可以看出,<code>b.js</code>依赖于<code>a.js</code>,<code>c.js</code>依赖于<code>a.js</code>和<code>b.js</code>。作为示例,这里写得非常简单。</p>
<p>好了,下面看看<code>build.js</code>的配置,为了带来实验的成就感,这里的配置非常简单,<code>r.js</code> <code>github</code>有更详细的配置,此处不详细描述了。</p>
<pre><code class="javascript">({
appDir: './',
baseUrl: 'js',
dir: '../r6-built',
paths: {
jquery: 'empty:'
},
modules: [
{
name: 'b'
},
{
name: 'c'
},
]
})</code></pre>
<p>配置完成后,<code>b.html</code>和<code>c.html</code>分别则是对<code>b.js</code>和<code>c.js</code>的引入。这个我就不用贴代码了。</p>
<h2>运行</h2>
<p>进入目录,命令行输入:<code>node r.js -o build.js</code>,'-o'是自动优化的意思。</p>
<p><img src="http://segmentfault.com/img/bVbOUs" alt="demo截图" title="demo截图"></p>
<p>尼玛,报错了。没找到<code>r.js</code>。所以,要运行任务,目录得先找对。</p>
<p>重来,进入 <code>r.js</code> 和 <code>build.js</code> 对应的目录(这两个js最好放在同一目录位置),并运行。</p>
<p><img src="http://segmentfault.com/img/bVbO5b" alt="demo截图" title="demo截图"></p>
<p>运行成功,有木有啊!太哈皮了,然后看看发生了什么。</p>
<p><img src="http://segmentfault.com/img/bVbOUA" alt="成功demo" title="成功demo"></p>
<p>啊啊啊<del>~,嗯嗯</del>~,不错呢。和<code>r6</code>同一级生成了一个<code>r6-build</code>目录,该目录下的文件名和<code>r6</code>下的文件名一致。但是内容肯定发生了变化。</p>
<p>除了<code>css</code>被去掉了注释之外(<code>r.js</code>也是解决<code>css</code>文件<code>@import</code>的工具,<code>@import</code>可以实现<code>css</code>的依赖,而<code>r.js</code>可以合并<code>@import</code>导入的文件,这点,<code>less</code>也可以做到,但是对于不喜欢使用<code>less</code>的童靴来说,<code>r.js</code>则是不错的选择),所有的<code>js</code>也被压缩了。而我们最为关注的<code>b.js</code>和<code>c.js</code>代码中,是否已经是把依赖拼合好的代码呢?</p>
<p>看一看吧(因为代码被压缩成了一行,为了好看,所以我使用<code>sublime jsformat</code>格式化了一下代码):</p>
<p><strong>b.js:</strong></p>
<pre><code class="javascript">define("a", {
color: "red"
}), require.config({
baseUrl: "js"
}), define("b", ["a"], function(a) {
return console.log("b.js:" + a.color), {
color: a.color,
width: "120px"
}
})</code></pre>
<p><strong>c.js:</strong></p>
<pre><code class="javascript">define("a", {
color: "red"
}), require.config({
baseUrl: "js"
}), define("b", ["a"], function(a) {
return console.log("b.js:" + a.color), {
color: a.color,
width: "120px"
}
}), require.config({
baseUrl: "js"
}), define("c", ["b"], function(a) {
console.log("run c.js :" + a.color + "," + a.width)
})</code></pre>
<p>尼玛哟,除了正确把依赖文件代码拼合好了,连名字都起好了(define的第一个参数),省去了手动的麻烦。</p>
<p>再来看看r6文件下的c.html和r6-build下的c.html差别:</p>
<p>r6/c.html 截图:</p>
<p><img src="http://segmentfault.com/img/bVbOUX" alt="r6 c.html demo" title="r6 c.html demo"></p>
<p>r6-build/c.html 截图:</p>
<p><img src="http://segmentfault.com/img/bVbOU4" alt="r6-rebuild c.html demo" title="r6-rebuild c.html demo"></p>
<p>很明显,使用了<code>r.js</code>优化后,页面中只有<code>require.js</code>和<code>c.js</code>了,而<code>c.js</code>就是依赖拼合后的文件。</p>
<h2>结语:</h2>
<p>使用了<code>r.js</code>后,模块化的文件请求就不是问题了。当然,我给的示例很简单,打包没有发现bug吧?</p>
<p>哈哈哈,<a href="http://blog.segmentfault.com/f2e/1190000000395435">下一篇</a>给出正确的<code>build.js</code>配置。</p>
<p>ps:以上仅仅是个简单的开始,<code>grunt</code>工具也有<code>requirejs</code>优化,先写到这里,我会继续完善。</p>
<p>更新:关于<code>gulp</code>的<code>requirejs</code>构建,请参考:<a href="https://link.segmentfault.com/?enc=0e2X4uVmxtrAUS8CGbA4rw%3D%3D.NXtdbj5BxYw7vCDqPsRjkQnC0UxvPCrTlUILpJWb1PcKPqYU4Z3cQPbNPZVwx95etdPYe7WPszMXEPI4FyvW0A%3D%3D" rel="nofollow">https://github.com/phated/req...</a>,会使用r.js的配置,那么这个也很容易使用了。在<code>gulpfile</code>中,我们只需要引入<code>requirejs</code>的node模块,然后把<code>requirejs</code>的options静态对象配置包裹到<code>optimize</code>方法中即可。</p>
<p>若有疑问,请留言联系我。<br>参考:</p>
<ul>
<li><p><a href="https://link.segmentfault.com/?enc=9yhUSV%2BLE%2BlUiqz0hivf9A%3D%3D.ldUW17YxGDCpawvXHN3O5bxjJyGJPvt668Pqpc8fnVh3kn%2F73L8kCaHAU9WkxaD0" rel="nofollow">requirejs进阶优化三</a></p></li>
<li><p><a href="https://link.segmentfault.com/?enc=72YUofEz%2FM1Texb1wqWIyA%3D%3D.15nxmpsOAzhW9FJrcZEkZKz%2BP2oABOH49jY5EPaMe%2BdKWnyj0jwyvRHBoagk7Ma6rJWF%2Bi94VhekmnQmM8QExg%3D%3D" rel="nofollow">使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript</a></p></li>
</ul>
学习使用sublime插件sublimeLinter
https://segmentfault.com/a/1190000000389188
2014-01-16T12:32:13+08:00
2014-01-16T12:32:13+08:00
52lidan
https://segmentfault.com/u/f2e
3
<h2>前言</h2>
<p>干啥事情都得学会偷懒,code也是如此。尽管<strong>grunt</strong>工具提供了<strong>jshint</strong>对js代码作检查的插件,但是,这就有点后知后觉了。如何在你code时就将错误给锁定并消灭呢?</p>
<p>还好,使用<strong>submlime</strong>的童鞋就有福了。<strong>sublimeLinter</strong>就是这样一个提供代码检测的工具。</p>
<h2>检测语言列表</h2>
<p><strong>支持的语言截图:</strong><br><img src="http://segmentfault.com/img/bVbNqJ" alt="语言检测列表"></p>
<h2>安装sublimeLinter包</h2>
<p>安装package我就不多说了了,用过<strong>sublime</strong>的筒子们都知道两种方法。使用<strong>sublime</strong>控制台装不了就从github上直接安装吧。地址是:<a rel="nofollow" href="https://github.com/SublimeLinter/SublimeLinter">https://github.com/SublimeLinter/SublimeLinter</a></p>
<p>安装好后,还要有node环境,这样才能检测。没安装node的就安装node吧。</p>
<p>javascript和css的检测分别使用的是<strong>jshint/jslint(视你node安装了哪个)</strong>和<strong>csslint</strong>。</p>
<p>从个人使用来看,检测只针对文件后缀来判定检测,所以,写在html中的js,css<br>
就没法做检测了。这是唯一的缺点。所以,grunt的检测还是需要做最后一步把关的。</p>
<h2>个性化配置</h2>
<p>然后就是配置了。提供一份自己的配置。如下:</p>
<pre><code class="lang-javascript">{
"sublimelinter": "save-only",
"sublimelinter_popup_errors_on_save": true,
"sublimelinter_executable_map": {
"javascript": "D:/Program Files/nodejs/node.exe",
"css": "D:/Program Files/nodejs/node.exe"
},
"jshint_options": {
"strict": false,
"quotmark": "single", //只能使用单引号
"noarg": true,
"noempty": true, //不允许使用空语句块{}
"eqeqeq": true, //!==和===检查
"undef": true,
"curly": true, //值为true时,不能省略循环和条件语句后的大括号
"forin": true, //for in hasOwnPropery检查
"devel": true,
"jquery": true,
"browser": true,
"wsh": true,
"evil": true,
"unused": "vars", //形参和变量未使用检查
"latedef": true, //先定义变量,后使用
"globals": {
"grunt": true,
"module": true,
"window": true,
"jQuery": true,
"$": true,
"global": true,
"document": true,
"console": true,
"setTimeout": true,
"setInterval": true
}
},
"csslint_options": {
"adjoining-classes": false,
"box-sizing": false,
"box-model": false,
"compatible-vendor-prefixes": false,
"floats": false,
"font-sizes": false,
"gradients": false,
"important": false,
"known-properties": false,
"outline-none": false,
"qualified-headings": false,
"regex-selectors": false,
"shorthand": false,
"text-indent": false,
"unique-headings": false,
"universal-selector": false,
"unqualified-attributes": false
}
}
</code></pre>
<p>考虑到csslint的检测比较坑爹,为了覆盖默认的设置(否则每次保存,将有错误和警告提示,其中属性值为<code>true</code>则是错误提示,为<code>warning</code>则是警告提示),我们必须在user setting中重新设置<code>true</code>为<code>false</code>,这样就没有提示啦。这个设置参考了<code>bootstrap</code>的<code>.csslintrc</code>文件配置。</p>
<p><strong>sublime警告提示截图:</strong></p>
<p><img src="http://segmentfault.com/img/bVbNul" alt="警告提示截图"></p>
<p>其它参数说明,请参考官方文档。css,js检测参数,参考插件对应的官网说明。</p>
<p>html5 tidy 如何配置呢?我不知道,谁知道,请告知。</p>
<p><strong>参考文档:</strong><a rel="nofollow" href="http://www.cnblogs.com/lhb25/archive/2013/05/02/sublimelinter-for-js-css-coding.html">http://www.cnblogs.com/lhb25/archive/2013/05/02/sublimelinter-for-js-css-coding.html</a></p>
解决grunt-contrib-imagemin无法压缩jpg格式的问题
https://segmentfault.com/a/1190000000387023
2014-01-14T11:58:28+08:00
2014-01-14T11:58:28+08:00
52lidan
https://segmentfault.com/u/f2e
0
<p>从相关的<a href="https://link.segmentfault.com/?enc=en68AVpHTY%2BAt7wG5tSu0Q%3D%3D.w3zBoaWKaGs1m3y7Ykj9OpiQQTwEpbjc%2F646hi%2B3w7UvKfmdioGfLdjIBTktEUsl%2Boco%2B2M7lfVuZpzKULpuKA%3D%3D" rel="nofollow">github issue</a>来看,好像win7系统32位,64位都有如此问题。任务运行时,bug提示为:</p>
<p><code>Running "imagemin:dist" (imagemin) task Fatal error: spawn ENOENT</code></p>
<p>怎么解决呢?</p>
<p>首先,在<code>package.json</code>文件中增加<code>"jpegtran-bin": "0.2.0"</code>,必须写在<code>grunt-contrib-imagemin</code>依赖声明之前。注意,增加的版本号不需要"~"哦!</p>
<p>比如我的<code>package.json</code>:</p>
<pre><code class="javascript">{
"name": "grunt-test",
"version": "0.1.0",
"dependencies": {
"grunt-css-combo": "~0.2.2"
},
"devDependencies": {
"grunt": "~0.4.2",
"jpegtran-bin": "0.2.0",
"grunt-contrib-imagemin": "~0.4.1"
}
}</code></pre>
<p>然后删掉本地的<code>node_modules</code>目录中的<code>grunt-contrib-imagemin</code>目录,重新安装。</p>
<p>Gruntfile.js配置:</p>
<pre><code class="javascript">module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
imagemin: {
/* 压缩图片大小 */
dist: {
files: [{
expand: true,
cwd: "./dev/images/",
src: ["**/*.{jpg,png,gif}"],
dest: "./official/images/"
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-imagemin'); //图像压缩
// 注册任务
grunt.registerTask('default', ['imagemin']);
};</code></pre>
<p>再次启动<code>grunt</code>任务命令,<code>.jpg</code>格式的图片全部可以压缩了。very nice!</p>
<p>注意更新使用<code>grunt-contrib-imagemin</code>的<code>0.4.1</code> 版本,因为最新版的会有同样问题。</p>