<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>一格 &#187; Javascript</title>
	<atom:link href="http://www.yigle.net/category/web-tech/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.yigle.net</link>
	<description></description>
	<lastBuildDate>Fri, 30 Dec 2011 13:10:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>2维平面相交（一）：两矩形区域相交</title>
		<link>http://www.yigle.net/2011/11/2d-cross-two-regions/</link>
		<comments>http://www.yigle.net/2011/11/2d-cross-two-regions/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 15:16:20 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[相交]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37422</guid>
		<description><![CDATA[最近遇到几种相交问题，看似简单的逻辑，却有多种不同的算法，不知你是否考虑过所用的算法好不好的问题。
两矩形区域相交
在写随机展示的一个模块时,需要判断随机生成的区域与原有区域是否相交

]]></description>
			<content:encoded><![CDATA[<p>最近遇到几种相交问题，看似简单的逻辑，却有多种不同的算法，不知你是否考虑过所用的算法好不好的问题。</p>
<h2>两矩形区域相交</h2>
<p>在写<a href="/demo/randshow.html" target="_blank">随机展示</a>的一个模块时,需要判断随机生成的区域与原有区域是否相交</p>
<p><a href="http://www.yigle.net/yigle.net/wp-content/uploads/2011/11/cross-region1.png"><img class="aligncenter size-full wp-image-37426" title="cross-region" src="http://www.yigle.net/yigle.net/wp-content/uploads/2011/11/cross-region1.png" alt="cross-region" width="431" height="226" /></a></p>
<p>最初想到的方法是：</p>
<p><strong>算法一：如果一个区域有一点在另一区域中，那么两个区域一定相交</strong></p>
<p>js实现代码：</p>
<pre class="brush:js">// 判断点在矩形内
function _inRegion(point, region) {

    return point[0] &lt; region.right &amp;&amp; point[0] &gt; region.left
        &amp;&amp; point[1] &lt; region.top &amp;&amp; point[1] &gt; region.bottom;

}
// 判断矩形1是否有点在矩形2中
function _isCross(region1, region2) {

    return  _inRegion([region1.left,region1.top], region2) ||
        _inRegion([region1.right,region1.top], region2) ||
        _inRegion([region1.right,region1.bottom], region2) ||
        _inRegion([region1.left,region1.bottom], region2);

}

function isCross(region1, region2) {

    return _isCross(region1, region2) || _isCross(region2, region1);

}</pre>
<p>看到上面的代码觉得实现太麻烦了吧，再来看一种算法：</p>
<p><strong>算法二：如果两个矩形相交，则必然存在线条交叉，而能交叉的线条只有横线和竖线，两根横线或两根竖线都不可能交叉。所以，这个问题就转化成寻找是否存在交叉的横线与竖线。</strong></p>
<p>实现代码：</p>
<pre class="brush:js">// 判断横线1是否与竖线2相交
function _crossLine(line1, line2) {
    return line1.from[1] &lt; line2.from[1] &amp;&amp; line1.to[1] &gt; line2.to[1] &amp;&amp;
        line1.from[0] &lt; line2.from[0] &amp;&amp; line1.to[0] &gt; line2.to[0];
}
//判断矩形1横线是否与矩形2竖线相交
function _isCross(r1, r2) {
    return _crossLine({from:[r1.left,r1.top],to:[r1.right,r1.top]},
        {from:[r2.left,r2.top],to:[r2.left,r2.bottom]}) ||
        _crossLine({from:[r1.left,r1.bottom],to:[r1.right,r1.bottom]},
            {from:[r2.left,r2.top],to:[r2.left,r2.bottom]}) ||
        _crossLine({from:[r1.left,r1.top],to:[r1.right,r1.top]},
            {from:[r2.right,r2.top],to:[r2.right,r2.bottom]}) ||
        _crossLine({from:[r1.left,r1.bottom],to:[r1.right,r1.bottom]},
            {from:[r2.right,r2.top],to:[r2.right,r2.bottom]});
}
function isCross(region1, region2) {
    return _isCross(region1, region2) || _isCross(region2, region1);
}</pre>
<p>这种看起来也不简单，但至少是一种思路，还有其他算法吗？有</p>
<p><strong>算法三：如果两个矩形相交，相交区域一定是个点或者矩形</strong></p>
<p>实现代码：</p>
<pre class="brush:js">function isCross(region1, region2) {
    var left = Math.max(region1.left, region2.left),
        right = Math.min(region1.right, region2.right),
        top = Math.min(region1.top, region2.top),
        bottom = Math.max(region.bottom, region.bottom);
    return left &lt; right &amp;&amp; bottom &lt; top;
}</pre>
<p>我靠，代码瞬间减少了很多，再看一种算法：</p>
<p><strong>算法四：如果矩形1的一边距离到矩形2对应边的距离小于等于矩形1的宽度或高度 =&gt;两矩形相交</strong></p>
<p>实现代码：</p>
<pre class="brush:js">function isCross(region1, region2) {
    return (region2.left &gt; region1.left &amp;&amp;
            region2.left - region1.left &lt; region1.right - reigon1.left) ||
        (region2.top &gt; region1.top &amp;&amp;
            region2.top - region1.top &lt; region2.top - region2.bottom) ||
        (region1.left &gt; region2.left &amp;&amp;
            region1.left - region2.left &lt; region2.right - reigon2.left) ||
        (region1.top &gt; region2.top &amp;&amp;
            region1.top - region2.top &lt; region1.top - region1.bottom);
}</pre>
<p>貌似代码有优化的空间，先不管，考虑还有没有更简练的算法？</p>
<p><strong>算法五：如果一个矩形在另一矩形所形成的四个区间外 &lt;=&gt; 两个矩形不想交</strong></p>
<p>实现代码：</p>
<pre class="brush:js">function isCross(region1, region2) {
    return !(region1.left &gt; region2.right || region1.right&lt;region2.left||
        region1.bottom &gt; region2.top || region1.top &lt; region2.bottom);
}</pre>
<p>看到这种算法时，我震惊了，无论从代码量还是性能上都优于上面的算法，当然如果需要获取相交区域，算法3更好。</p>
<p>回头看看上面5种算法，从不同的角度出发得到不同的算法：算法1从点的角度触发，算法2从线的角度，<br />
算法3从区域的角度，算法4从一维距离角度，算法5从区间的角度；思量一下，前4种算法都是正向思维，第5种逆向思维；<br />
逆向思维，也许自己太多过程式编程脑子秀逗了，好的算法醍醐灌顶。</p>
<p>结束，下一篇关于如何判断两个线段是否相交，你如何实现？</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/11/2d-cross-two-regions/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>使用IE selection 两个问题</title>
		<link>http://www.yigle.net/2011/08/two-problems-with-ie-selection/</link>
		<comments>http://www.yigle.net/2011/08/two-problems-with-ie-selection/#comments</comments>
		<pubDate>Tue, 09 Aug 2011 09:08:12 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[blur]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[selection]]></category>
		<category><![CDATA[submit]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37391</guid>
		<description><![CDATA[最近用到IE的document.selection，遇到两个问题：
1.selection.clear file表单 导致form 无法 submit
demo
第一次点击提交按钮无法提交表单
]]></description>
			<content:encoded><![CDATA[<p>最近用到IE的document.selection，遇到两个问题：</p>
<h3>1.selection.clear file表单 导致form 无法 submit</h3>
<p><a href="/demo/selection-clear-form-submit.html" target="_blank">demo</a></p>
<p>第一次点击提交按钮无法提交表单<br />
<strong>说明：</strong>ie浏览器不允许直接通过value=“”方式情空文件控件，所以尝试使用selection.clear清除，就会引发这个问题</p>
<p><strong>解决方法：</strong>使用form的原生reset清空</p>
<h3>2.selection.emtpy 触发 window.blur</h3>
<p><a href="/demo/ie-selection-empty-fire-blur.html" target="_blank">demo</a></p>
<p>选择一段文字后，在空白处点击，浏览器默认清除选择，触发window blur</p>
<p><strong>说明：</strong>ie8-以下才会这样，ie9其他标准浏览器都不触发；显示调用 selection.empty方法，及浏览器默认改变选择都触发</p>
<p><strong>解决方法：</strong>mousedown中延时注册blur，因为是在mousedown 之后触发的，通过延时注册，让其先触发</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/08/two-problems-with-ie-selection/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>JS简单拖放程序</title>
		<link>http://www.yigle.net/2011/08/simple-drag-and-drop-program/</link>
		<comments>http://www.yigle.net/2011/08/simple-drag-and-drop-program/#comments</comments>
		<pubDate>Tue, 09 Aug 2011 08:07:54 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[preventDefault]]></category>
		<category><![CDATA[selection]]></category>
		<category><![CDATA[setCapture]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37380</guid>
		<description><![CDATA[在大学做web应用时，就搞过拖放，当时从网上搜索的相关程序，感觉代码很复杂，最终效果还不好，自己也没调通，所以在脑子里总感觉拖放是很复杂的东西。最近正好有时间，再写下这种程序。
要实现拖放，基本思路就是：鼠标点下时监听移动，抬起时停止监听。
        var x,y;
        function move(e) {
]]></description>
			<content:encoded><![CDATA[<p>在大学做web应用时，就搞过拖放，当时从网上搜索的相关程序，感觉代码很复杂，最终效果还不好，自己也没调通，所以在脑子里总感觉拖放是很复杂的东西。最近正好有时间，再写下这种程序。</p>
<p>要实现拖放，基本思路就是：鼠标点下时监听移动，抬起时停止监听。</p>
<pre class="brush:js">        var x,y;
        function move(e) {
            el.style.left = e.clientX - x + 'px';
            el.style.top =  e.clientY - y + 'px';
        }

        function stop() {
            E.detach(document, 'mousemove', move);
            E.detach(document, 'mouseup', stop);
        }
        E.on(el, 'mousedown', function(e) {

            x = e.clientX - el.offsetLeft;
            y = e.clientY - el.offsetTop;
            E.on(document, 'mousemove', move);
            E.on(document, 'mouseup', stop);

        });</pre>
<p>这样拖放就实现了，当然还有一些问题：</p>
<h3>拖放禁止</h3>
<p>1.当第一次拖放后，再次拖放元素就会发现会出现拖放禁止图标，是因为第一次拖放时点击拖放元素，拖放元素被选择，第二次拖放时使用了浏览器默认拖放。浏览器对于 image，link，selection默认是可以拖放的。</p>
<p>2.拖放过程中移动到其他元素时，其他元素被选择，再次拖放也会出现拖放禁止图标，原因同上。</p>
<p>解决这个问题:</p>
<p><strong>方法一：preventDefault</strong></p>
<p>问题1，在mousedown中阻止默认动作，防止元素被选择；问题2，在mousemove中阻止默认动作，防止其他元素被选择(opera貌似无效)。</p>
<p>这个方法有点体验不足：在ie9，ff等现代浏览器将 “清除之前选择”的默认动作也阻止了，如果之前有选择内容，拖放时原选择不会被清除。</p>
<p><strong>方法二：清空 selection</strong></p>
<p>mousedown,mousemove的时候清空一下selection即可解决上面两个问题</p>
<p>有的程序也通过setCapture来阻止默认事件，并且扩大捕获，兼容性不好不考虑；</p>
<p>综合考虑，采用清空selection比较好。</p>
<h3>异常终止问题</h3>
<p>1.窗口失去焦点时（如：alt+tab切换，alert等），无法触发mouseup（chrome 可以触发），导致拖放不终止，回到窗口时继续拖放，体验不好</p>
<p>2.页面中存在iframe时，鼠标移动到iframe中事件传递到iframe的document中，无法到达原来的document导致无法触发mouseup，mousemove等</p>
<p>解决第一个问题，可以监听下window blur事件，终止拖放；但这里有个问题：ie通过document.selection.empty()方法清除selection，当selection有内容时，<a href="http://www.yigle.net/2011/08/two-problems-with-ie-selection/" target="_blank">导致window触发blur事件</a>，拖放终止。这个触发是在mousedown处理完之后触发的，即window blur注册之后再触发，解决可以考虑setTimeout延时注册下。</p>
<p>解决第二个问题，考虑将事件仍传递到本页面document，可以建个遮罩层将iframe遮挡(为了简洁，这里不实现)。</p>
<p>这样简单的拖放程序就出来了，<a href="/demo/drag.html" target="_blank">demo</a>，code：</p>
<pre class="brush:js">function Drag(el) {
    this.el = el;
    this._initEvents();
}

Drag.prototype = {
    moveTo : function(x, y) {
        var el = this.el;
        el.style.left = x + 'px';
        el.style.top = y + 'px';
    },
    _initEvents : function() {

        var x,y,self = this,el = this.el;

        function move(e) {
            // 防止移动过程中选择其他元素，保证体验一致
            util.clearSelection();
            self.moveTo(e.clientX - x, e.clientY - y);
        }

        function stop() {
            E.detach(document, 'mousemove', move);
            E.detach(document, 'mouseup', stop);
            E.detach(window, 'blur', stop);
        }

        E.on(this.el, 'mousedown', function(e) {

            // 清除之前选择，防止拖拽时禁止
            util.clearSelection();

            var style = util.getComputeStyle(el);
            x = e.clientX - el.offsetLeft + (parseInt(style.marginLeft) || 0);
            y = e.clientY - el.offsetTop + (parseInt(style.marginTop) || 0);

            E.on(document, 'mousemove', move);
            E.on(document, 'mouseup', stop);
            // 失去焦点时终止   document.selection.empty() 会触发blur，延时注册
            setTimeout(function(){E.on(window, 'blur', stop);},0);
        });
    }
};</pre>
<p>如果要做成组件，当然还需要考虑全面一些，如：iframe情况，移动范围，非绝对定位元素，移动元素和控制元素不同，相关事件支持等等。现在回头看看实现不复杂，只是遇到问题时自己跑偏了，之前看过<a href="http://www.cnblogs.com/cloudgamer/archive/2008/11/17/1334778.html" target="_blank">《JavaScript 拖放效果》</a>这篇文章，觉得很多问题需要处理，然后写的时候遇到问题就去看文中的介绍，而并没真正从问题本身和解决方法上入手，结果严重影响了效率，而且弄的晕头转向，不知在为了解决什么问题而加代码。有参考固然是好的，但是需要更多的独立思考，理清思路，关注本质，这样程序才能得心应手，才是自己的程序。</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/08/simple-drag-and-drop-program/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[JS基础]运算符优先级及运算顺序</title>
		<link>http://www.yigle.net/2011/07/javascript-operator-precedence-and-order-of-execluate/</link>
		<comments>http://www.yigle.net/2011/07/javascript-operator-precedence-and-order-of-execluate/#comments</comments>
		<pubDate>Tue, 12 Jul 2011 16:47:43 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[evaluate-order]]></category>
		<category><![CDATA[JS基础]]></category>
		<category><![CDATA[operator-precedence]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37357</guid>
		<description><![CDATA[不知什么时候形成一种认识：运算符的优先级越高就越先执行，前两天在群里写了段代码：
var a =1;
function fn(){alert(a);}
var b = a++ + fn();
]]></description>
			<content:encoded><![CDATA[<p>不知什么时候形成一种认识：运算符的优先级越高就越先执行，前两天在群里写了段代码：</p>
<pre class="brush:applescript">var a =1;
function fn(){alert(a);}
var b = a++ + fn();</pre>
<p>alert出的值是是怎么呢？</p>
<p>按照我之前的认识应该是 1,()优先级最高最先被执行此时a=1，可事实在各个浏览器都是 2</p>
<p>不得不怀疑之前的认识</p>
<p>又测试了下其他语言， php，java也都一样的结果：2，c和c++输出：1</p>
<p>参考了下犀牛书还有语言的官方，基本都表述：运算符优先级决定被运算的顺序，像<a href="https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence" target="_blank">MDN</a>：“Operator precedence determines the order in which operators are evaluated”，但<a href="http://php.net/manual/en/language.operators.precedence.php" target="_blank"> PHP</a> ：“The precedence of an operator specifies how “tightly” it binds two expressions together”。</p>
<p>按php的说法就很好理解上面的问题了，优先级表示着运算符结合能力的强弱</p>
<p>但是真是那些说 “evaluate order”的错了？？？咨询了好几个同事，终于算是有个自己满意的解释</p>
<p>表达式的运算顺序和运算符优先级根本是两个概念：运算符的优先级只表示着对于<strong>运算时</strong>哪个运算符先被结合（也可以说执行），至于怎么个顺序运算和它无关，而表达式的<strong>运算顺序和各个语言的规范有关</strong>，比如java中就明确定义<a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7" target="_blank">evaluate order：Left To Right</a>，看了下JS的ECAM规范，虽然没用像java那么明确说明运算顺序，但是跟据各个运算符的运算顺序可以看出也是 从左向右 运算的（包括函数调用，参数计算）。</p>
<h4>上面 b = a++ + fn()运算过程：</h4>
<p>1.运算b是个ref<br />
2.运算=右边部分，a++，此时a=2<br />
3.运算fn()</p>
<p>具体过程可以这样理解：</p>
<p>1.扫描到=操作符，先运算b<br />
2.扫描到++(尽可能多的结合)，后面是+，++的优先级高于+，运算a++，此时a++会产生副作用（a+=1）,js会先算完子表达式再执行，a=2<br />
3.接着扫描到()执行函数运算</p>
<h4>C语言又怎吗输出1呢？</h4>
<p>C语言可能为了编译器可以灵活的优化程序，并没用明确确定运算顺序以及副作用处理，所以不同编译器运算结果也可能不同，比如：</p>
<pre class="brush:cpp">int i =1;
int m = (i++) +(i++)+(i++)</pre>
<p>有的编译器是6，有的编译器(MSC,TurboC)编译出来是3；还有函数实参运算顺序的问题也一样。</p>
<h3>总之：</h3>
<p>表达式的运算顺序由语言规范定义和运算符的优先级“无关”，运算符的优先级及结合性只在运算时判断那个运算符先被运算（这样说，MDN的解释也没错只是自己理解错了），更好理解些优先级结合性表示运算符谁先被结合使用;Javascript中 跟据ECAM中对各个操作符的运算顺序的规范，Javascript的运算顺序应该是：<strong>从左到右，副作用运算先执行再继续表达式运算</strong>(不知有没有特例，没看到明确说明,-_-)</p>
<h3>相关的讨论：</h3>
<p>JavaEye关于java和c运算顺序的问答：<a href="http://www.iteye.com/problems/20258" target="_blank">http://www.iteye.com/problems/20258</a><br />
StackOverflow Javascript运算顺序的讨论：<a href="http://stackoverflow.com/questions/5944593/javascript-evaluation-order" target="_blank"> http://stackoverflow.com/questions/5944593/javascript-evaluation-order</a><br />
一篇讨论c#，java,c运算顺序的blog：<a href="http://rednaxelafx.iteye.com/blog/132187" target="_blank">http://rednaxelafx.iteye.com/blog/132187</a> <span style="color: #999999;">//added 2011-07-13</span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/07/javascript-operator-precedence-and-order-of-execluate/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[JS基础]prototype谁是谁祖宗</title>
		<link>http://www.yigle.net/2011/07/prototype-who-is-ancestors/</link>
		<comments>http://www.yigle.net/2011/07/prototype-who-is-ancestors/#comments</comments>
		<pubDate>Fri, 08 Jul 2011 13:24:57 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[JS基础]]></category>
		<category><![CDATA[prototype]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37310</guid>
		<description><![CDATA[先看一段代码：
var a = function() {},
    b = new a();
Function.prototype.isPrototypeOf(a);
]]></description>
			<content:encoded><![CDATA[<p>先看一段代码：</p>
<pre class="brush:js">var a = function() {},
    b = new a();
Function.prototype.isPrototypeOf(a);
Object.prototype.isPrototypeOf(a);
Object.prototype.isPrototypeOf(b);
Function.prototype.isPrototypeOf(b);
Object.prototype.isPrototypeOf(Function);
Function.prototype.isPrototypeOf(Object);
Object.constructor === Function;
Function.constructor === Function;</pre>
<p>上面3-10行各返回的是true还是false？<span style="text-decoration: underline;" title="只有第7行为false">答案</span></p>
<p>如果你都想对了，就别浪费时间继续看了-_-</p>
<p>理解上面的问题，就要搞清谁是原型链的顶级及继承关系,参考ECMA5规范：</p>
<blockquote><p>“The value of the [[Prototype]] internal property of the Object prototype object is null, the value of the [[Class]]internal property is “Object”, and the initial value of the [[Extensible]] internal property is true.”</p>
<p>“The Function prototype object is itself a Function object (its [[Class]] is  “Function”) that, when invoked,accepts any arguments and returns undefined.The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object (15.2.4). The initial value of the [[Extensible]] internal property of the Function prototype object is true.”</p></blockquote>
<p>Object prototype object 标准内置对象（这里简称OPO） 是所有原型链的鼻祖，再向上就是null了<br />
Function prototype object 标准内置对象（这里简成FPO） 是函数的原型对象，其__proto__为OPO</p>
<p>Function prototype及__proto__都指向FPO<br />
所有其他内置构造函数其__proto__为FPO，prototype指向OPO，如：Object 构造函数 __proto__指向内置FPO，prototype指向OPO</p>
<p>函数实例也是一个实例对象（通过 new Function而来），和Object一样，内部prototype指向构造函数的protype属性，即FPO，prototype属性是创建时被添加（为OPO）用来作为构造函数而创建的 。</p>
<p>说的比较乱，画张图：</p>
<p><img src="http://farm6.static.flickr.com/5115/5911420237_0c05b79ac4.jpg" alt="2011-07-06_193220" width="500" height="289" /></p>
<p>Function 用于实例函数对象，其prototype 为FPO，这样保证所有其他函数的继承FPO的属性方法<br />
其他函数 用于实例普通对象，其prototype为OPO，保证每个对象实现都继承OPO的属性方法。</p>
<p>再来看看代码中用到的isPrototypeOf方法：</p>
<h3>isPrototypeOf方法</h3>
<p>O.isPrototypeOf(V)方法用于检查V的构造函数的原型是不是O，根据ECMA5规范</p>
<blockquote>
<pre style="font-family: tahoma;">Object.prototype.isPrototypeOf (V)
When the isPrototypeOf method is called with argument V, the following steps are taken:
1.  If V is not an object, return false.
2.  Let O be the result of calling ToObject passing the this value as the argument.
3.  Repeat
    a.  Let V be the value of the [[Prototype]] internal property of V.
    b.  if V is null, return false
    c.  If O and V refer to the same object, return true.</pre>
</blockquote>
<p>&nbsp;</p>
<p>在检查时会有个向上遍历的过程，即：检测V的构造函数的原型是不是O，如果不是，接着检查V的构造函数的原型 的 构造函数原型 是不是O，直至原型为null，返回false。</p>
<p>理解了上面，再来看看开始的那段代码：</p>
<pre class="brush:js">var a = function() {},
    b = new a();
Function.prototype.isPrototypeOf(a); // a的__proto__为 FPO，返回true
Object.prototype.isPrototypeOf(a); // a的__proto__为 FPO，向上查找，FPO的 __proto__ === Object.prototype,返回true
Object.prototype.isPrototypeOf(b); // b的__proto__为OPO，返回true
Function.prototype.isPrototypeOf(b); // b的__proto__为OPO，向上查找，OPO的__proto__为null，返回false
Function.prototype.isPrototypeOf(Object); // F的__proto__为FPO,返回true
Object.prototype.isPrototypeOf(Function); // F的__proto__为FPO，向上查找，FPO的 __proto__ === Object.prototype,返回true
Object.constructor === Function; // true
Function.constructor === Function; // true</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/07/prototype-who-is-ancestors/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>脚本的加载执行及优化</title>
		<link>http://www.yigle.net/2011/05/script-load-execution-and-optimization/</link>
		<comments>http://www.yigle.net/2011/05/script-load-execution-and-optimization/#comments</comments>
		<pubDate>Sun, 22 May 2011 10:05:04 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37275</guid>
		<description><![CDATA[Javascript及UI Render的单线程
讨论之前，首先需要理解的是js的单线程机制，即在同一时刻js只能执行一个任务，另外实际上浏览器的渲染也和js共用同一线程，这就意味着js在处理代码时浏览器会停止解析，如图所示：

当遇到js代码时js引擎会解析执行代码，此时浏览器渲染停止，直到脚本解析完毕才继续。当脚本执行耗时过长，尤其当引入外部脚本时加上脚本的加载时间，对ui渲染的阻塞更为严重，这是性能优化的一个方面，也是这篇文章为什么讨论脚本加载执行顺序的主要原因。
]]></description>
			<content:encoded><![CDATA[<h2>Javascript及UI Render的单线程</h2>
<p>讨论之前，首先需要理解的是js的单线程机制，即在同一时刻js只能执行一个任务，另外实际上浏览器的渲染也和js共用同一线程，这就意味着js在处理代码时浏览器会停止解析，如图所示：</p>
<p><img class="alignnone" src="http://farm6.static.flickr.com/5304/5745379353_9fd9988fa3_z.jpg" alt="js single thread" width="580" height="206" /></p>
<p>当遇到js代码时js引擎会解析执行代码，此时浏览器渲染停止，直到脚本解析完毕才继续。当脚本执行耗时过长，尤其当引入外部脚本时加上脚本的加载时间，对ui渲染的阻塞更为严重，这是性能优化的一个方面，也是这篇文章为什么讨论脚本加载执行顺序的主要原因。</p>
<h2>两种嵌入方式</h2>
<h3>解析嵌入（Parser-inserted）</h3>
<p>指在html源代码中通过&lt;script src=”XXX”&gt;等方式引入（术语上这类脚本成为 Parser-inserted script），这种方式下浏览器会以&lt;script&gt;标签在页面中出现的顺序加载和执行，即同步加载执行。正如前面讨论的，这种方式会阻塞页面其他元素的加载，及页面渲染。</p>
<h3>脚本嵌入（script-inserted）</h3>
<p>通过脚本动态载入方式（术语上这类脚本成为Parser-inserted script），动态载入脚本的方式有很多中如：document.write,xhr eval,script document Element等<sup><a href="#zs2">[1]</a></sup>，其中通过动态创建script标签是被广泛应用推荐的一种，这里只讨论这种情况。</p>
<p>通过动态创建script元素可以实现无阻塞加载，但这意味着什么？一个脚本阻塞是从开始加载到执行完毕才结束，大体有三个步骤：</p>
<ol>
<li>加载脚本</li>
<li>解析</li>
<li>执行</li>
</ol>
<p>在这个过程中通常#1耗时最长，通过这种方式意味着不必等待脚本的#1(也许还有#2)过程，浏览器ui线程可以不受干扰继续执行，但是由于js单线程机制对于#3过程无可避免的会对ui线程阻塞。当然，浏览器解析通常很快的（尤其是新的js引擎），通常这部分影响几乎察觉的到，但是如果脚本性能不好，执行需要较长时间这也会产成性能瓶颈。</p>
<p>总之，无阻塞加载使浏览器不必等待脚本加载而继续渲染，两者异步执行，但脚本执行仍会阻塞渲染。</p>
<h2>并行加载</h2>
<p>根据上面的无阻塞方式加载多个脚本文件，这些文件是并行加载的，这样相对传统方式带来的好处是，使浏览器更快更早的请求文件并且不会阻塞浏览器渲染，但是这里仅仅是并行“加载”，并不意味着会并行执行，还是那个原因js是单线程的，同一时间只有一个js会被执行！另外带来的一个问题是，脚本并行加载其执行顺序又是怎样的呢？</p>
<p>对于脚本的执行顺序，目前各个浏览器执行情况还有些差异：</p>
<p>1.opera and firefox3.6-</p>
<p>执行的顺序会按照脚本被动态插入的顺序执行</p>
<p>2.ie，Safari，chorme and firefox4<sup><a href="#zs2">[2]</a></sup></p>
<p>脚本加载完毕即会执行，不会保持他们被插入的顺序</p>
<h2>浏览器的努力：defer &amp; async</h2>
<p>对于无阻塞脚本加载，浏览器也给出了一定的方案，包括早期ie实现（后来被firefox3.1引入）的defer属性以及HTML5定义的async属性</p>
<p><strong>defer</strong></p>
<p>脚本下载不会阻塞浏览器渲染，直到文档解析完成时才会执行，<code title="event-DOMContentLoaded">DOMContentLoaded触发之前</code>（兼容性不好）。</p>
<p><strong>async</strong></p>
<p>这个属性由html5新引入的标准属性，这是个布尔属性，当被定义时，脚本会无阻塞加载，脚本文件加载完成立即执行（不顾及脚本出现的先后顺序），如果需要脚本保持引入的先后顺序需要显示的设置.async = false(对于script-created 脚本默认为true，目前仅firefox4支持false)。</p>
<h2>开发者的努力：LABjs</h2>
<p>labjs项目主要特性能够实现脚本并行加载和顺序执行，已经被多个大型网站采用，看个例子：</p>
<pre class="brush:js">   $LAB
   .script("a1.js").wait()
   .script("a2.js")
   .script("a3.js").wait()
   .script("a4.js");</pre>
<p>这样,a1a2a3a4脚本会被并行加载，但是执行顺序还是会按照a1-&gt;a2a3||a3a2-&gt;a4的顺序执行。由于前面提到的对于动态创建script元素，不同浏览器两种不同的执行顺序，其实现进行了browser sniff：opera and old firefox直接动态插入即可保证执行顺序；对于IE chrome sarfari方面通过一个trick： 预加载script文件（通过设置script type为不能识别的值，保证浏览器只加载不执行），然后按照需要的顺序插入正确的script元素，这些元素就会立刻执行并且保持顺序：</p>
<p><img class="alignnone" src="http://farm6.static.flickr.com/5229/5745379409_01ccaa0fa0_z.jpg" alt="LABjs 并行下载的实现" width="580" height="99" /></p>
<p>当然，还有许多其他项目的实现如 requireJS 的<a href="http://requirejs.org/docs/api.html#order">order pluigin</a>，<a rel="nofollow" href="http://headjs.com/" target="_blank">HeadJS</a> ，<a rel="nofollow" href="http://stevesouders.com/controljs/" target="_blank">ControlJS</a>&#8230;</p>
<h2>总结</h2>
<p>由于javascript及ui render的单线程机制，采用无阻塞加载脚本来提高页面性能，同时看到浏览器及开发者对于这方面的努力及实现，但请注意无阻塞加载仅仅是“加载”而已，执行过程仍然会阻塞渲染，进一步的优化需要我们回归到代码本身，减少其执行时间，以获得更快的响应。</p>
<p>注：</p>
<ol>
<li id="zs1">Steve提出了各种实现方式<a href="http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/" target="_blank"> loading scripts without blocking</a></li>
<li id="zs2">firefox4 pre7之前仍是按照原来的方式顺序执行，为了符合html5的规范改为无序执行，这里还有<a href="http://blog.getify.com/2010/10/ff4-script-loaders-and-order-preservation/" target="_blank">labjs的作者和火狐开发者的一个故事</a>，最终firefox4通过设置async=false来保持这种执行顺序。</li>
</ol>
<p>参考：</p>
<ul>
<li><a href="http://www.whatwg.org/specs/web-apps/current-work/#the-script-element" target="_blank">http://www.whatwg.org/specs/web-apps/current-work/#the-script-element</a></li>
<li><a href="http://www.nczonline.net/blog/2010/08/10/what-is-a-non-blocking-script/" target="_blank"> http://www.nczonline.net/blog/2010/08/10/what-is-a-non-blocking-script/</a></li>
<li><a href="https://developer.mozilla.org/En/HTML/Element/Script" target="_blank"> https://developer.mozilla.org/En/HTML/Element/Script</a></li>
</ul>
<p><a href="http://blog.getify.com/2010/10/ff4-script-loaders-and-order-preservation/" target="_blank"> </a>Update：</p>
<ol>
<li>添加defer脚本执行的确切时间 2011-05-24</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/05/script-load-execution-and-optimization/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>IE的两个问题</title>
		<link>http://www.yigle.net/2011/03/ie-two-problem/</link>
		<comments>http://www.yigle.net/2011/03/ie-two-problem/#comments</comments>
		<pubDate>Tue, 22 Mar 2011 16:17:59 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37245</guid>
		<description><![CDATA[上次提到IE下通过window属性定义变量的bug，最近又遇到两个问题：

IE dom方法赋值变量调用错误
IE 的&#60;html&#62;元素border问题
]]></description>
			<content:encoded><![CDATA[<p>上次提到<a href="http://www.yigle.net/posts/2011/03/window-defined-by-the-variable-ie-bug/">IE下通过window属性定义变量的bug</a>，最近又遇到两个问题：</p>
<ol>
<li><a href="#ieDom">IE dom方法赋值变量调用错误</a></li>
<li><a href="#ieBorder">IE 的&lt;html&gt;元素border问题</a></li>
</ol>
<h3 id="ieDom">IE dom方法赋值变量调用错误</h3>
<p>如下代码：</p>
<pre class="brush:javascript">    var s = document.getElementById;
    alert(typeof s);
    var el = s.call(document, 'test');
    el.innerHTML = "ok";</pre>
<p>应该alert出什么？el里的文字是？</p>
<p>按道理应该alert出 function，文字是ok，但在ie下却alert 出 object，IE7/8下文字是ok，ie6下却报错</p>
<p><cite>“object doesn&#8217;s support this property or method”</cite></p>
<p><a href="/demo/show/ie-dom-fn-refer.html" target="_blank">test demo</a></p>
<p>因此应避免将dom方法直接付给变量，再来看看另一问题</p>
<h3 id="ieBorder">IE 的&lt;html&gt;元素border问题</h3>
<p>在计算元素offset时经常用到docuementElement.offsetHeight,documentElement.scrollTop</p>
<p>比如要实现一个元素固定居于浏览器右下角，ie6下通过脚本实现：</p>
<pre class="brush:applescript">    var el = document.getElementById('test'),
        htmlEl = document.documentElement;
    function rb() {
        el.style.top = htmlEl.scrollTop + (window.innerHeight||htmlEl.offsetHeight) - 50+'px';
    }
    window.onscroll=rb;
    window.onresize=rb;</pre>
<p>offsetHeight是指整个元素的高度(border box)，在ie下html元素错误的box模型，offsetHeight返回的也是窗的高度</p>
<p><a href="/demo/show/ie-html-element-2px-border.html" target="_blank">test demo</a></p>
<p>看下测试demo发现ie无限向下滚动，原因在ie下html元素有2px的border，而元素定位是相对body的这样每次计算都多出2px</p>
<p>将offsetHeight改为clientHeight ok！clientheight是元素内容区域的高度（padding box）</p>
<p>以后处理基于&lt;html&gt;元素定位问题时需要留意这2px的box</p>
<p>最后看看jquery offset方法的实现也是对这个问题进行了处理，<a href="https://github.com/jquery/jquery/blob/master/src/offset.js" target="_blank">offset.js</a></p>
<p><img class="alignnone" title="jquery offset.js" src="http://farm4.static.flickr.com/3050/5747291812_d766532c85_z.jpg" alt="" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/03/ie-two-problem/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>一级故障！</title>
		<link>http://www.yigle.net/2011/03/a-failure/</link>
		<comments>http://www.yigle.net/2011/03/a-failure/#comments</comments>
		<pubDate>Thu, 10 Mar 2011 15:20:05 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37217</guid>
		<description><![CDATA[对于这次故障，回想有许多点可以避免，如果在这个点上怎样，如果在那个点上怎样&#8230;脑子也一直思考是意识的问题还是行为流程的问题，不能说没有足够的意识，但又是意识支配行为，在发布测试那段时间，本来清楚的记得要改动那个文件，后来改动文件目录时，却把这个文件忽略了，检查时也跟本没留意。意识是不怎么可靠的，什么什么的原因都可能影响到那段时间的意识，加强意识保持意识还需要很多的提升，我需要更多的思考和体会，但下面几点是对于这次故障我比较明确的体会和教训：

1.开发中根本不要引用本地/demo路径。php可以配置文件引用，assets也可以host/fiddler等等，虽然开发完可以通过很多方式检查是否引用来避免，但仍然不百分百可靠，所以根本就不要使用。
]]></description>
			<content:encoded><![CDATA[<p>对于这次故障，回想有许多点可以避免，如果在这个点上怎样，如果在那个点上怎样&#8230;脑子也一直思考是意识的问题还是行为流程的问题，不能说没有足够的意识，但又是意识支配行为，在发布测试那段时间，本来清楚的记得要改动那个文件，后来改动文件目录时，却把这个文件忽略了，检查时也跟本没留意。意识是不怎么可靠的，什么什么的原因都可能影响到那段时间的意识，加强意识保持意识还需要很多的提升，我需要更多的思考和体会，但下面几点是对于这次故障我比较明确的体会和教训：</p>
<div id=":zx">
<strong>1.开发中根本不要引用本地/demo路径。</strong>php可以配置文件引用，assets也可以host/fiddler等等，虽然开发完可以通过很多方式检查是否引用来避免，但仍然不百分百可靠，所以根本就不要使用。</p>
<p><strong>2.有不合理、需要改进的地方，能够主动去推进。</strong>系统流程不可能就非常完美，仍然存在很多问题，只有不断的改进才会越来越成熟，遇到觉得不合理的问题，我们需要去主动去推进改变，而不是等待系统自己改变，这样才能更快的完善，避免可能会出现的问题。</p>
<p><strong>3.制定流程、严格的执行。</strong>流程是很死的东西，也正是这种很固定的东西才能给不可靠的意识加一层保障，意识是需要加强，但通过固定的流程方法，是避免问题很有效的方法。</div>
<div></div>
<div>第一次出现的故障让我认识到对业务熟悉的重要，第二次故障让我认识了流程的重要，而这次的故障让我认识到制定合理流程和合理方式的重要及意识的不可靠。</div>
<div></div>
<div>我不会允许自己再出现这样的故障！</div>
<div></div>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/03/a-failure/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>通过window属性定义变量IE bug</title>
		<link>http://www.yigle.net/2011/03/window-defined-by-the-variable-ie-bug/</link>
		<comments>http://www.yigle.net/2011/03/window-defined-by-the-variable-ie-bug/#comments</comments>
		<pubDate>Thu, 03 Mar 2011 14:01:06 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37213</guid>
		<description><![CDATA[通过window属性的方式我们可以定义变量，主要在一些变量名未知时用到，window[variable]=sth;但是在IE下却有问题：
&#60;html&#62;
&#60;body&#62;
&#60;script&#62;
]]></description>
			<content:encoded><![CDATA[<p>通过window属性的方式我们可以定义变量，主要在一些变量名未知时用到，window[variable]=sth;但是在IE下却有问题：</p>
<pre class="brush:php">&lt;html&gt;
&lt;body&gt;
&lt;script&gt;
window.a={};
&lt;script src="test.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>test.js代码如下：</p>
<pre class="brush:js">alert(a);
var a=window.a||{};</pre>
<p>测试Demo：<a href="/demo/show/window-define-variable-ie-bug.html" target="_blank">window属性方式定义变量IE Bug</a></p>
<p>在IE下会alert出undefined，这个bug出现的情况是：一个文件中通过window属性定义变量，另一文件中通过var方式定义同名变量。这样原来定义的变量在另一文件中就会未定义。看来通过var的方式定义最为稳妥，像使用KISSY.app(“YIGLE”)这种方式，还是在前面加个var 定义以免带来一些不必要的麻烦：</p>
<pre class="brush:js">var YIGLE=KISSY.app("YIGLE");</pre>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
update: 2011-04-11<br />
hax 一篇比较深入说明此问题的文章：<a href="http://hax.iteye.com/blog/349569" target="_blank">http://hax.iteye.com/blog/349569</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/03/window-defined-by-the-variable-ie-bug/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>window.print 打印指定区域</title>
		<link>http://www.yigle.net/2011/01/window-print-designated-areas/</link>
		<comments>http://www.yigle.net/2011/01/window-print-designated-areas/#comments</comments>
		<pubDate>Sat, 15 Jan 2011 17:25:09 +0000</pubDate>
		<dc:creator>Tiejun</dc:creator>
				<category><![CDATA[Javascript]]></category>

		<guid isPermaLink="false">http://www.yigle.net/?p=37137</guid>
		<description><![CDATA[我们通过link 标签的media属性是可以指定打印样式的，隐藏无用的元素，只显示相应区域，这是一种最有效的方法。但是，某些情况下我们可能需要js来实现打印功能，而window.print方法有没有什么参数可用，我们必须来改变页面显示来实现打印某一区域。
改变页面呈现，很容易想到，可以直接改变本页面然后打印“后”再复原，（这是网上随处可见的方法），也可以在iframe中隐式打印，或者新打开个窗口来打印，下面是我实际测试这几种方法的问题：
[测试：win7/ opera 11.00,firefox 3.6.13, ie8,chrome7.0,sarfari5.0.3 ]
直接操作页面:
]]></description>
			<content:encoded><![CDATA[<p>我们通过link 标签的media属性是可以指定打印样式的，隐藏无用的元素，只显示相应区域，这是一种最有效的方法。但是，某些情况下我们可能需要js来实现打印功能，而window.print方法有没有什么参数可用，我们必须来改变页面显示来实现打印某一区域。</p>
<p>改变页面呈现，很容易想到，可以直接改变本页面然后打印“后”再复原，（这是网上随处可见的方法），也可以在iframe中隐式打印，或者新打开个窗口来打印，下面是我实际测试这几种方法的问题：</p>
<p>[测试：win7/ opera 11.00,firefox 3.6.13, ie8,chrome7.0,sarfari5.0.3 ]<br />
<strong>直接操作页面:</strong><br />
1.闪烁<br />
2.firefox有问题，未等提取信息就会执行接下来的语句</p>
<p><strong>iframe 方式:</strong><br />
1.ie,opera 打印的是原页面的</p>
<p><strong>window.open 方式:</strong><br />
1.opeara 不打印,绑定onload可打印，但立即关闭页面会造成opera崩溃</p>
<p><a href="/demo/show/print.html" target="_blank">测试demo</a></p>
<p>相对而言，比较中意打开新页面的方式，就像提供了一种打印预览的功能，我们也可以在新页面中加载相应的打印样式，遗憾的opera下就让用户手动关闭或者用其他方法吧。</p>
<p>注：</p>
<p>1.ie下有onbeforeprint,onafterprint事件，其他浏览器均不支持</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yigle.net/2011/01/window-print-designated-areas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

