<?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://blog.zhourunsheng.com/tag/javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhourunsheng.com</link>
	<description>天空一朵雨做的云</description>
	<lastBuildDate>Sat, 08 May 2021 05:17:21 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>JavaScript之变量和函数提升</title>
		<link>http://blog.zhourunsheng.com/2013/09/javascript%e4%b9%8b%e5%8f%98%e9%87%8f%e5%92%8c%e5%87%bd%e6%95%b0%e6%8f%90%e5%8d%87/</link>
		<comments>http://blog.zhourunsheng.com/2013/09/javascript%e4%b9%8b%e5%8f%98%e9%87%8f%e5%92%8c%e5%87%bd%e6%95%b0%e6%8f%90%e5%8d%87/#comments</comments>
		<pubDate>Fri, 06 Sep 2013 23:54:32 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1838</guid>
		<description><![CDATA[<p>JS代码有一个特殊的规则，那就是hoisted提升规则，函数的调用可以放在函数实现的前面，但是仅限于函数是以非 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/javascript%e4%b9%8b%e5%8f%98%e9%87%8f%e5%92%8c%e5%87%bd%e6%95%b0%e6%8f%90%e5%8d%87/">JavaScript之变量和函数提升</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>JS代码有一个特殊的规则，那就是<strong>hoisted提升规则</strong>，函数的调用可以放在函数实现的前面，但是仅限于函数是以非表达式的场合声明实现的，那么变量的提升又有什么不同呢，在开发过程中还会出现哪些经常犯的错误，本文对此进行了介绍。</p>
<p>One of the trickier aspects of JavaScript for new JavaScript developers is the fact that variables and functions are "hoisted." Rather than being available after their declaration, they might actually be available beforehand. How does that work? Let's take a look at variable hoisting first.<span id="more-1838"></span></p>
<pre>// ReferenceError: noSuchVariable is not defined
console.log(noSuchVariable);</pre>
<p>This is more or less what one would expect. An error is thrown when you try to access the value of a variable that doesn't exist. But what about this case?</p>
<pre>// Outputs: undefined
console.log(declaredLater);

var declaredLater = "Now it's defined!";

// Outputs: "Now it's defined!"
console.log(declaredLater);</pre>
<p>What is going on here? It turns out that JavaScript treats variables which will be declared later on in a function differently than variables that are not declared at all. Basically, the JavaScript interpreter "looks ahead" to find all the variable declarations and "hoists" them to the top of the function. Which means that the example above is equivalent to this:</p>
<pre>var declaredLater;

// Outputs: undefined
console.log(declaredLater);

declaredLater = "Now it's defined!";

// Outputs: "Now it's defined!"
console.log(declaredLater);</pre>
<p>One case where this is particularly likely to bite new JavaScript developers is when reusing variable names between an inner and outer scope. For example:</p>
<pre>var name = "Baggins";

(function () {
    // Outputs: "Original name was undefined"
    console.log("Original name was " + name);

    var name = "Underhill";

    // Outputs: "New name is Underhill"
    console.log("New name is " + name);
})();</pre>
<p>In cases like this, the developer probably expected <em>name</em> to retain its value from the outer scope until the point that <em>name</em> was declared in the inner scope. But due to hoisting, <em>name</em> is <em>undefined</em>instead.</p>
<p>Because of this behavior JavaScript linters and style guides often recommend putting all variable declarations at the top of the function so that you won't be caught by surprise.</p>
<p>So that covers variable hoisting, but what about function hoisting? Despite both being called "hoisting," the behavior is actually quite different. Unlike variables, a function declaration doesn't just hoist the function's name. It also hoists the actual function definition.</p>
<pre>// Outputs: "Yes!"
isItHoisted();

function isItHoisted() {
    console.log("Yes!");
}</pre>
<p>As you can see, the JavaScript interpreter allows you to use the function before the point at which it was declared in the source code. This is useful because it allows you to express your high-level logic at the beginning of your source code rather than the end, communicating your intentions more clearly.</p>
<pre>travelToMountDoom();
destroyTheRing();

function travelToMountDoom() { /* Traveling */ }
function destroyTheRing() { /* Destruction */ }</pre>
<p>However, <strong>function definition hoisting only occurs for function declarations</strong>, not function expressions. For example:</p>
<pre>// Outputs: "Definition hoisted!"
definitionHoisted();

// TypeError: undefined is not a function
definitionNotHoisted();

function definitionHoisted() {
    console.log("Definition hoisted!");
}

var definitionNotHoisted = function () {
    console.log("Definition not hoisted!");
};</pre>
<p>Here we see the interaction of two different types of hoisting. Our variable <em>definitionNotHoisted</em>has its declaration hoisted (thus it is <em>undefined</em>), but not its function definition (thus the<em>TypeError</em>.)</p>
<p>You might be wondering what happens if you use a named function expression.</p>
<pre>// ReferenceError: funcName is not defined
funcName();

// TypeError: undefined is not a function
varName();

var varName = function funcName() {
    console.log("Definition not hoisted!");
};</pre>
<p>As you can see, the function's name doesn't get hoisted if it is part of a function expression.</p>
<p>And that is how variable and function hoisting works in JavaScript.</p>
<p>引用：<a href="http://designpepper.com/blog/drips/variable-and-function-hoisting" target="_blank">http://designpepper.com/blog/drips/variable-and-function-hoisting</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/javascript%e4%b9%8b%e5%8f%98%e9%87%8f%e5%92%8c%e5%87%bd%e6%95%b0%e6%8f%90%e5%8d%87/">JavaScript之变量和函数提升</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/09/javascript%e4%b9%8b%e5%8f%98%e9%87%8f%e5%92%8c%e5%87%bd%e6%95%b0%e6%8f%90%e5%8d%87/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>解读JavaScript的Scope和Context</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e8%a7%a3%e8%af%bbjavascript%e7%9a%84scope%e5%92%8ccontext/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e8%a7%a3%e8%af%bbjavascript%e7%9a%84scope%e5%92%8ccontext/#comments</comments>
		<pubDate>Sat, 31 Aug 2013 01:47:22 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1831</guid>
		<description><![CDATA[<p>本文详细解读了Javascript语言中的作用域Scope和上下文Context。JS是一种特殊的解释型语言， [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e8%a7%a3%e8%af%bbjavascript%e7%9a%84scope%e5%92%8ccontext/">解读JavaScript的Scope和Context</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>本文详细解读了Javascript语言中的作用域Scope和上下文Context。JS是一种特殊的解释型语言，尤其是闭包的概念，使得JS更灵活，还有事件的绑定bind，this指针的用法等，相信本文有所帮助。</p>
<p>JavaScript’s implementation of scope and context is a unique feature of the language, in part because it is so flexible. Functions can be adopted for various contexts and scope can be encapsulated and preserved. These concepts are behind some of the most powerful design patterns JavaScript has to offer. However, this is also a tremendous source of confusion amongst developers, and for good reason. The following is a comprehensive explanation of scope and context in JavaScript, the difference between them, and how various design patterns make use of them.<span id="more-1831"></span></p>
<h2>Context vs. Scope</h2>
<p>The first important thing to clear up is that context and scope are not the same. I have noticed many developers over the years often confuse the two terms, incorrectly describing one for the other. To be fair, the terminology has become quite muddled over the years.</p>
<p>Every function invocation has both a scope and a context associated with it. Fundamentally, scope is <em>function-based</em> while context is <em>object-based</em>. In other words, scope pertains to the variable access of a function when it is invoked and is unique to each invocation. Context is always the value of the <em>this</em> keyword, which is a reference to the object that “owns” the currently executing code.</p>
<h3>Variable Scope(变量作用域)</h3>
<p>A variable can be defined in either local or global scope, which establishes the variables’ accessibility from different scopes during runtime. Any defined global variable, meaning any variable declared outside of a function body, will live throughout runtime and can be accessed and altered in any scope. Local variables exist only within the function body of which they are defined and will have a different scope for every call of that function. There it is subject for value assignment, retrieval, and manipulation only within that call and is not accessible outside of that scope.</p>
<p>JavaScript presently does not support block scope which is the ability to define a variable to the scope of an if statement, switch statement, for loop, or while loop. This means the variable will not be accessible outside the opening and closing curly braces of the block. Currently any variables defined inside a block are accessible outside the block. However, this is soon to change, the <em>let</em> keyword has officially been added to the ES6 specification. It can be used as an alternative to the <em>var</em> keyword in order to support the declaration of block scope local variables.</p>
<h3>What is “this” Context（this指针上下文）</h3>
<p>Context is most often determined by how a function is invoked. When a function is called as a method of an object, <em>this</em> is set to the object the method is called on:</p>
<pre>var object = {
    foo: function(){
        alert(this === object); 
    }
};

object.foo(); // true</pre>
<p>The same principle applies when invoking a function with the <em>new</em> operator to create an instance of an object. When invoked in this manner, the value of <em>this</em> within the scope of the function will be set to the newly created instance:</p>
<pre>function foo(){
    alert(this);
}

foo() // window
new foo() // foo</pre>
<p>When called as an unbound function, <em>this</em> will default to the global context or window object in the browser. However, if the function is executed in <em>strict mode</em>, the context will default to undefined.</p>
<h2>Execution Context and Scope Chain（运行时上下文和作用域链）</h2>
<p>JavaScript is a single threaded language, meaning only one thing can be done at a time in the browser. When the JavaScript interpreter initially executes code, it first enters into a global execution context by default. Each invocation of a function from this point on will result in the creation of a new execution context.</p>
<p>This is where confusion often sets in, the term “execution context” is actually for all intents and purposes referring to scope and not context as previously discussed. It is an unfortunate naming convention, however it is the terminology as defined by the ECMAScript specification, so were kinda stuck with it.</p>
<p>Each time a new execution context is created, it is appended to the top of what is called a scope chain, sometimes referred to as an execution or call stack. The browser will always execute the current execution context that is atop the scope chain. Once completed, it will be removed from the top of the stack and control will return to the execution context below. For example:</p>
<pre>function first(){
    second();
    function second(){
        third();
        function third(){
            fourth();
            function fourth(){
                // do something
            }
        }
    }   
}
first();</pre>
<p>Running the preceeding code will result in the nested functions being executed all the way down to the <em>fourth</em> function. At this point the scope chain would be, from top to bottom: fourth, third, second, first, global. The <em>fourth</em> function would have access to global variables and any variables defined within the <em>first</em>, <em>second</em> and <em>third</em> functions as well as the functions themselves. Once the <em>fourth</em> function has completed execution, it will be removed from the scope chain and execution will return to the <em>third</em> function. This process continues until all code has completed executing.</p>
<p>Name conflicts amongst variables between different execution contexts are resolved by climbing up the scope chain, moving locally to globally. This means that local variables with the same name as variables higher up the scope chain take precedence.</p>
<p>An execution context can be divided into a creation and an execution phase. In the creation phase, the interpreter will first create a <em>variable object</em> (also called an activation object) that is composed of all the variables, function declarations and arguments defined inside the execution context. From there the scope chain is initialized next and the value of <em>this</em> is determined last. Then in the execution phase, code is interpreted and executed.</p>
<p>To put it simply, each time you attempt to access a variable within a function’s execution context, the look-up process will always begin with its own variable object. If the variable is not found in the variable object, the search continues into the scope chain. It will climb up the scope chain examining the variable object of every execution context looking for a match to the variable name.</p>
<h2>Closures（闭包）</h2>
<p>A closure is formed when a nested function is made accessible outside of the function in which it was defined, so that it may be executed after the outer function has returned. It maintains access to the local variables, arguments, and inner function declarations of its outer function. Encapsulation allows us to hide and preserve the execution context from outside scopes while exposing a public interface and thus is subject to further manipulation. A simple example of this looks like the following:</p>
<pre>function foo(){
    var local = 'private variable';
    return function bar(){
        return local;
    }
}

var getLocalVariable = foo();
getLocalVariable() // private variable</pre>
<p>One of the most popular types of closures is what is widely known as the module pattern. It allows you to emulate public, private and privileged members:</p>
<pre>var Module = (function(){
    var privateProperty = 'foo';

    function privateMethod(args){
        //do something
    }

    return {

        publicProperty: "",

        publicMethod: function(args){
            //do something
        },

        privilegedMethod: function(args){
            privateMethod(args);
        }
    }
})();</pre>
<p>The module acts as if it were a singleton, executed as soon as the compiler interprets it, hence the opening and closing parenthesis at the end of the function. The only available members outside of the execution context of the closure are your public methods and properties located in the return object (<em>Module.publicMethod</em> for example). However, all private properties and methods will live throughout the life of the application as the execution context is preserved, meaning variables are subject to further interaction via the public methods.</p>
<p>Another type of closure is what is called an immediately-invoked function expression (IIFE) which is nothing more than a self-invoked anonymous function executed in the context of the window:</p>
<pre>function(window){

    var a = 'foo', b = 'bar';

    function private(){
        // do something
    }

    window.Module = {

        public: function(){
            // do something 
        }
    };

})(this);</pre>
<p>This expression is most useful when attempting to preserve the global namespace as any variables declared within the function body will be local to the closure but will still live throughout runtime. This is a popular means of encapsulating source code for applications and frameworks, typically exposing a single global interface to interact with.</p>
<h3>Call and Apply（函数调用）</h3>
<p>These two simple methods, inherent to all functions, allow you to execute any function in any desired context. The <em>call</em> function requires the arguments to be listed explicitly while the <em>apply</em> function allows you to provide the arguments as an array:</p>
<pre>function user(first, last, age){
    // do something 
}
user.call(window, 'John', 'Doe', 30);
user.apply(window, ['John', 'Doe', 30]);</pre>
<p>The result of both calls is exactly the same, the user function is invoked in the context of the window and provided the same three arguments.</p>
<p>ECMAScript 5 (ES5) introduced the <em>Function.prototype.bind</em> method that is used for manipulating context. It returns a new function that is permanently bound to the first argument of <em>bind</em> regardless of how the function is being used. It works by using a closure that is responsible for redirecting the call in the appropriate context. See the following polyfill for unsupported browsers:</p>
<pre>if(!('bind' in Function.prototype)){
    Function.prototype.bind = function(){
        var fn = this, context = arguments[0], args = Array.prototype.slice.call(arguments, 1);
        return function(){
            return fn.apply(context, args);
        }
    }
}</pre>
<p>It is commonly used where context is frequently lost: object-orientation and event handling. This is necessary because the <em>addEventListener</em> method of a node will always execute the callback in the context of the node the event handler is bound to, which is the way it should be. However if your employing advanced object-oriented techniques and require your callback to be a method of an instance, you will be required to manually adjust the context. This is where bind comes in handy:</p>
<pre>function MyClass(){
    this.element = document.createElement('div');
    this.element.addEventListener('click', this.onClick.bind(this), false);
}

MyClass.prototype.onClick = function(e){
    // do something
};</pre>
<p>While reviewing the source of the <em>bind</em> function, you may have also noticed what appears to be a relatively simple line of code involving a method of an Array:</p>
<pre>Array.prototype.slice.call(arguments, 1);</pre>
<p>What is interesting to note here is that the <em>arguments</em> object is not actually an array at all, however it is often described as an array-like object much like a nodelist (anything returned by <em>document.getElementsByTagName()</em>). They contain a length property and indexed values but they are still not arrays, and subsequently don’t support any of the native methods of arrays such as slice and push. However, because of their similar behavior, the methods of Array can be adopted or hijacked, if you will, and executed in the context of an array-like object, as in the case above.</p>
<p>This technique of adopting another object’s methods also applies to object-orientation when emulating classical inheritance in JavaScript:</p>
<pre>MyClass.prototype.init = function(){
    // call the superclass init method in the context of the "MyClass" instance
    MySuperClass.prototype.init.apply(this, arguments);
}</pre>
<p>By invoking the method of the superclass (<em>MySuperClass</em>) in the context of an instance of a subclass (<em>MyClass</em>), we can mimic this powerful design pattern.</p>
<h2>Conclusion</h2>
<p>It is important to understand these concepts before you begin to approach advanced design patterns, as scope and context play a significant and fundamental role in modern JavaScript. Whether were talking about closures, object-orientation and inheritance, or various native implementations, context and scope play an important role in all of them. If your goal is to master the JavaScript language and better understand all it encompasses, then scope and context should be one of your starting points.</p>
<p>参考文章：<a href="http://flippinawesome.org/2013/08/26/understanding-scope-and-context-in-javascript/" target="_blank">http://flippinawesome.org/2013/08/26/understanding-scope-and-context-in-javascript/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e8%a7%a3%e8%af%bbjavascript%e7%9a%84scope%e5%92%8ccontext/">解读JavaScript的Scope和Context</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e8%a7%a3%e8%af%bbjavascript%e7%9a%84scope%e5%92%8ccontext/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>网站SEO优化之CSS JS代码合并</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/#comments</comments>
		<pubDate>Fri, 30 Aug 2013 03:34:48 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[SEO]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1824</guid>
		<description><![CDATA[<p>在优化网站的过程中，其中一个规则就是尽量减少HTTP的请求数量，比如图片的合并，JS，CSS代码的合并等，本文 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/">网站SEO优化之CSS JS代码合并</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在优化网站的过程中，其中一个规则就是尽量减少HTTP的请求数量，比如图片的合并，JS，CSS代码的合并等，本文介绍了一款PHP的自动合并脚本，可以在网站中使用，只要将原来需要引用JS和CSS的地方，换成该脚本的函数调用，那么脚本就会自动将过个请求合并成一个请求，重而提高网站的打开速度。</p>
<h3>Combining Assets</h3>
<p>One of the basic tenets of making a web-page load quickly is reducing the number of HTTP requests that your page makes. One very common way of doing this, is assuring that all of your CSS files are combined into one request (ideally in the &lt;head&gt; tag), and all of your javscript files are combined into one request (ideally at the bottom of the &lt;body&gt; tag).<span id="more-1824"></span></p>
<h3>The Solution</h3>
<p>I’ve worked with several large sites which each had their own solutions, but recently I found myself needing to speed up a fairly simple site: <a title="Burndown Charts for Trello" href="https://burndownfortrello.com/">Burndown for Trello</a>.</p>
<p><strong>To make things simple (and so that I’d never have to write this again), I made a <tt>StaticResources</tt>system which will allow me to put just one PHP script into the root directory of the site, and use a few simple calls to add files to the header or footer of the page.</strong> The script requires no setup, installation, or configuration to run correctly. However, it has some optional advanced settings (which we’ll discuss at the end).</p>
<h3>Usage</h3>
<p>Usage is very simple. Just make a call to add files to the system, so that they’re included in the header or footer. Both local and external files are added with the same kind of call.<br />
StaticResources - how to add files<br />
PHP</p>
<pre>StaticResources::addHeaderFile("css/GoogleWebfont_Tauri_Latin.css");
StaticResources::addFooterFile("jquery-ui.js");
StaticResources::addFooterFile("http://code.jquery.com/ui/1.9.1/jquery-ui.js");</pre>
<p>After you’ve added the files, make sure that somewhere in your code you print the HTML which will contain all of the files that were added<br />
StaticResources - printing the script and style tags<br />
PHP</p>
<pre>// put this at the bottom of the HEAD tag
print StaticResources::getHeaderHtml();

// put this at the bottom of the BODY tag
print StaticResources::getFooterHtml();</pre>
<p>Here’s an example file of how you’d include a bunch of JS / CSS using this <tt>StaticResources</tt> system. It obviously won’t work if you don’t create JS/CSS files in the locations referenced, but this is very simple code to show the system in action.</p>
<p>StaticResources - EXAMPLE USAGE PAGE<br />
PHP</p>
<pre>&lt;?php

include 'static.php';

// You can add header files any time before the StaticResources::getHeaderHtml() call.
StaticResources::addHeaderFile("css/GoogleWebfont_Tauri_Latin.css");

// You can add footer files any time before the StaticResources::getHeaderHtml() call.
StaticResources::addFooterFile("jquery-1.9.1.js");

// For files that won't change (like a specific version of jQuery), it's often better to host it on your
// own server instead of making a separate HTTP request to the CDN.
StaticResources::addFooterFile("jquery-ui.js");

?&gt;&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;
        &lt;title&gt;StaticResources example page&lt;/title&gt;
        &lt;?php

        // You can add header files any time before the StaticResources::getHeaderHtml() call.
        StaticResources::addHeaderFile("jquery-ui.min.css");

        // A good place to output the HTML is right at the end of the HEAD tag.
        // getHeaderHtml() returns a string (to be more flexible with various PHP frameworks)
        // so be sure to print its result.
        print StaticResources::getHeaderHtml();
        ?&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;header&gt;
            Created for &lt;a href='https://burndownfortrello.com'&gt;Burndown for Trello&lt;/a&gt;.
        &lt;/header&gt;

        &lt;article&gt;
            &lt;h1&gt;StaticResources example page&lt;/h1&gt;
            &lt;p&gt;This page is an example of loading static resources in combined files.&lt;/p&gt;
        &lt;/article&gt;

        &lt;footer&gt;
            © 2013 - Sean Colombo
        &lt;/footer&gt;
        &lt;?php

        // For files that won't be changing
        StaticResources::addFooterFile("stripeIntegration.js");

        // To add a file from another site, just make sure to use the full URL.
        // Since there is no file extension, we have to pass in the filetype as a second parameter.
        StaticResources::addFooterFile("https://www.google.com/jsapi", "js");

        // Output the footerHtml at the bottom of the page. It doesn't have to be in the FOOTER tag, it should
        // be done as far down on the page as possible (close to the end of the BODY tag).
        print StaticResources::getFooterHtml();

        ?&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre>
<h3>The Code</h3>
<p>Without further delay, this is the code of <tt>static.php</tt> that you should put in the main (eg: public_html) directory of your app.<br />
static.php - THE ACTUAL CODE<br />
PHP</p>
<pre>&lt;?php
/**
* @author Sean Colombo
* @date 20130817
*
* This script is designed to be a generalized (not site-specific) tool
* for including static JS/CSS assets in PHP web-apps.  It's a single file,
* requires no configuration to start using it, and makes delivery of static
* assets significantly faster.
*
* BEST PRACTICE:
* Ideally, any javascript that doesn't NEED to be loaded higher up in the page should be
* loaded in the footer (added using addFooterFile()) so that the page can render while the
* javascript is still being requested from the server.
* However, since the page cannot render properly until the CSS is loaded, any CSS used for the initial
* page-layout should be put into the header. Otherwise the page will start out looking unstyled, then
* will re-flow (flicker or adjust) after the CSS file is loaded.
*
* The performance benefits include:
* - Making less HTTP-requests results in a lot less setup/teardown and latency-time being wasted.
* - Good caching headers by default (makes subsequent page-requests faster since the user won't have to
*   make another request for the file they already have cached).
* - Minification: NOTE: NOT DONE YET - this will make the js/css files significantly smaller which decreases
*   the download-time.
*
* This script should be placed in the root directory of your site.  It is both an includable
* script (for building the list of files) and an endpoint (for actually serving up combined files).
*
* Remote files are currently sent as separate requests from local files - they are not bundled into
* the same package.  However, the implementation details are transparent - no special action needs to
* be taken for remote files. Just add them like you'd add a resource that's on your own server, and as
* long as you include the full URL, they will be handled properly.  Example:
*        // Local and remote files are included the same way.
*        StaticResources::addHeaderFile("js/myFile.js");
*        StaticResources::addHeaderFile("http://cdn.google.com/someOtherFile.js");
*
* == USAGE ==
* To output the header files - YOU MUST ADD THIS CALL. PUT IT RIGHT BEFORE THE END OF THE &lt;HEAD&gt; TAG.
*        print StaticResources::getHeaderHtml();
*
* To output the footer files - YOU MUST ADD THIS CALL. PUT IT RIGHT BEFORE THE END OF THE &lt;BODY&gt; TAG.
*        print StaticResources::getFooterHtml();
*
* To add files to the HEAD tag or to the bottom of the page:
*        StaticResources::addHeaderFile("css/GoogleWebfont_Tauri_Latin.css");
*        StaticResources::addFooterFile("jquery-ui.js");
*        StaticResources::addFooterFile("http://code.jquery.com/ui/1.9.1/jquery-ui.js");
*
* To add a file which doesn't have an extension (eg: ".js") at the end.
*        StaticResources::addFooterFile("https://www.google.com/jsapi", "js");
*
* Optional: to specify that this file should be served from a different URL (eg: a Content Delivery Network).
* Put this right below where you include() this static.php file:
*        StaticResources::setRootUrl("http://cdn.example.com/"); // use a trailing slash
*
*
*
* TODO: Add minification
* TODO: LATER: Add options to allow the files to use HTML5 async and deferred attributes.
*/

/**
* For security reasons, we limit the types of files that can be outputted.
* If this wasn't here, someone could request each of your .php files and read all of
* your source-code (and password files, etc.), so please only change this if you understand
* the implications.
*
* NOTE: See STATICRESOURCES_ALLOWED_FILE_TYPES below for the actual array.
*/

// Need to define this way (serialized and inside of define()) because arrays can't be class-constants.
define("STATICRESOURCES_ALLOWED_FILE_TYPES", serialize( array( "js", "css" ))); // all types must be lowercase!

// Character(s) used in the URL to separate file names. This should be a character that's not
// allowed in filenames, otherwise a real file could be split into two filenames.
// For example: "bob-vila.txt" would be interpreted as "bob" and "vila.txt" if the delimiter was "-".
// There is no perfect result for this (since different characters are allowed on different operating systems).
define("STATICRESOURCES_FILE_DELIMITER", "||");

// If this script is being run as the page (rather than just included) then serve up the files
// being requested.
if( $_SERVER['SCRIPT_FILENAME'] == __FILE__ ){
    $files = (isset($_GET['files']) ? $_GET['files'] : "");
    $files = explode(STATICRESOURCES_FILE_DELIMITER, $files); // turn it into an array.

    if(count($files) == 0){
        print "/* NOTE: No files found in the URL.\n";
        print "Please specify the file names in the 'file' parameter, separated by \"".STATICRESOURCES_FILE_DELIMITER."\"\n";
        print "See ".basename(__FILE__)." for documentation on how to use this system.\n";
        print "*/\n";
    } else {
        $fileType = ""; // don't know the fileType until we examine the file names.

        foreach($files as $fileName){
            $fileName = StaticResources::sanitizeFileName($fileName);

            $ext = StaticResources::getFileExtension($fileName);
            if( (!empty($fileType)) &amp;&amp; ($ext != $fileType) ){
                // Warn the user that they're mixing file-types.
                print "\n\n/* ===== WARNING: \"$fileName\" is of wrong file type. ";
                print "It is a '$ext' file, but this is a '$fileType' file. Skipped. ===== */\n\n\n\n";

                // Don't output this file since it's the wrong type. Skip to next file.
                continue;
            } else {
                // If fileType is still empty, this is the first file encountered.  Set correct headers.
                if(empty($fileType)){
                    $fileType = $ext;

                    // Set the content-type headers
                    $allowMimeSniffing = false; // usually, listen to our Content-types instead of sniffing Mime-type. Only exception is when we don't set a Content-type.
                    if($fileType == "css"){
                        header("Content-type: text/css");
                    } else if($fileType == "js"){
                        header("Content-type: application/javascript");
                    } else {
                        $allowMimeSniffing = true;
                        print "/* WARNING: Don't know what Content-type header to set for files with extension: '$fileType' */\n";
                    }
                    if(!$allowMimeSniffing){
                        // Disables IE mime-type sniffing so it always listens to Content-type.
                        header("X-Content-Type-Options: nosniff");
                    }

                    // Set reasonable caching headers.
                    // LATER: Could add a configuration array for tweaking the expiration by file-type. This is a reasonable default though.
                    $SECONDS_IN_MONTH = 60*60*24*30;
                    header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + $SECONDS_IN_MONTH)); // needs to be after session_start if session_start is used.
                }
            }

            // If there is a query-string, chop that off before looking for the file.
            if(strpos($fileName, "?") !== false){
                $fileName = substr($fileName, 0, strpos($fileName, "?"));
            }

            // Read, preprocess (eg: minify), and print the file contents.
            print "/* File: \"$fileName\" */\n";
            @$content = file_get_contents($fileName);
            if($content === false){
                print "/* FILE NOT FOUND */\n\n";
            } else {

                // TODO: PRE-PROCESS THE CONTENT.  MINIFICATION, ETC.
                // TODO: PRE-PROCESS THE CONTENT.  MINIFICATION, ETC.

                // Prints the content.
                print "$content\n\n";
            }
        }
    }
}

/**
* Helper class for creating tags which will import several static resources in a single
* request.
*
* The most common way to interface with this class is through the static functions at the
* bottom of the class (which will all operate on the same singleton).
*/
class StaticResources {
    const ERR_WRONG_FILETYPE = "Error in StaticResources: Tried to add a file with a file-type that isn't allowed.  File had extension '.%s' which is not in ALLOWED_FILE_TYPES in %s\n";
    const ERR_CANT_OUTPUT_FILETYPE = "Error in StaticResources: Don't know how to include code for files of type '%s'\n";
    const ERR_ALREADY_OUTPUT_HEADER = "Error in StaticResources: Tried to add a head file after the head files have already been retrieved. The file that was added late was: \"%s\".";
    const ERR_ALREADY_OUTPUT_FOOTER = "Error in StaticResources: Tried to add a footer file after the footer files have already been retrieved. The file that was added late was: \"%s\".";

    private static $ALLOWED_FILE_TYPES; // we have to initialize this in the constructor because arrays can't be class-constants.
    private $staticResourcesRootUrl = "./";

    // NOTE: Inclusion-order matters for both js and css, so never sort the array of fileNames... always output them in the order
    // received.
    private $_headerFilesByType = array('js' =&gt; array(), 'css' =&gt; array());
    private $_footerFilesByType = array('js' =&gt; array(), 'css' =&gt; array());

    private $_headerAlreadyReturned = false;
    private $_footerAlreadyReturned = false;

    /**
     * Basic constructor - note that most of the interaction with this class will
     * be done using static functions which grab a singleton.
     */
    public function __construct(){
        self::$ALLOWED_FILE_TYPES = unserialize( STATICRESOURCES_ALLOWED_FILE_TYPES );

        foreach(self::$ALLOWED_FILE_TYPES as $type){
            $this-&gt;_headerFilesByType[$type] = array();
            $this-&gt;_footerFilesByType[$type] = array();
        }
    } // end constructor

    /**
     * If your static resources are served from another domain (such as cdn.yoursite.com), then set
     * that here. This string is prepended to the filename (eg: "static.php") when building the URL for the combined-file.
     */
    public function mySetRootUrl($rootUrl){
        $this-&gt;staticResourcesRootUrl = $rootUrl;
    }

    /**
     * Adds the filename to the list of files to include in the head tag for its given
     * filetype.  If the fileName does not have an extension of one of the ALLOWED_FILE_TYPES,
     * then the file will NOT be added and a warning will be given instead.
     *
     * For these files to be used, the calling code must remember to print the results of
     * getHeaderHtml() in the &lt;head&gt; tag.
     *
     * NOTE: The fileName must be structured relative to where THIS (StaticResources) file is,
     * and should not be in normal URL format, but rather in a format that this file can be read
     * from disk.  So doing "/directory/filename.ext" to get to the root of public_html will not work
     * unless the StaticResources file happens to be in public_html.  If this sounds confusing,
     * the simplest fix is to put this StaticResources file in your public_html directory, then you
     * can write file names exactly the way you would have written them in &lt;script&gt;&lt;/script&gt; tags.
     *
     * @param fileType - if this is empty, the fileType will be detected from the filename. Sometimes
     * the filename might not have an extension (some JS libraries don't have file extensions) so you may
     * need to specify this value. Should be "js" or "css".
     */
    public function myAddHeaderFile($fileName, $fileType="", $warnIfAddedLate=true){
        // Make sure this file wasn't added after the header files were already displayed.
        if( $warnIfAddedLate &amp;&amp; $this-&gt;_headerAlreadyReturned){
            $errorString = sprintf(self::ERR_ALREADY_OUTPUT_HEADER, $fileName);
            trigger_error($errorString, E_USER_WARNING);
        }

        // If this file type is allowed, then remember it for later.
        if( $this-&gt;fileIsAllowed($fileName) ){
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $this-&gt;headerFilesByType[ $ext ][] = $fileName;
        } else {
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $errorString = sprintf(self::ERR_WRONG_FILETYPE, $ext, __FILE__);
            trigger_error($errorString, E_USER_WARNING);
        }
    } // end addHeaderFile()

    /**
     * Adds the filename to the list of files to include at the bottom of the BODY tag.
     * If the fileName does not have an extension of one of the ALLOWED_FILE_TYPES,
     * then the file will NOT be added and a warning will be given instead.
     *
     * For these files to be used, the calling code must remember to print the results of
     * getFooterHtml() somewhere very late in the document, ideally at the very bottom of
     * the &lt;body&gt; tag (right before &lt;/body&gt;).
     *
     * @param fileType - if this is empty, the fileType will be detected from the filename. Sometimes
     * the filename might not have an extension (some JS libraries don't have file extensions) so you may
     * need to specify this value. Should be "js" or "css".
     */
    public function myAddFooterFile($fileName, $fileType="", $warnIfAddedLate=true){
        // Make sure this file wasn't added after the footer files were already displayed.
        if( $warnIfAddedLate &amp;&amp; $this-&gt;_footerAlreadyReturned){
            $errorString = sprintf(self::ERR_ALREADY_OUTPUT_FOOTER, $fileName);
            trigger_error($errorString, E_USER_WARNING);
        }

        // If this file type is allowed, then remember it for later.
        if( $this-&gt;fileIsAllowed($fileName) ){
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $this-&gt;footerFilesByType[ $ext ][] = $fileName;
        } else {
            $ext = (empty($fileType) ? StaticResources::getFileExtension($fileName) : strtolower($fileType));
            $errorString = sprintf(self::ERR_WRONG_FILETYPE, $ext, __FILE__);
            trigger_error($errorString, E_USER_WARNING);
        }
    } // end addFooterFile()

    /**
     * Returns a string which contains the HTML that's needed to
     * import the files in the HEAD tag.  This should be called exactly
     * once (and it's results should be printed in the &lt;head&gt; tag) on every
     * page.
     */
    public function myGetHeaderHtml(){
        $html = "&lt;!-- StaticResources::getHeaderHtml() --&gt;\n" . $this-&gt;getHtmlForArray( $this-&gt;headerFilesByType );
        $_headerAlreadyReturned = true;
        return $html;
    } // end getHeaderHtml()

    /**
     * Returns a string which contains the HTML that's needed to
     * import the files at the bottom of the BODY tag.  This should be called exactly
     * once (and it's results should be printed at the bottom of the &lt;body&gt; tag - right
     * before &lt;/body&gt;) on every page.
     */
    public function myGetFooterHtml(){
        $html = "&lt;!-- StaticResources::getFooterHtml() --&gt;\n" . $this-&gt;getHtmlForArray( $this-&gt;footerFilesByType );
        $_footerAlreadyReturned = true;
        return $html;
    } // end getFooterHtml()

    /**
     * Given an associative array of static resources whose keys are
     * resource types and whose values are arrays of fileNames (local
     * and/or remote), this will return a string which contains the
     * HTML that's needed to import those files.
     */
    private function getHtmlForArray($filesByType){
        $html = "";

        // The URL of this script (which will act as an endpoint and serve up the actual content.
        $url = $this-&gt;staticResourcesRootUrl . basename(__FILE__);
        foreach($filesByType as $fileType =&gt; $files){
            $localFiles = array();

            foreach($files as $fileName){
                if(StaticResources::isRemoteFile($fileName)){
                    // Add the HTML for including the remote file.
                    if($fileType == "css"){
                        $html .= "        &lt;link rel=\"stylesheet\" href=\"$fileName\"/&gt;\n";
                    } else if($fileType == "js"){
                        $html .= "        &lt;script src=\"$fileName\"&gt;&lt;/script&gt;\n";
                    } else {
                        // Each file type needs to be included a certain way, and we don't recognize this fileType.
                        $errorString = sprintf(self::ERR_CANT_OUTPUT_FILETYPE, $fileType);
                        trigger_error($errorString, E_USER_WARNING);
                    }
                } else {
                    $localFiles[] = $fileName;
                }
            }

            // Output the HTML which makes the request for the combined-file of all local files of the same fileType.
            if(count($localFiles) &gt; 0){

                // TODO: TWEAK SO THAT THE DELIMITER ISN'T URL-ENCODED. MAKES IT MORE READABLE AND SHORTER.
                // TODO: TWEAK SO THAT THE DELIMITER ISN'T URL-ENCODED. MAKES IT MORE READABLE AND SHORTER.

                $fullUrl = $url . "?files=".rawurlencode( implode(STATICRESOURCES_FILE_DELIMITER, $localFiles) );

                if($fileType == "css"){
                    $html .= "        &lt;link rel=\"stylesheet\" href=\"$fullUrl\"/&gt;\n";
                } else if($fileType == "js"){
                    $html .= "        &lt;script src=\"$fullUrl\"&gt;&lt;/script&gt;\n";
                } else {
                    // Each file type needs to be included a certain way, and we don't recognize this fileType.
                    $errorString = sprintf(self::ERR_CANT_OUTPUT_FILETYPE, $fileType);
                    trigger_error($errorString, E_USER_WARNING);
                }
            }
        }

        return $html;
    } // end getHtmlForArray()

    /**
     * Returns true if the given fileName is allowed to be included, false otherwise.
     * The reason a file may not be allowed is that it's of the wrong file-type. One
     * reason for this is that we don't want attackers to request file types that may
     * contain password-files or source code that some users of this script might not
     * want to make public, etc..
     */
    private function fileIsAllowed($fileName){
        $fileIsAllowed = true;
        if( !StaticResources::isRemoteFile($fileName)){
            $fileExtension = strtolower( StaticResources::getFileExtension($fileName) );
            $fileIsAllowed = in_array($fileExtension, self::$ALLOWED_FILE_TYPES);
        }
        return $fileIsAllowed;
    } // end fileIsAllowed()

    /**
     * Returns true if the fileName is from another site and false if it is from this site.
     */
    public static function isRemoteFile($fileName){
        // If it starts with a protocol (ftp://, http://, https://, etc.) then it is remote (not local).
        return (0 &lt;  preg_match("/^[a-z0-9]+:\/\//i", $fileName));
    }

    /**
     * If the 'fileName' is a local file, this will return that fileName in such a way
     * that the fileName can be loaded from disk safely (without allowing the user to
     * jump out of the current directory with "../" or absolute directories such
     * as "/usr/bin/").
     */
    public static function sanitizeFileName($fileName){
        // Only local files need to be sanitized.
        if( !StaticResources::isRemoteFile($fileName)){
            // Make sure the user can't get above the current directory using "../".
            while(strpos($fileName, "../") !== false){
                $fileName = str_replace("../", "", $fileName);
            }

            // Starting out with current directory avoids abusing absolute paths such as "/usr/bin"
            if(strpos($fileName, "./") !== 0){ // if path already starts with ./, don't duplicate it.
                if(strpos($fileName, "/") === 0){ // path already starts with "/", just turn it into "./".
                    $fileName = ".$fileName";
                } else {
                    $fileName = "./$fileName"; // all other paths that start 'normally' (not "./" or "/").
                }
            }
        }

        return $fileName;
    }

    public static function getFileExtension($fileName){
        // If there is a query-string, chop that off before looking for the extension.
        if(strpos($fileName, "?") !== false){
            $fileName = substr($fileName, 0, strpos($fileName, "?"));
        }

        return pathinfo($fileName, PATHINFO_EXTENSION);
    }

    // ----- STATIC HELPERS -----
    /**
     * Gets a singleton object of the StaticResources type to make it easy for the
     * script to use StaticResources throughout the web-app without passing the object
     * around.  This is probably the most common use-case.
     */
    public static function getSingleton(){
        global $staticResourcesSingleton;
        if(empty($staticResourcesSingleton)){
            $staticResourcesSingleton = new StaticResources();
        }
        return $staticResourcesSingleton;
    } // end getSingleton()

    public static function addHeaderFile($fileName, $fileType="", $warnIfLateAdded=true){
        $singleton = StaticResources::getSingleton();
        $singleton-&gt;myAddHeaderFile($fileName, $fileType, $warnIfLateAdded);
    }
    public static function addFooterFile($fileName, $fileType="", $warnIfLateAdded=true){
        $singleton = StaticResources::getSingleton();
        $singleton-&gt;myAddFooterFile($fileName, $fileType, $warnIfLateAdded);
    }
    public static function getHeaderHtml(){
        $singleton = StaticResources::getSingleton();
        return $singleton-&gt;myGetHeaderHtml();
    }
    public static function getFooterHtml(){
        $singleton = StaticResources::getSingleton();
        return $singleton-&gt;myGetFooterHtml();
    }
    public static function setRootUrl( $rootUrl ){
        $singleton = StaticResources::getSingleton();
        $singleton-&gt;mySetRootUrl($rootUrl);
    }

} // end class StaticResources</pre>
<h3>Using this script with a CDN</h3>
<p>One drawback of serving a combined file is that your server has to read in all of the files, then print them all. This makes the call slightly slower than it has to be. One solution is to use a <a title="Content Delivery Network - Wikipedia" href="http://en.wikipedia.org/wiki/Content_delivery_network">CDN</a> to cache the files on your site. This way, even if it takes your server a second or so to generate the file the first time, it can be served to the next user instantly, from the CDN.</p>
<p>For <a title="Burndown for Trello" href="https://burndownfortrello.com/">Burndown for Trello</a>, we use <a title="Fastly CDN" href="http://www.fastly.com/">Fastly</a> as our CDN (disclosure: my fiance works there, but we use it because it’s free for one backend and it’s crazy fast).</p>
<p>The code to make this work with a CDN is very simple: one extra line below the include call:</p>
<p>How to serve the static assets from a CDN<br />
PHP</p>
<pre>include 'static.php';
StaticResources::setRootUrl("http://b4t.global.ssl.fastly.net/");</pre>
<h3>Code minification</h3>
<p>Another common performance trick is to use a <a href="http://en.wikipedia.org/wiki/Minification_(programming)">minifier</a> to reduce file-size. This hasn’t been added to the script yet, but may come in the future. If you have a favorite minifier, I’ve conspicuously commented in the code where it would make sense to run the minifier.</p>
<p>Minification takes a little while to run, so it’s highly recommended that you get a CDN if you’re using minificaiton.</p>
<h3>The End</h3>
<p>Hope you found the script useful. If you use it on your site, please link to it in the comments!<br />
Thanks,</p>
<p>参考文章：</p>
<p>1. <a href="http://bluelinegamestudios.com/blog/posts/simple-single-file-php-script-for-combining-js-css-assets" target="_blank">http://bluelinegamestudios.com/blog/posts/simple-single-file-php-script-for-combining-js-css-assets</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/">网站SEO优化之CSS JS代码合并</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e7%bd%91%e7%ab%99seo%e4%bc%98%e5%8c%96%e4%b9%8bcss-js%e4%bb%a3%e7%a0%81%e5%90%88%e5%b9%b6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Aspect Oriented Programming (AOP) 设计导读</title>
		<link>http://blog.zhourunsheng.com/2013/08/aspect-oriented-programming-aop-%e8%ae%be%e8%ae%a1%e5%af%bc%e8%af%bb/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/aspect-oriented-programming-aop-%e8%ae%be%e8%ae%a1%e5%af%bc%e8%af%bb/#comments</comments>
		<pubDate>Sat, 17 Aug 2013 00:22:00 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[AOP]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1789</guid>
		<description><![CDATA[<p>AOP通俗来讲，就是在不改变既有代码的基础上增加新功能，比如常用的地方有日志记录，可能我们已经上线了产品，代码 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/aspect-oriented-programming-aop-%e8%ae%be%e8%ae%a1%e5%af%bc%e8%af%bb/">Aspect Oriented Programming (AOP) 设计导读</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>AOP通俗来讲，就是在不改变既有代码的基础上增加新功能，比如常用的地方有日志记录，可能我们已经上线了产品，代码也已经进行了发布，如何在此种情况下，增加log记录，打印出后台函数的调用记录，参数传递情况等等。在面向对象的设计过程中，已经有其他的方式可以利用，比如继承，组合，代理等，但是相对于AOP来说，都没有后者灵活。本文基于JavaScript语言进行介绍。</p>
<p>Aspect Oriented Programming (AOP) is a technique for augmenting the behavior of objects, methods, and functions non-invasively. AOP allows you to add new behaviors and to combine and modify existing behaviors "from the outside".</p>
<p>While there are many techniques for adding and combining behavior, such as inheritance, composition, and delegation, AOP can prove to be more flexible and less invasive in many situations, and it is a worthwhile addition to your toolbox of techniques.<span id="more-1789"></span></p>
<p>To see how it works, let's look at a simple example.</p>
<p>Imagine our codebase contains the following simple object.</p>
<pre>function Thing() {}

Thing.prototype.doSomething = function(x, y) {
    var result;

    // compute some result using x and y

    return result;
};</pre>
<p>Imagine that we use many instances of the object throughout our application via code that looks something like the following.</p>
<pre>var thing = new Thing();

// some time later, and possibly even in
// another part of the application
var result = thing.doSomething(x, y);</pre>
<h4>Adding behavior</h4>
<p>Now imagine that we suspect that <strong>Thing.prototype.doSomething</strong> is the source of performance problems, and we'd like to log profiling information about its inputs, <strong>x</strong> and <strong>y</strong>, the time it takes to compute <strong>result</strong>, and the value of the <strong>result</strong>.</p>
<h5>Modifying all the call sites【修改调用代码】</h5>
<p>One approach would be to log data in every spot that <strong>Thing.prototype.doSomething</strong> is called:</p>
<pre>var start = Date.now();

var result = thing.doSomething(x, y);

console.log((Date.now() - start) + 'ms', x, y, result);</pre>
<p>Obviously, if <strong>Thing.prototype.doSomething</strong> is used many times in the application, this could be <em>a lot of cut and paste</em>. You might miss some places, or worse yet, you might forget to remove some after collecting the data.</p>
<h5>Modifying the source【修改源代码】</h5>
<p>Another approach would be to modify the source of <strong>Thing</strong>:</p>
<pre>Thing.prototype.doSomething = function(x, y) {
    var result;

    var start = Date.now();

    // compute some result using x and y

    console.log((Date.now() - start) + 'ms', x, y, result);

    return result;
};</pre>
<p>While this centralizes the change, it is also still fairly invasive: it requires changing the source code of <strong>Thing</strong>. Imagine if<strong>Thing.prototype.doSomething</strong> were a more complex method, with multiple <strong>return</strong> points and a few <strong>try/catch/finally</strong>blocks. It could be non-trivial to modify the code in a way that allows you to collect the data you want <em>without changing the method's behavior</em>.</p>
<p>If you ever wanted to profile other methods in a similar way, you would need to change their source code as well.</p>
<h5>Using inheritance【使用继承】</h5>
<p>Yet another approach would be to use inheritance to avoid modifying <strong>Thing</strong>'s source:</p>
<pre>function ProfiledThing() {
    Thing.apply(this, arguments);
}

ProfiledThing.prototype = Object.create(Thing.prototype);

ProfiledThing.prototype.doSomething = function() {
    var start = Date.now();

    var result = Thing.prototype.doSomething.apply(this, arguments);

    console.log((Date.now() - start) + 'ms', x, y, result);

    return result;
}</pre>
<p>This approach avoids modifying <strong>Thing</strong>'s source, but has a <em>significant</em> problem: it requires changing every spot in the code that constructs a <strong>new Thing()</strong>, to construct a <strong>new ProfiledThing()</strong> instead.</p>
<p>There are ways to mitigate this problem, but by now it should be becoming clear that there simply has to be a better way to introduce this profiling behavior.</p>
<h4>Unrelated Concerns</h4>
<p>An interesting characteristic of this profiling behavior is that it is unrelated to <strong>Thing</strong>'s primary purpose. It is a side effect.</p>
<p><strong>Thing</strong> has most likely been created to solve a particular problem in a particular domain. The solutions above attempt to introduce this unrelated behavior into <strong>Thing</strong>'s domain, and it's highly likely that <strong>Thing</strong>'s problem domain has nothing to do with profiling.</p>
<p><strong>Thing</strong> need not know anything about profiling to do its job, but the solutions above force profiling concerns directly into<strong>Thing</strong>'s domain.</p>
<p>What we need is a technique that allows us to introduce this kind of behavior in a controlled, non-invasive way. That is, a way that makes strong guarantees about preserving <strong>Thing</strong>'s behavior, and without requiring that we modify <strong>Thing</strong>'s source code or every bit of code that creates or consumes a <strong>Thing</strong>.</p>
<h4>Enter AOP【使用AOP】</h4>
<p>AOP, as we said above, is a technique for augmenting behavior non-invasively. In JavaScript, it is quite simple. You don't necessarily even need tools or a library to apply it, although they certainly help, as does any tool or library that helps you apply a reusable pattern.</p>
<p>If you've ever done the following, you've done AOP in JavaScript:</p>
<pre>var origDoSomething = thing.doSomething;

// Method replacement is a simple form of AOP
thing.doSomething = function() {
    doSomethingElseFirst();

    return origDoSomething.apply(this, arguments);
}</pre>
<p>This effectively adds behavior to <strong>thing.doSomething</strong>. Now, when <strong>thing.doSomething</strong> is called, it will<strong>doSomethingElseFirst</strong>, and then perform the original behavior.</p>
<p>In AOP parlance, We can say that <strong>doSomethingElseFirst</strong> is a behavior aspect that has been applied to<strong>thing.doSomething</strong>. Specifically, <strong>doSomethingElseFirst</strong> is called "before advice" ... that is, we have <em>advised</em><strong>thing.doSomething</strong> that it should <strong>doSomethingElseFirst</strong> <em>before</em> doing it's original job. AOP implementations typically provide many advice types, such as before, after, afterReturning, afterThrowing, and around.</p>
<p>There are several important things to note about this simple example:</p>
<ul>
<li>The source code of <strong>Thing</strong> hasn't been modified.</li>
<li>The consumers of <strong>thing</strong> do not need to change.</li>
<li>The behavior of the original <strong>doSomething</strong>, i.e. <em>its contract</em> has been preserved.</li>
<li><strong>Thing</strong> has no knowledge of <strong>doSomethingElseFirst</strong>, and no dependency on it. Thus, <strong>Thing</strong>'s unit tests do not need to be updated. Of course, we need to write unit tests for <strong>doSomethingElseFirst</strong>, but any new code requires unit tests.</li>
</ul>
<h5>AOPing the example【基于AOP的实现】</h5>
<p>Let's use a similar approach to AOP to add profiling to all the <strong>Thing</strong>s.</p>
<pre>var origDoSomething = Thing.prototype.doSomething;

Thing.prototype.doSomething = function() {
    var start = Date.now();

    var result = origDoSomething.apply(this, arguments);

    console.log((Date.now() - start) + 'ms', x, y, result);

    return result;
}</pre>
<p>We've again used the method replacement technique, but this time we've replaced a method on <strong>Thing</strong>'s prototype. All<strong>Thing</strong> instances will have this new, profiling version of <strong>doSomething</strong>. This type of aspect is called "around" advice because it does something both before and after (thus, "around") the original method.</p>
<p>While this looks very similar to the <a href="http://know.cujojs.com/tutorials/aop/intro-to-aspect-oriented-programming?utm_source=javascriptweekly&amp;utm_medium=email#Using-inheritance">inheritance</a> example above, there is one very important difference: We've not introduced a new constructor, and thus consumers of <strong>Thing</strong> do not need to change.</p>
<h4>AOP in practice</h4>
<p>Non-invasively adding profiling to a single method of a single prototype is a simple way to show how AOP can be applied easily in JavaScript, but the technique can be used to do much more sophisticated and interesting things, such as:</p>
<ul>
<li>Collect profiling data about an entire application</li>
<li>Trace program execution to visualize the call tree</li>
<li>Automatically retry failed asynchronous I/O, such as XHR or database queries</li>
<li>Connect collaborating components in an application in a loosely coupled way without using events or pubsub.</li>
</ul>
<p>In upcoming tutorials, we'll look at more examples of how to apply AOP, and the kinds of problems it is good at solving.</p>
<p>文章摘自：<a href="http://know.cujojs.com/tutorials/aop/intro-to-aspect-oriented-programming?utm_source=javascriptweekly&amp;utm_medium=email" target="_blank">intro-to-aspect-oriented-programming</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/aspect-oriented-programming-aop-%e8%ae%be%e8%ae%a1%e5%af%bc%e8%af%bb/">Aspect Oriented Programming (AOP) 设计导读</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/aspect-oriented-programming-aop-%e8%ae%be%e8%ae%a1%e5%af%bc%e8%af%bb/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>15个绚丽的jQuery特效</title>
		<link>http://blog.zhourunsheng.com/2011/07/15%e4%b8%aa%e6%96%b0%e9%b2%9c%e5%92%8c%e4%bc%98%e7%a7%80%e7%9a%84jquery%e6%95%99%e7%a8%8b/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/15%e4%b8%aa%e6%96%b0%e9%b2%9c%e5%92%8c%e4%bc%98%e7%a7%80%e7%9a%84jquery%e6%95%99%e7%a8%8b/#comments</comments>
		<pubDate>Thu, 07 Jul 2011 03:38:13 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=388</guid>
		<description><![CDATA[<p>Vintage typewriter: The sexiest jQuery contact form eve [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/15%e4%b8%aa%e6%96%b0%e9%b2%9c%e5%92%8c%e4%bc%98%e7%a7%80%e7%9a%84jquery%e6%95%99%e7%a8%8b/">15个绚丽的jQuery特效</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<h3>Vintage typewriter: The sexiest jQuery contact form ever</h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/typewriter.jpg"><img class="alignnone size-full wp-image-400" title="typewriter" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/typewriter.jpg" alt="" width="550" height="431" /></a></p>
<p><span id="more-388"></span></p>
<h3><a href="http://tutorialzine.com/2011/03/photography-portfolio-shutter-effect/">Shutter Effect Portfolio with jQuery and Canvas</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/shutter-effect.jpg"><img class="alignnone size-full wp-image-401" title="shutter-effect" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/shutter-effect.jpg" alt="" width="550" height="302" /></a></p>
<h3><a href="http://line25.com/tutorials/how-to-build-a-sliding-feature-slideshow-with-jquery">How To Build a Sliding Feature Slideshow with jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/slideshow.jpg"><img class="alignnone size-full wp-image-402" title="slideshow" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/slideshow.jpg" alt="" width="550" height="298" /></a></p>
<h3><a href="http://superdit.com/2011/04/02/drag-drop-shopping-cart-using-jquery/">Drag Drop Shopping Cart Using JQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/drag-n-drop.jpg"><img class="alignnone size-full wp-image-403" title="drag-n-drop" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/drag-n-drop.jpg" alt="" width="550" height="511" /></a></p>
<h3><a href="http://tympanus.net/codrops/2011/05/25/image-wall/">Image Wall with jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/imagewall.jpg"><img class="alignnone size-full wp-image-404" title="imagewall" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/imagewall.jpg" alt="" width="550" height="299" /></a></p>
<h3><a href="http://tutorialzine.com/2011/04/jquery-webcam-photobooth/">Photobooth with PHP, jQuery and CSS3</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/photobooth.jpg"><img class="alignnone size-full wp-image-406" title="photobooth" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/photobooth.jpg" alt="" width="550" height="302" /></a></p>
<h3><a href="http://webdesignerwall.com/tutorials/html5-grayscale-image-hover">HTML5 Grayscale Image Hover</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/grayscale.jpg"><img class="alignnone size-full wp-image-407" title="grayscale" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/grayscale.jpg" alt="" width="550" height="376" /></a></p>
<h3><a href="http://www.tutorialshock.com/tutorials/single-page-portfolio-vertical-parallax-jquery-css/">jQuery and CSS Single Page Portfolio, a Vertical Parallax Navigation Experiment</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/Parallax.jpg"><img class="alignnone size-full wp-image-408" title="Parallax" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/Parallax.jpg" alt="" width="550" height="431" /></a></p>
<h3><a href="http://line25.com/tutorials/how-to-create-a-cool-animated-menu-with-jquery">How To Create a Cool Animated Menu with jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/cool-animated-menu.jpg"><img class="alignnone size-full wp-image-409" title="cool-animated-menu" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/cool-animated-menu.jpg" alt="" width="550" height="260" /></a></p>
<h3><a href="http://superdit.com/2011/01/22/shuffle-between-images-using-jquery/">Shuffle Between Images Using jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/shuffle.jpg"><img class="alignnone size-full wp-image-410" title="shuffle" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/shuffle.jpg" alt="" width="550" height="362" /></a></p>
<h3><a href="http://tympanus.net/codrops/2011/04/28/rotating-image-slider/">Rotating Image Slider with jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/RotatingImageSlider.jpg"><img class="alignnone size-full wp-image-411" title="RotatingImageSlider" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/RotatingImageSlider.jpg" alt="" width="550" height="299" /></a></p>
<h3><a href="http://webdesignerwall.com/tutorials/animated-scroll-to-top">Animated Scroll to Top</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/scroll-to-the-top.jpg"><img class="alignnone size-full wp-image-412" title="scroll-to-the-top" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/scroll-to-the-top.jpg" alt="" width="550" height="269" /></a></p>
<h3><a href="http://tutorialzine.com/2011/03/better-check-boxes-jquery-css/">Better Check Boxes with jQuery and CSS</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/check-boxes.jpg"><img class="alignnone size-full wp-image-413" title="check-boxes" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/check-boxes.jpg" alt="" width="550" height="302" /></a></p>
<h3><a href="http://tympanus.net/codrops/2011/03/16/expanding-image-menu/">Expanding Image Menu with jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/expandingimagemenu.jpg"><img class="alignnone size-full wp-image-415" title="expandingimagemenu" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/expandingimagemenu.jpg" alt="" width="550" height="299" /></a></p>
<h3><a href="http://buildinternet.com/2010/02/animate-panning-slideshow-with-jquery/">Animate Panning Slideshow with jQuery</a></h3>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/panning-slideshow-screen.jpg"><img class="alignnone size-full wp-image-416" title="panning-slideshow-screen" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/panning-slideshow-screen.jpg" alt="" width="550" height="316" /></a></p>
<hr />
<p>原文来源： <a href="http://webexpedition18.com/articles/15-fresh-and-brilliant-jquery-tutorials/">http://webexpedition18.com/articles/15-fresh-and-brilliant-jquery-tutorials/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/15%e4%b8%aa%e6%96%b0%e9%b2%9c%e5%92%8c%e4%bc%98%e7%a7%80%e7%9a%84jquery%e6%95%99%e7%a8%8b/">15个绚丽的jQuery特效</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/15%e4%b8%aa%e6%96%b0%e9%b2%9c%e5%92%8c%e4%bc%98%e7%a7%80%e7%9a%84jquery%e6%95%99%e7%a8%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>10个优秀的JavaScript框架</title>
		<link>http://blog.zhourunsheng.com/2011/07/10%e4%b8%aa%e4%bc%98%e7%a7%80%e7%9a%84javascript%e6%a1%86%e6%9e%b6/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/10%e4%b8%aa%e4%bc%98%e7%a7%80%e7%9a%84javascript%e6%a1%86%e6%9e%b6/#comments</comments>
		<pubDate>Wed, 06 Jul 2011 14:01:43 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=381</guid>
		<description><![CDATA[<p>本文推荐几个优秀的JavaScript框架： 1) JQuery-JavaScript Library 2)  [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/10%e4%b8%aa%e4%bc%98%e7%a7%80%e7%9a%84javascript%e6%a1%86%e6%9e%b6/">10个优秀的JavaScript框架</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>本文推荐几个优秀的JavaScript框架：<br />
<img title="javascript" src="http://zoomzum.com/wp-content/uploads/2011/07/javascript.jpg" alt="" width="400" height="267" /><br />
<span id="more-381"></span></p>
<h3>1)<a href="http://jquery.com/"> JQuery-JavaScript Library</a></h3>
<p><img title="jQuery" src="http://zoomzum.com/wp-content/uploads/2011/07/jQuery-e1309936964402.jpg" alt="" width="550" height="310" /></p>
<h3>2) <a href="http://mootools.net/">MooTools-JavaScript FrameWork</a></h3>
<p><a href="http://mootools.net/"><img title="MooTools" src="http://zoomzum.com/wp-content/uploads/2011/07/MooTools-e1309937309871.jpg" alt="" width="550" height="301" /></a></p>
<h3>3)<a href="http://labs.adobe.com/technologies/spry/home.html"> Adobe Spry-Ajax Framework</a></h3>
<p><a href="http://labs.adobe.com/technologies/spry/home.html"><img title="Adobe Spry framework" src="http://zoomzum.com/wp-content/uploads/2011/07/Adobe-Spry-framework-e1309937123734.jpg" alt="" width="550" height="394" /></a></p>
<h3>4)<a href="http://www.sproutcore.com/">SproutCore-JavaScript Framework</a></h3>
<p><a href="http://www.sproutcore.com/"><img title="my_SproutCor2" src="http://zoomzum.com/wp-content/uploads/2011/07/SproutCore_1309799871164-e1309799956593.png" alt="" width="550" height="235" /></a></p>
<h3>5)<a href="http://javascriptmvc.com/">JavaScriptMVC-javaScript Framework</a></h3>
<p><a href="http://zoomzum.com/wp-content/uploads/2011/07/JavaScriptMVC_1309800208352.png"><img title="my_JavaScriptMVC" src="http://zoomzum.com/wp-content/uploads/2011/07/JavaScriptMVC_1309800208352-e1309800267932.png" alt="" width="550" height="289" /></a></p>
<h3>6) <a href="http://raphaeljs.com/">Raphael-JavaScript Library</a></h3>
<p><a href="http://raphaeljs.com/"><img title="My_Raphaël—JavaScript Library1" src="http://zoomzum.com/wp-content/uploads/2011/07/Raphaël—JavaScript-Library_1309800495833-e1309800541787.png" alt="" width="550" height="275" /></a></p>
<h3>7)<a href="http://dhtmlx.com/"> DH<strong>TMLX-JavaScript Librar</strong>y</a></h3>
<p><a href="http://dhtmlx.com/"><img title="my_dhtmlx" src="http://zoomzum.com/wp-content/uploads/2011/07/DHTMLX-JavaScript-Ajax-Library-Components-for-Rich-Web-UI-Complete-Suite-of-Ajax-Controls-File-Uploader-Scheduler-Gantt_1309800677253-e1309800743626.png" alt="" width="550" height="309" /></a></p>
<h3>8 )<a href="http://openrico.org/rico/home.page"> Rico -Ajax Framework</a></h3>
<p><a href="http://openrico.org/rico/home.page"><img title="my_rico1" src="http://zoomzum.com/wp-content/uploads/2011/07/rico.jpg" alt="" width="515" height="270" /></a></p>
<h3>9) <a href="http://developer.yahoo.com/yui/">YUI Yahoo-JavaScript And CSS Library</a></h3>
<p><a href="http://developer.yahoo.com/yui/"><img title="my_yui-library" src="http://zoomzum.com/wp-content/uploads/2011/07/yui-library.jpg" alt="" width="515" height="270" /></a></p>
<h3>10)<a href="http://www.prototypejs.org/"> Prototype-JavaScript Framework</a></h3>
<p><a href="http://www.prototypejs.org/"><img title="my_prototypejssss" src="http://zoomzum.com/wp-content/uploads/2011/07/prototypejs.jpg" alt="" width="515" height="270" /></a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/10%e4%b8%aa%e4%bc%98%e7%a7%80%e7%9a%84javascript%e6%a1%86%e6%9e%b6/">10个优秀的JavaScript框架</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/10%e4%b8%aa%e4%bc%98%e7%a7%80%e7%9a%84javascript%e6%a1%86%e6%9e%b6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery Mobile 教程</title>
		<link>http://blog.zhourunsheng.com/2011/07/jquery-mobile-%e6%95%99%e7%a8%8b/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/jquery-mobile-%e6%95%99%e7%a8%8b/#comments</comments>
		<pubDate>Tue, 05 Jul 2011 06:49:21 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[Web设计]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=317</guid>
		<description><![CDATA[<p>1.  Creating a Website Using JQuery Mobile 在本教程中将告诉您如何利 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/jquery-mobile-%e6%95%99%e7%a8%8b/">jQuery Mobile 教程</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p><strong>1.  <a href="http://miamicoder.com/category/tutorials/jquery-mobile-tutorials/" target="_blank">Creating a Website Using JQuery Mobile</a></strong></p>
<p>在本教程中将告诉您如何利用jQuery Mobile建立一个网站。这个教程的第一部分为您解释该网站的特点并且完成网站的主页。</p>
<p><img class="alignnone size-full wp-image-319" title="Creating a Website Using JQuery Mobile" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/Creating-a-Website-Using-JQuery-Mobile.jpg" alt="Creating-a-Website-Using-JQuery-Mobile" width="500" height="303" /><br />
<span id="more-317"></span><br />
<strong>2. <a href="http://www.elated.com/articles/jquery-mobile-what-can-it-do-for-you/" target="_blank">jQuery Mobile: What Can It Do for You?</a></strong></p>
<p>这是一篇介绍jQuery Mobile主要特性和优点的详细教程，举例说明了如何利用这个新架构帮助你快速建立优质移动应用。</p>
<p><img class="alignnone size-full wp-image-320" title="jQuery Mobile What Can It Do for You" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/jQuery-Mobile-What-Can-It-Do-for-You.jpg" alt="jQuery-Mobile-What-Can-It-Do-for-You" width="500" height="254" /></p>
<p><strong>3. <a href="http://fuelyourcoding.com/getting-started-with-jquery-mobile-rails-3/" target="_blank">Getting Started with jQuery Mobile &amp; Rails 3</a></strong></p>
<p>本教程教我们如何利用Rails 3和jQuery Mobile创建CRUD应用。该应用程序将允许我们通过移动设备创建，读取，更新和删除博客帖子。</p>
<p><img class="alignnone size-full wp-image-321" title="Getting Started with jQuery Mobile &amp; Rails 3" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/Getting-Started-with-jQuery-Mobile-Rails-3.jpg" alt="Getting-Started-with-jQuery-Mobile-Rails-3" width="503" height="319" /></p>
<p><strong>4. <a href="http://devgrow.com/mobile-web-dev-using-jquery-mobile/" target="_blank">Creating a Simple App Using jQuery Mobile</a></strong></p>
<p>在这篇文章中，我们将看看如何利用框架，动态（AJAX）的页面加载，iOS的风格设计和页面过渡等创建一个简单的移动网站。</p>
<p><img class="alignnone size-full wp-image-322" title="Creating a Simple App Using jQuery Mobile" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/Creating-a-Simple-App-Using-jQuery-Mobile.jpg" alt="Creating-a-Simple-App-Using-jQuery-Mobile" width="500" height="242" /></p>
<p><strong>5. <a href="http://www.catswhocode.com/blog/how-to-create-a-mobile-wordpress-theme-with-jquery-mobile" target="_blank">How to create a mobile WordPress theme with jQuery Mobile</a></strong></p>
<p>本教程为您介绍如何创建一个移动优化WordPress主题。</p>
<p><img class="alignnone size-full wp-image-323" title="How to create a mobile WordPress theme with jQuery Mobile" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/How-to-create-a-mobile-WordPress-theme-with-jQuery-Mobile.jpg" alt="How-to-create-a-mobile-WordPress-theme-with-jQuery-Mobile" width="500" height="285" /></p>
<p><strong>6.<a href="http://www.mobiledevelopersolutions.com/home/start/twominutetutorials/tmt1part1" target="_blank">Two-Minute Tutorial  1</a> </strong></p>
<p>这个两分钟教程展示了如何使用AppLaud和PhoneGap Media API播放音频文件，并使用jQuery Mobile实现按钮和文本显示。它也显示了两种方法来访问音频文件：网址或本地上传。</p>
<p><img class="alignnone size-full wp-image-324" title="Two-Minute Tutorial" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/Two-Minute-Tutorial.jpg" alt="Two-Minute-Tutorial" width="500" height="303" /></p>
<p><strong>7. <a href="http://mobile.tutsplus.com/tutorials/mobile-web-apps/jquery-mobile-forms/" target="_blank">jQuery Mobile Framework – A Forms Tutorial</a> </strong></p>
<p>jQuery Mobile是一个相对较新的移动网络框架，2010年10月首次发布。该框架有许多有趣的功能来支持移动应用开发。在本教程中，我们将集中于 jQuery Mobile的一些基本要素：页面结构，形式和Ajax提交表单。本教程是基于jQuery Mobile 1.0 alpha版本。</p>
<p><img class="alignnone size-full wp-image-325" title="jQuery Mobile Framework – A Forms Tutorial" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/jQuery-Mobile-Framework-–-A-Forms-Tutorial.jpg" alt="jQuery-Mobile-Framework-–-A-Forms-Tutorial" width="500" height="286" /></p>
<p><strong>8. <a href="http://net.tutsplus.com/tutorials/javascript-ajax/how-to-build-an-rss-reader-with-jquery-mobile-2/" target="_blank">How to Build an RSS Reader with jQuery Mobile</a> </strong></p>
<p>本教程教我们如何使用PHP和jQuery Mobile建立一个简单的Tuts+ RSS阅读器。学习完这个教程，您可以轻而易举地在iPhone或Android手机上创建Tuts+ RSS阅读器。</p>
<p><img class="alignnone size-full wp-image-326" title="How to Build an RSS Reader with jQuery Mobile" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/How-to-Build-an-RSS-Reader-with-jQuery-Mobile.jpg" alt="How-to-Build-an-RSS-Reader-with-jQuery-Mobile" width="500" height="275" /></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/jquery-mobile-%e6%95%99%e7%a8%8b/">jQuery Mobile 教程</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/jquery-mobile-%e6%95%99%e7%a8%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
