tag:blogger.com,1999:blog-163524332024-03-14T10:38:16.720+08:00Summer。桑莫。夏天summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.comBlogger84125tag:blogger.com,1999:blog-16352433.post-44871671105059775242017-02-04T16:05:00.003+08:002017-02-04T16:19:43.291+08:00部落格搬家摟謝謝大家的支持,我的部落格搬家了<br />
<a href="https://cythilya.github.io/" target="_blank">Summer。桑莫。夏天</a> <a href="https://cythilya.github.io/" target="_blank">https://cythilya.github.io</a><br />
<br />
對於過去一年也整理了-2016總回顧<br />
<a href="https://cythilya.github.io/2017/02/04/2016-yearly-review" target="_blank">https://cythilya.github.io/2017/02/04/2016-yearly-review</a><br />
<br />
當初會寫這個部落格是因為前輩的建議(她目前是一位很受歡迎的youtuber)。她說,寫部落格除了能對自己技能的檢視外,也是未來找工作的作品集之一。很感謝她的建議,為了每個月能順利產出文章,除了規劃技能精進的項目、時程、不斷要求自己多看多想而真的有持續很大的進步外,找工作也非常好用,還有些工作是由部落格連繫到小妹我本人的--在這裡也跟這位前輩再次感謝。<br />
<br />
希望在新的一年,我的技術能更上一層樓,和大家也有更深刻的交流。summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-2717463472131855812016-11-24T22:47:00.000+08:002017-08-08T13:52:15.923+08:00網站如何控制索引範圍?<!DOCTYPE html>
<html>
<head>
<title>網站如何控制索引範圍?</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
</head>
<body>
<p>過去在操作規模較小的網站時,重點往往是增加網站的網頁索引數,但操作大型網站除了增加索引數外,更重要的是引導搜尋引擎爬取正確的路線/頁面,意即有效控制索引範圍。一方面是不浪費索引的額度(Google對每個網站都有一定的索引額度),另一方面也是不增加我們網站本身的流量負擔(CDN等也是很貴的)。</p>
<h2>網站結構</h2>
<p>避免重要頁面埋得太深而不易被索引,重要的頁面離首頁愈近愈好,愈多內部連結連到愈好。</p>
<h2>網址結構</h2>
<p>三層以後的網址易被robot忽略。例如:<code>http://sample/abc/def/ghi</code>,其中「def」是第三層,「ghi」是第四層。網址太長可能會遭到robot捨棄。但根據我的觀察,這樣的狀況是很少的,但如果使用site指令是可以發現有這樣的狀況,超過某個長度後的網址便不完全比對。</p>
<p>因此,重要的頁面要放在三層以內,確保網址不會因為太長而被捨棄索引。</p>
<h2>內部連結要妥善設計,必要時加上nofollow</h2>
<p>避免robot迴圈般的爬取網址,或爬某個需要登入或偽連結,導致連不到重點和深層連結。</p>
<p>為什麼我會注意到這個問題呢?因為在Search Console上,我常看到報錯上顯示一個很有趣的狀況...
</p>
<p>網站上有些功能是需要登入才能使用的,因此當點擊這個連結的時候,如果使用者沒有登入,就會回傳登入頁面。若是robot做這個動作,它就會看到soft 404,也就是想要找的頁面不見了,但又沒有回傳404。</p>
<p>這樣的功能問題在我們網站上好像挺多的,例如:商品檢舉(登入後,點擊商品檢舉連結,會跳到填寫檢舉表單頁)--我常常看到一種報錯--soft 404,來源是某個需要登入的連結,範例:<code>http://goods.ruten.com.tw/item/violate.htm?123456789</code>,這就是因為robot在爬這個商品頁時,遇到這個連結,跟著進去發現需要登入(被轉址導到登入頁)而產生的。這種浪費robot時間資源、對自己網站產生無謂流量的狀況應該要盡量避免。</p>
<p>解決辦法,最簡單的就是當使用者沒有登入的時候,就不要顯示這個連結;或是,在這個連結上加入 「nofollow」,明確告知robot不要去爬。</p>
<h2>canonical</h2>
<p>網站格式統一,避免同一頁但不同網址的頁面競爭排名。使用canonical可能會導致網站總索引數下降,但若下降也代表網站有太多虛報的頁面了。</p>
<p>配合<code><link rel="alternate"></code>,這個指令同時也能使robot得知並索引手機版網頁、AMP網頁,這在<a href="https://developers.google.com/webmasters/mobile-sites/mobile-seo/separate-urls?hl=zh-tw">官方文件</a>上是有提到的。在做桌機與手機版網址對應的優化前,我們家露天在桌機常搜尋到手機版的頁面,或在手機上搜尋到桌機版的頁面,利用<code><link rel"canonical"></code>和<code><link rel="alternate"></code>設定桌機與手機版本網址的對應,這情況已逐漸改善。</p>
<h2>robots.txt:disallow</h2>
<p>避免robot爬到或索引到不必要的頁面,例如:需要登入的頁面、沒有價值的頁面。好處是避免robot不停地去訪問這些頁面,減少流量的負擔。我們家網站在做這個優化後,在search console上可以看到robot每日讀取的byte數逐漸下降。</p>
<p>另外,你可能會有疑惑,到底什麼時候要用<code>robots.txt:disallow</code>?什麼時候要用<code>meta noindex</code>呢?我的原則是,如果這是server端的controller而非view,或是在某個資料夾底下都要阻擋,那就用<code>robots.txt:disallow</code>;而若只是單一頁面,就可用<code>meta noindex</code>。順到一提,如果想要使用search console上在搜尋結果上移除網址的功能,這兩個動作至少要選一個來做。</p>
<h2>search console網址參數設定</h2>
<p>避免robot爬到或索引到不必要的頁面,或建議對某些有意義的參數設定逐一檢索。這個要小心設定,對於網站索引數是有一定大的影響的。</p>
<p>另外,使用<code>canonical</code>或search console網址參數設定的判斷標準是比較模糊的,這邊我兩邊都會做設定,但若參數太多太複雜太難考究,我就會以search console網址參數設定為主,避免網站索引數掉得太多。</p>
<h2>HTML撰寫</h2>
<p>script盡量使用外部檔案,避免robot拆解script中長得像網址的字串,拼成網址後拿去訪問這個網址。同理html中的<code><a></code> tag的href中有 <code>javascript:void(0)</code>,UI元件的tab中有 <code>#</code>,也是會被robot拆解成網址去訪問。這在search console報錯上很常看到,做這項優化也是為了不要產生額外的流量負擔!</p>
<hr />
<p>以上重點是這一年我在露天拍賣網站上,對於控制索引範圍議題的構思、實作與心得,但由於寫文章的時間有限就只能簡短摘要了,歡迎交流討論噢。</p>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/11/24/indexation-for-seo/" target="_blank" title="網站如何控制索引範圍?">網站如何控制索引範圍?</a>。
<hr>
</body>
</html>
summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-30271546350409557062016-10-28T18:33:00.000+08:002017-08-08T13:49:15.208+08:00ES6 - let, const, Block-Level Scope<!DOCTYPE html>
<html>
<head>
<title>ES6 - let, const, Block-Level Scope</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
</head>
<body>
<p>let, const, var, function declaration, block-level scope, hoisting 相關筆記。</p>
<h2>let</h2>
<p>變數宣告,功能類似 <code>var</code>,但有以下特性:</p>
<ul>
<li>block-level scope (塊級作用域)</li>
<li>沒有 hoising (變量提升)</li>
<li>temporal dead zone, TDZ:宣告後才能使用</li>
<li>不允許重複宣告</li>
</ul>
<h2>const</h2>
<p>常數宣告,有以下特性:</p>
<ul>
<li>對於 primitives 的變數,宣告後就不能改變值,因此宣告時一定要設值,並且變數名指向資料;對於非 primitives 的變數,變數名指向資料所在的 address,不可重新賦值但可改變該 address 的內容</li>
<li>block-level scope</li>
<li>沒有 hoising</li>
<li>temporal dead zone, TDZ</li>
<li>不允許重複宣告</li>
</ul>
<h2>Block-Level Scope 塊級作用域</h2>
<p>javascript 只有 global 和 function 兩種 scope,意即若使用 <code>var</code> 與 <code>function</code> 做變數宣告,只能將變數的活動範圍限制在全域或函數中,這可能會產生非預期的結果。</p>
<p>例如:使用 <code>var</code> 宣告 <code>i</code> 當成 for loop 的 counter,當 i 離開 for loop 後,仍可被存取,因此得到 <code>a[6]()</code> 為 10。</p>
<pre><code>var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); //10
</code></pre>
<p>改用 ES6 即可解決這樣的問題,let (和 const)有 block-level scope 特性,離開大括號({...})的範圍後便失效。</p>
<pre><code>var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[2](); //2
</code></pre>
<h2>Top-Level LexicalEnvironment</h2>
<p>Top-Level LexicalEnvironment 在瀏覽器環境指的是 window,在 node 指的是 global。在 ES5 之中,Top-level LexicalEnvironment 的屬性與 global variable 的屬性是相同的。</p>
<pre><code>var a = 6; //global variable
console.log(window.a); //6,a 屬於 window 的屬性
</code></pre>
<p>在 ES6 中使用 <code>let</code>、<code>const</code>、<code>class</code> 所宣告的 global 變數,不屬於 Top-level LexicalEnvironment 的屬性。但為了兼容,使用 <code>var</code> 和 <code>function</code> 宣告的 global 變數仍維持屬於 Top-level LexicalEnvironment 的屬性。</p>
<p>例如:a 由 <code>var</code> 宣告為 global 變數,為 Top-level LexicalEnvironment (在此為 window )的屬性;b 由 <code>let</code> 宣告為 global 變數,不為 Top-Level LexicalEnvironment 的屬性,得到 undefined。</p>
<pre><code>var a = 1;
window.a //1
let b = 1;
window.b //undefined
</code></pre>
<hr />
<h2>References</h2>
<ul>
<li><a target="_blank" href="http://es6.ruanyifeng.com/#docs/let">let和const命令</a></li>
<li><a target="_blank" href="http://javascript.info/tutorial/initialization#instantiation-of-top-level-variables">Initialization of functions and variables</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/10/28/es6-let-const-block-level-scope/" target="_blank" title="ES6: let, const, Block-Level Scope">ES6: let, const, Block-Level Scope</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-29121536199972782032016-09-16T21:44:00.001+08:002017-08-08T13:46:10.549+08:00利用Prerender Node為SPA做SEO<!DOCTYPE html>
<html>
<head>
<title>利用Prerender Node為SPA做SEO</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p><img src="https://lh3.googleusercontent.com/NvzQRdYpbT0jnLhOBFjfViK77KohFn4HYBAzdma63KKrw6_COk5E0f0Js-JVzzHNH7e-6I6GY73EHLvEJvDkodRchsYx4XngLNme5JStcJ_1zFZZTn6b_vwp8K-sLmGx4wghAgUVYVPOAQjULRqvx1VgzHQ90zdCaEmeBFhk4CB7Vf-WMV4M_kZ0wBt6bX1nq4nSHfJphbxmXjrjV0ns8_vJIUqYQX6CKRmvFZlCvftyEQXklPxOxP07IlrTOckmetNJZcL09hMKLQYLOmKG4qgkyhdBe656MfxSy_ze6QZtTegQxIYQsLmVnmCaL_Vw6JeKF87etuowhnbFd2uADWrCBXf7rEzk8w-cuczSfkgYJ1Txlv3YQ-zpT9B6WqI0jFqkg6oL8p4FOylh5Vdf-9K6V3quJMUyhEsObZEorxR9yY8jGQTNhJHJ0Ab-9vBYwVrkBTZSrG253dWFFpYnaZBNQmDzF2YDc6kOYvTizy42Lb70MlFAPc5rL1i5OGdUMAu60wc0UQKkEyRBhfT2vdy5kb8BoovTsGFfpDEvpd_TTWJJK3KFSz2croxLHzaCH-TmkVh6c6vjLtndN01h_aqNcalhvkfN6s6TBCWh8RatbicW=w738-h492-no" alt="利用Prerender Node為SPA做SEO" /></p>
<p>圖片來源:<a target="_blank" href="http://freshlimeinc.com/wake-up-seos-ndash-the-new-new-google-is-here/">Wake Up, SEOs – the NEW New Google is Here</a>。</p>
<p>繼<a target="_blank" href="http://cythilya.blogspot.tw/2016/09/phantomjsandseo.html">利用PhantomJS為SPA做SEO</a>又來一個為SPA解決SEO問題的解法。</p>
<a name='more'></a>
<h2>如何使用Prerender Node?</h2>
<p>依照官方文件操作<a target="_blank" href="https://github.com/prerender/prerender-node">Prerender Node</a>,並設定app.js如下:</p>
<pre><code>var prerender = require('prerender-node');
app.use(prerender);
prerender.set('beforeRender', function(req, done) {
console.log('before render');
done(null, "Hello");
}).set('afterRender', function(err, req, prerender_res) {
console.log('after render');
});
</code></pre>
<p><code>beforeRender</code> 是指robot來爬,但尚未render頁面給robot之前的動作,通常在這裡設定將要回傳的快取頁面;<code>afterRender</code> 是指回傳robot後的動作,通常用來將頁面做快取以備之後使用。在這邊先回傳簡單的字串 <code>Hello</code>,前面的null表示沒有錯誤。</p>
<p>訊息如下。</p>
<p><img src="https://lh3.googleusercontent.com/CbEF-vvlvaCsi6JRYWnuK2zHs4EbZG2FJRFr2wCXNRgc1K_ANvbND2ab5Jm2WsIL-xbxDQNYtrshUag8n6NUsgMzI1SUTMAlZbbs0CkH8lPvhhJrS_h4Fv6q7XrcMuwMnW121HbOoAI8jJnAiw6kTFFEVtl9JeDl4xQWVOwC8MwYVTbQPzVdlMPNqYdrjVO8H93Lvja-TzmQQTdM7M1AKlykmRufMJqT-m9H_jQFo1Il9XzEo8u6c0hrJKiQ-w_EpSz-2b8JtWmPfTJbEIq-btqKKSbCzdV9TdXqiIpLIwPzQsu2YvgMpVOp4ZboG-v4l7zFHuAK__MDtfgUmKB9QdwiY6EZY7a0xk2qmLU4yrQsLI9s-yCngcMSQ_CpjpcrAZMbpyJK_nCNcGw8_0VgHH2kQTwDUEeXUjL-4kHmdS4hxzSU_9c4-sHmEwS7rzQ-eVIZIumCj5uPT7nc2xN7FnQ-7sL_iQm-kih0qshcH4FGyO-tByT07QDs5fXdqMikmgfm9sdqP6t-NeX-cAHU0xigq2s-kYWPhj0HgNq_sopv4Y9ta0TyRWnBjtdY-QSuebFmTWkNdlWn32QNnoTv4Mzy0KI28ltHpewyJJfP-vHOm_BJ=w237-h59-no" alt="利用Prerender為SPA做SEO" />
</p>
<p>頁面出現剛剛設定的 <code>Hello</code> 字串。
</p>
<p><img src="https://lh3.googleusercontent.com/SV0JRb9IFM7HE2mOx_rGqwvx179P3cuYUFcQBKHtvaLGXHWelLcfK6-oEZTXnPJv5HXrbzHJxO5G7QaUCdvbFitsx2rkyPYlOD2Zk-rHnc0raA-jt-RazwLCkAKmqFMXVncx1MDiurqt-UbiRAv2T0PDgx9J4Fnxc5sz8sjmcH33P6yXrb8tWyFhObRVMYidz01j5uYC53KsDMHxqqIp1cn4fPaJ26O8NCZwabKnj44hGREtviEvnTocRh7u4maTMPDbxGxi9wysYTifZuXWvo-uYQxVBjBsmD5p0327BjFLx6_cRrYGXhOUE7QN3o4ps5lsQ_MnKU1FxhyBOdxqHTEr_z2cklRAGJhIbua7GWfuguD9GkfCeb7iQwIhZCBVhla0nVkVetGH0Hv0joYsI-13FSlMJngoaxTIKKEMB8SRyQLbWuxZZABmm68zzTpmAITLilCqSLLXh6jdy2zdYDVQ8sRPea9nDbKE_A3hGsatpgzXMtunmXA7yah28nxiv7RSh1Eufshy8lpOgN5zqb59pkSfE50VoQouRg2jlk5uEB7lydb3S6UHJ_sOb5jCwWJ0UiigIhxfGfLC-jzyD1JTxN8RNvSODIMhcgfLFYClrY-r=w261-h94-no" alt="利用Prerender為SPA做SEO" />
</p>
<h3>讀取快取頁面</h3>
<p>經過了剛剛簡單回傳字串的方式,當然我們也可以設定回傳一個已經快取好的頁面 <code>index_cached.html</code>。</p>
<h4>設定</h4>
<pre><code>prerender.set('beforeRender', function(req, done) {
fs.readFile('./public/index_cached.html', 'utf8', function(err, data){
if(err) {
console.log(err);
done();
} else {
done(null, data);
}
});
}).set('afterRender', function(err, req, prerender_res) {
console.log('after render');
});
</code></pre>
<h4>快取頁面</h4>
<p>快取頁面的程式碼如下,這不是一個真的快取頁面噢,只是範例檔。</p>
<pre><code><!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Hello</title>
</head>
<body>
<h1>Hello</h1>
</body>
</html>
</code></pre>
<p>頁面出現剛剛設定的快取頁面。</p>
<p><img src="https://lh3.googleusercontent.com/_LCPn8bs4KE60P0c21S0A92b-uA_oJOCS6h5b5A1gESolU3yIUG2yIzZd0cBPvwsaunvuqbEcN3srz2RZO7NO0y4ut3M5CFkbv7bqEUtdV0vsRVFEADgvmBmONpFkEM6bWCxB3HAz3S2pvK1m2brjN_eiRpTbtGWZoRSulL2SC9EOi9em3NNCXNtBeZmELDMVhBE61oadVWDbkpGKHuSgMaoJQhGUmj5EeT6FpszjdyjzGIYUy_z5TvcWrXA0LxvBj6tLb0tIEdIyQ3-iaoHVCmOJdf4CRSNLNMVvs237YjOa1e9HOL2kydTzKyHumN4HqFKljf5eN9_TA7gJbmlXVrJ0ikuLjZmh48kOkxs57SkhSxqLU5SR4MlHtqId9-VSCf1kmI2A2bKLy0bl3HrdK3R2GOR9zRlCyIDgXbfg8P0Euysx_NcRnWID3_8TxT60wvXyNGHAm4n1o1A_uCRuejuzb9VGO24-e2hbN9nO4QY8qQ7Mq2aEt9GDV8GKL1Yv92gov9mpa-gJ9ogRXMRtfpzmtoMGm6lx-eC8nupNCZmb3A7Gwq2tj4BcpsQ3h53LKfMIOQetVbhhkoF_B3_GSR-xT-WzEvBKnPqdEfqlxJxwZG4=w267-h122-no" alt="利用Prerender為SPA做SEO" /></p>
<p>也可以使用<a target="_blank" href="https://github.com/prerender/prerender-node#caching">Redis</a>等來做快取頁面。</p>
<pre><code>var prerender = require('prerender-node'),
redis = require("redis"),
client = redis.createClient();
app.use(prerender);
prerender.set('beforeRender', function(req, done) {
client.get(req.url, done);
}).set('afterRender', function(err, req, prerender_res) {
client.set(req.url, prerender_res.body)
});
</code></pre>
<h2>原理</h2>
<p>看了原始碼之後,Prerender Node的運作方式是這樣的:</p>
<ul>
<li>檢查User Agent中的字串是否有robot名稱(檢查對象有Googlebot、Bingbot等,但是寫死在程式碼裡面的,無法新增),如果有符合的robot名稱,就回傳快取頁面。 <!-- - 如果Header中含有Buffer Agent,就回傳頁面。 --></li>
<li>如果URL中含有 <code>_escaped_fragment_</code>,就表示是robot,要回傳頁面。</li>
<li>檢查頁面是否在白名單或黑名單裡面。若是robot要求且在白名單裡面,就回傳頁面;如果是robot要求但在黑名單裡面,就不要回傳頁面。</li>
<li>若robot請求的是頁面而非資源(例如,附檔名為.js、.css的檔案,這部份也是寫死的),則回傳頁面。</li>
</ul>
<h2>備註</h2>
<p>如果測試的時候發現怎麼頁面對常用的Googlebot都沒有反應,這是因為<code>prerender-node</code>的<code>index.js</code>這個檔案把我們常用的Googlebot等都註解掉了,記得要打開才會看到正確的執行結果。</p>
<p><img src="https://lh3.googleusercontent.com/QJGuknIMz9biGJYMAWOoNiPRZ7Nvtv-76sAR96NiRugf9eAcZWv3xFNuz8_a7l3j_juIFRy2zjm3M5e5rEDbl_jyhP90ZDKYT8wW4hGTTqCZeyOp_w3lwPBYYipNVqTM74aA1-GJMUCPl6v4Ea07GiK63VEMmRWrqbx4GM2CK1VjWH5F7Lc758dEwa9k2NnaplQIxvEO-7bNQt5SFuVKbclVVgTzwblI_XOeC55N2uQ4Wq1uYkyISBig3kpjydW4Y2PcWuVFMGvNgDK8PB5hxhzmjIaAasDAfP4Dtgq3Mz3IfObx_nWBE18xUZQiklQp4F9qfbUHHhLFixOvgAbrkp_BetkIMqou-odJSdTXE6zpht298O0i36DTL5eDvykiU4B8uaPNwoIumdzutP4j1tthl8oWppSGm0dMXFD7i8cAaV1bYUbDNI6iL3kHFKEeSAtZGQupii4kz_qchGrx41N4FxuaQ7dYb5cu7C67_fTigJ5PsqliGUvTtncEyg-rDvStRtdKjNBiNR1_qa_8YlGzgf151C325uTid1dsXAfK5EE5rsvzg6CGKOvky1JF0tynqQHTEk22v0WH9TO4WavYzEQG04_48rp0bUoj8LHU-1Be=w401-h100-no" alt="利用Prerender為SPA做SEO" /></p>
<p>作者註解這一段的原因有寫在程式碼裡面</p>
<pre><code>// googlebot, yahoo, and bingbot are not in this list because
// we support _escaped_fragment_ and want to ensure people aren't
// penalized for cloaking.
</code></pre>
<p>大意是說,因為支援偵測URL是否含有 <code>_escaped_fragment_</code> 且為了避免網站被誤認為黑帽技法而被懲罰,因此註解起來。但<code>_escaped_fragment_</code>已被google廢棄,並且在此對於robot或使用者我並沒有回傳不同的內容(只是render的方式不同,一個是後端,一個是前端),所以就手動打開吧。</p>
<h2>總結</h2>
<p>與<a target="_blank" href="http://cythilya.blogspot.tw/2016/09/phantomjsandseo.html">利用PhantomJS為SPA做SEO</a>比較起來,這次使用Prerender Node解決了執行兩次回應和每次都會重新產生靜態檔案(而能使用快取頁面)的問題,相較之下是更能減輕server端負擔。而且,更模組化的包裝起來了。</p>
<hr />
<p>同場加映,自己建立Prerender服務!</p>
<h2>自己建立Prerender服務</h2>
<p>如果只是小小的部落格網站,使用<a target="_blank" href="https://prerender.io/">Prerender.io</a>這個服務即可,但免費帳號是有快取頁數限制的。若頁面數量很大,就可以利用它們的開源專案自己架一個服務來用。</p>
<p>Step 1:下載<a target="_blank" href="https://github.com/prerender/prerender">Prerender Service</a>。在local端run起來 <code>http://localhost:3000</code> 會看到以下訊息:</p>
<p><img src="https://lh3.googleusercontent.com/-U0nQweksTOES0U1cb6PMQgxHDDuFrIXRBhFRax6sTM-P_mPMEQpQPmwM_74k4xlcka3c8xJcmVMqewUJafW560cB7eQXwlhmVxic_swhkwv-1C3N21yM6d_YGxEWDfdm6fCQqqK-jWQTpNhKQ0xFdyFsS-Yt7gFHAimlMVbs3bI3tsxPBMVfOFIdT4i6Nn4tPFyKIGUtxUBRgwxTLNzPTGUEj8xOSc_mxyGUVWcZKS1g7Xi-FmWWRc2TUVziifAHSbHF_iN84YvoI7FuWd-feErKn8t606g_GtW6ivIJovumR1LVugdQBJKhJh7-w_Ayw9Gix5qj_yFC_ZQIU01Zi8ECkEx8Cl-Hpb5ieHAV4MzKIZMF3M8ETZRlurr-0ucUqYPTFJ62LhI5mJ-LPDsdTXpAcHSX0Rnk4WdNub6hNxyivaOWYil5tgBRDdycMq0mOAzlesGrD0_fFb1IEog5s7SQaWlaIqvLoxouONuf6hv__U4K7cywap1Sh3hTatPOrzLCMpJ5UqNT5dXiRa7Tc0tG4NfqA01i9o86TB2TNfc1jZ2b04ubAfrS7bZm3L9MaP2K2HKgsGgevdtco5yd1BttpJ77yKcCnTj38-i7wqroHvw=w425-h238-no" alt="利用Prerender為SPA做SEO" /></p>
<p>Step 2:在自己的專案修改app.js,修改port號,將port 3000讓給Prerender,自己的專案改用8080。</p>
<pre><code>app.set('port', process.env.PORT || 8080);
var server = app.listen(app.get('port'), function () {
console.log('Ready on port ' + server.address().port);
});
</code></pre>
<p>Step 3:在瀏覽器鍵入 <code>http://localhost:3000/http://localhost:8080</code></p>
<p><img src="https://lh3.googleusercontent.com/aGuvz7mpkrIADkKq1Bh-igAnQaUWe1b46CUcw9a0VKY6HYbz2RrAD_uVKyQq2gciinkB4tST0nRd3_1rdoVVjXXDJpskd0Xh8TkLFJzGgBVzkS7Ze82y1sAKQSIhvmZvDaXIaP1KCX6yHCFKuJkLMV_GEwMgaemH_vVv4RKSwh3agbDcTcSDcSm8xkzaBj1Dt1YUdCV486pVPsZ7WSD1lDx96JuLL-5ZtmiAw0FrRiF8wJ_aI4-wCUJkuCJF39zv4fNS5UMVWhs5g4CgcryMOg-Q8PRLFARIhM3crlQ6pcyDdFzfIDG2OORXyCRnpDLe925HCF3YMxy-UWBBrnbn8P9D6fM821g2lkTx4fZyuGatOZQAC325qof391oaaCh1crm1hE2_4KFa03pLEvOoibXq1IOLUMDGlPkdPg30RkOJFsgQ8QMYdFA6G8B5BH5T-zt4quQjjLkRtVHlKrV-5Mg5OZddcP9SpqRo1DG5sOWG7AB8JfGi7iGAPpoRFx5dDKLQGrS7VcsHVwsGE7aM58dD8dr_XAQBC5HZ4t8tpLfqrQNN3eNjpeLAfJbECSeg4Jte6PVLELS0prBUCYSpmLMfHsYCw3JeX9Bi1PipRg42sNvv=w635-h436-no" alt="利用Prerender為SPA做SEO" /></p>
<p>來看看結果,可以利用自己建立的服務看到自己的專案了。</p>
<p><img src="https://lh3.googleusercontent.com/WZwjYc5IWwm94r-xhWgrK_STqh3S0-igbvEqkDBbpL8TXTJE0fUrq45TLw5by_v9rl8DRbcv0T3gjktigqsZP27WkiNoI76OEThLv8rVFsyR2YrpGscJ7geMltTvD-IDGtgLFFarKugdQ6WfXpHXoe1tGdm2jKJQOyUS7KPCU0YxdbRNNFjnf3cWhqK7Fc9IPCYpQPLKyHo2kH1-81pbI21OR6ocMpa1il9Yr9Fi-XlnRBg2oFVqSFsC1cYLkl63CUo5nkR7reSmkHlbb7F33sEePIahFL5_0-nVKiOVtLIYvqRaFUCtsff_nTR9UMKu3K0bbl6SJ3z1aOM_rNeNVYxZsRBOCYJ3n6vPK9G2Dr4k_QT87KPSMfomXaCNW0p8Wa3wPmNdwUz7BvHMr20_E5ENxHwjWJtWvgi8MceZJ_qBc8iaXmXawndMICKcIz78NNNefkKIb2b4p-N6FdiQ3tGKVHdzjiHWvckEHileteIVZmtcC_ynRnf_AqJIIYiXI0XbSy_2df5fjSXEKQuetwmqeJ19jVxlNGypSoiDmwcKiatMq9YC9Uc2VfjRKMT_uFVE8M-SOA5mpcM2mGGHOjF2M-oyuxFe4aJ3JKgajra0OfRy=w600-h398-no" alt="利用Prerender為SPA做SEO" /></p>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/09/16/seo-prerender-node/" target="_blank" title="利用 Prerender Node 為 SPA 做 SEO">利用 Prerender Node 為 SPA 做 SEO</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-25712303891038166662016-09-11T17:29:00.000+08:002017-08-08T13:42:49.405+08:00利用PhantomJS為SPA做SEO<!DOCTYPE html>
<html>
<head>
<title>利用PhantomJS為SPA做SEO</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
</head>
<body>
<p><img src="https://lh3.googleusercontent.com/KKkQ3Jr4sw43BgHyCSYnd_p3gUnMVH12DTn6s4O7d3t4QfBHv3nWVrwHSZarr68bdQNO0bHdhUKXz93oJbr_V_dXeRdFEy40eoMXy3uTh4wqQunHZukvA033DUhlvIPyss8yQIeW3eKsE1aDAFdMCDpcXRKlyxATZe5K-pPX0fgJSXrGiDAJXAGBXn8kFuuDLwbO_eGe4jsQeG3NpFOlvwxcDV6_Y3MapCvpPHG-b7lZJ8kfyZh3trYkQWQtCMQ52a9w65EXNEjRrjYqfguuH7XLQNLd7LmXzuxnhAjQb46sPevEPLxeq2nwn4eBZmgFE-GCMRQ8FJ9N-34TOH7ub3nId5CsVqQsv8aK-F71QZj5UM_Or1GocefZrA8HqKSuHiQAhmAKTwYcWBTgrSL7qajjxH_iICgLsdyH2x0dFJvEjJMjroNmwGs6ROtHElx08Y4MVDj6ceBjfQWFfL7BzEdWi56M_7EL4GLVTSuk694VharVuyiqaoHPc_OI8VDOtL1pwJzwrv-UASj8S8dx6R1PIzWKtZDqj8P3Xy2gKuLE0Lb0WZddlNqKgGWgRlFNy_kYVy_NdZLaJajM6CVcpcNAB76ZYn-D_3mGUOtKrh2EtUrO=w800-h494-no" alt="利用PhantomJS為SPA做SEO" />
</p>
<p>隨著前端技術的不斷更新,使用前端框架(React、Vue等)來建構網站是普遍現象。但目前搜尋引擎無法有效讀取使用JavaScript render出來的頁面,且使用<a target="_blank" href="https://developers.google.com/webmasters/ajax-crawling/docs/getting-started">AJAX Crawling</a>的解法只適用於Google又此法已被廢棄,因此另一解法就是使用PhantomJS。</p>
<p>這裡使用 Node.js + PhantomJS 為SPA/以Ajax讀取資料的頁面做SEO。</p>
<a name='more'></a>
<h2>說明</h2>
<p>以下程式碼是我為某個頁面寫的範例。</p>
<p>頁面載入時會用ajax取得一行字串「今天吃飯,該吃什麼好呢?只要輸入美食欲望,立刻給你最真實、現場、生活化的評價,找餐廳再也不煩惱,就讓吃什麼,どっち幫你決定吃什麼!」,然後append到畫面上。</p>
<p>程式流程:當讀取頁面的時候,先去判斷是不是robot,如果是robot,就使用PhantomJS丟給它已讀取好的靜態頁面內容,說白話些就是預先載入頁面後取得頁面內容;如果不是robot,就走一般的流程,也就是render樣版。</p>
<p>PhantomJS和Node.js是不相容的,因此使用了一個中間橋梁<a target="_blank" href="https://github.com/amir20/phantomjs-node">phantom - Fast NodeJS API for PhantomJS</a>。但這不是官方的,可能會因為各種原因崩解。</p>
<h2>程式碼</h2>
<pre><code>var express = require('express'),
router = express.Router(),
phantom = require('phantom'),
fs = require('fs');
router.get('/', function(req, res, next) {
var ua = req.headers['user-agent'],
fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl,
pattern = /Googlebot|Googlebot-News|Googlebot-Image|Googlebot-Video|Googlebot-Mobile|Mediapartners-Google|Mediapartners|AdsBot-Google|AdsBot-Google-Mobile-Apps/i,
isRobot = pattern.test(ua);
if(isRobot) {
var sitepage = null,
phInstance = null;
phantom.create().then(function (instance) {
phInstance = instance;
return instance.createPage();
}).then(function (page) {
sitepage = page;
return page.open(fullUrl);
}).then(function (status) {
return sitepage.property('content');
}).then(function (content) {
res.send(content);
sitepage.close();
phInstance.exit();
}).catch(function (error) {
console.log(error);
phInstance.exit();
});
} else {
res.render('system', {
title: '吃什麼,どっち',
description: '今天吃飯,該吃什麼好呢?只要輸入美食欲望,立刻給你最真實、現場、生活化的評價,找餐廳再也不煩惱,就讓吃什麼,どっち幫你決定吃什麼!'
});
}
});
router.post('/getInfo', function(req, res, next) {
res.json({
isSuccess: true,
response: '今天吃飯,該吃什麼好呢?只要輸入美食欲望,立刻給你最真實、現場、生活化的評價,找餐廳再也不煩惱,就讓吃什麼,どっち幫你決定吃什麼!'
});
});
module.exports = router;
</code></pre>
<h2>測試結果</h2>
<h3>未經過PhantomJS加入與修改之前</h3>
<p>未經過PhantomJS加入與修改之前,robot只能看到這樣的狀況--經由Ajax取得資料後append到畫面上的區塊是空的。</p>
<p><img src="https://lh3.googleusercontent.com/B4JE9DPSgkQ_O2LmIjIwnC-UHrZe8tNoTghYS6rKeyFc3VuVJuJg0YpD9R1bqYyeTml2KGcgmtsnnln8fUYrOqDbfoi_4OdT-wcnN4N4i2pC0FcbBxvsqARkFjhmb8ib27NynfRY1q4WimhKI8QPmPmBKsMNOna0vbDZHfgr2czEqqLLcnn1lS-fHW8cpLj8n4JIBUkNgFjZJU1MlOv89SB_kQ2bUfgLyw1CKuOBCKczK_WAWvvFAvTL1xq5H1SPUfHijJa9zl_PnU-I1uPHTyOkE2YitviGAwB8pyqoKLjhhW_2tS2ZT4aJwOhTX4xfqpJceiAfI6YpAf8TzRJGjk9aEEsrO3_rJ9CXKYr_2nyezs0tCKep_HSav_t8IvwMIxyeLRnc4Y-atHrbm-9tfqd6WVSXNqQcVXT3nlRObFmJDghn9HIzmuBOItuYOSpEQ8xNjZPNsxf8QJXwDzNGUqqrL31mfo-OY6T21CDjO17qstEoLbVtB1WrHuEzyGRbbi88Kt_KkyN6cIPhvjmNlbhYqhXnQF7YBS9WQ4Fr61sbRGMRzHNL72Da78LDZvcj1LVoIGCfnQv-PACA696nHMfSdMKf48ZwCSKu8Lhn4UHtz6g2=w800-h579-no" alt="利用PhantomJS為SPA做SEO"></p>
<h3>PhantomJS加入與修改之後</h3>
<p>一般使用者(非robot)檢視原始碼時會看到這樣的狀況,經由Ajax取得資料後append到畫面上的區塊依然是空的。
</p>
<p><img src="https://lh3.googleusercontent.com/nhZslp1z96YWqMErYuSW4PXsnxgqnmSq-_Nndf7MZ0MmLuk7Tqwad-QIOZa4O3Wsx7NEBoJdtxaIOKtTM8c_utoYd_UaXqeazA3zdN9Danu0w5PZWqmUSFzoSzKkDyHKloBgr0L5QA7G1cZ5aXdRfa-KyEEM-AGU7uR-Ld0AYb4eMOw9aBUbxeDDggqmdTDLSuOBjxQXg2bUBTPcrtzbOhxSR_T_YkiIUPU0NdirGubCYAwVPI16hUX6nNlC7FWvN0lXF_X4t3daDClSnLv901TYmLXxCK-_4JYBu7yC5gxo3nt-c87AiKPd2vrjnGrsaU1mmDfYJUYucXjijakLqNxpUAQikAv7ELB6GbdrIw0KixSt5Mz2SFnMuBWEnnrQng2FMxEuSbfxngk0lT5RCENIVntUK7sJFhMHFBTGADVw4PTUQ9aXX0HxWiLQgZbVSbUXswXYPlD5s1t2IYJCUAMVDj2Ep7RrSkjdK2DmhLFOwcUJnPQAx5OijpYAJbIMwFfh86SquvL53aOiWx_bvVd44H5wFh3g-Wo8JL9VhAAwdcgg-PrLM3lPlIR-Z6twP_1x_LKwCX95lveX6O0UO1YAjbaP1pEi6LFIUEg6m_i3gihA=w583-h105-no" alt="利用PhantomJS為SPA做SEO"></p>
<p>但...使用webmaster的測試工具來測,robot可以檢視到經由Ajax取得資料後append到畫面上的文字。</p>
<p><img src="https://lh3.googleusercontent.com/v-4ZOsmpYkxE2Oo81JlRbljDBRrl9BdNIOZrcYPYlPa7LGy37RCdW0DAxKCUEpzcfjBzBVpMEtBPhC8lH5qghGENErWVOhlgkS1OrZCTK8NDeNG_WpJgaYxB5ynZ06mJiDuGoK6zkgRuN3Z8J2Tdg5y-fO9qiLVZyj_SLlpvpSlbwBruXT_T_agV39P7k5kKlgJfDTZowLNcnPmluwH3nFa4V-4VsJY8T1Qgn9E2mIGabQSH4l_zl7RIh2OJGO72c3XsmqlWNzvjWSkpL3TDFADc6fh6ZH9ZYr7JpXTzlJBVLD2S9ig8ARVVNUYs7B1vJ21jfhas4Hz-YhrfPQ6V3IECRpvnTmwfinU8dAhEU52l78pXuPB6BPi3ntbp_4YRFQQoDD1GTDJX6NavCWAo0rAjNG5pDNkRT6IJSkM8vLTenHI5izfsKTJKZX_stQoIjm7T4mZ9k6VtLXJKf2JvkL7rYinucriZMsWjQznl-3QLMt58cWd05V1-4PMJuyvcd5c0AxBIYRZcbIthbS03rzkCpeKUdDnxSyiIZ-3_1_rZF6KFb2kvJTXouDxu5V8IWGXPSqtdg_Tya_nO0kCKV3gL6S8qNIlY6E6D8k78kdTrqf5c=w800-h579-no" alt="利用PhantomJS為SPA做SEO" /></p>
<h2>總結:優缺比較</h2>
<h3>優點</h3>
<p>可讓robot讀取由JavaScript render的頁面。</p>
<h3>缺點</h3>
<p>每一次robot來訪時,就會執行兩次回應:一次是robot讀取頁面,一次是PhantomJS打開頁面 (程式碼中 <code>return page.open(fullUrl)</code> 這一行),增加Server端的負擔。並且,phantomjs打開頁面後產生的靜態內容是每次都會重新產生的。</p>
<p>由於這些缺點,因此還找了其他解法使用。例如:使用Prerender.io或自己實作後端render的服務,後續會有相關文章說明和實驗。</p>
<h2>推薦閱讀</h2>
<ul>
<li><a target="_blank" href="http://www.rapospectre.com/blog/38">前端渲染與SEO優化踩坑小記</a></li>
<li><a target="_blank" href="http://f2er.info/article/29">用PhantomJS来给AJAX站点做SEO优化</a></li>
</ul>
<h2>參考資料</h2>
<ul>
<li><a target="_blank" href="https://github.com/amir20/phantomjs-node">phantom - Fast NodeJS API for PhantomJS</a></li>
<li><a target="_blank" href="http://phantomjs.org/quick-start.html">PhantomJS | PhantomJS</a></li>
<li><a target="_blank" href="https://vickev.com/#!/article/easily-index-your-single-page-application-thanks-to-phantomjs">Easily index your Single Page Application thanks to PhantomJS</a></li>
<li><a target="_blank" href="https://developers.google.com/webmasters/ajax-crawling/docs/getting-started">Getting Started | AJAX Crawling (Deprecated) | Google Developers</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/09/11/phantomjs-and-seo/" target="_blank" title="利用 PhantomJS 為 SPA 做 SEO">利用 PhantomJS 為 SPA 做 SEO</a>。
<hr>
</body>
</html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-69063393637792431382016-08-27T17:19:00.000+08:002017-08-08T13:38:59.981+08:00Node.js 中使用 Promise Q<!DOCTYPE html>
<html>
<head>
<title>在 Node.js 中使用 Promise Q</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p>假設我們今天要執行一項工作task 3,但執行task 3之前要先依序完成工作task 2與task 1。
</p>
<p>如果沒有使用promise,而用callback的方式完成,程式碼大概會是這樣...</p>
<pre><code> task1(function (value1) {
task2(value1, function(value2) {
task3(value2, function(value3) {
//do something with value3...
});
});
});
</code></pre>
<p>要做(依賴)的事情愈多,深度就愈深,完全就是callback hell了。</p>
<a name='more'></a>
<h2>使用Promise</h2>
<p>承上,若使用promise,只要等到目前的工作(task 1)完成,回傳一個訊息告知下一個工作(task 2)可以開始動工,就解決這個callback hell的問題了。</p>
<pre><code>var Q = require('q');,
deferred = Q.defer();
function task1(value1) {
var response = "Complete task with " + value1;
deferred.resolve(response);
return deferred.promise;
};
function task2(value2) {
console.log(value2);
};
task1('Hello World').then(function(success) {
task2(success);
},function(error){
console.log(error);
},function(progress){
console.log(progress);
});
</code></pre>
<p>注意,回傳的promise有三種狀態:</p>
<ul>
<li>回傳成功:<code>deferred.resolve('resolve')</code></li>
<li>回傳失敗:<code>deferred.reject('reject')</code></li>
<li>回傳處理中:<code>deferred.notify('in progress')</code></li>
</ul>
<p>為了舉例方便,範例只呈現了回傳成功的狀態。</p>
<h3>結果</h3>
<pre><code>Complete task with Hello World
</code></pre>
<hr />
<p>改寫前面依序執行三個工作(task 1、task 2與task 3)的callback hell。</p>
<pre><code>function task1(value){
var deferred = Q.defer(),
response = "Complete task" + value + " with Hello World" + value;
deferred.resolve(response);
return deferred.promise;
};
function task2(value){
var deferred = Q.defer(),
response = "Complete task" + value + " with Hello World" + value;
deferred.resolve(response);
return deferred.promise;
};
function task3(value){
var deferred = Q.defer(),
response = "Complete task" + value + " with Hello World" + value;
deferred.resolve(response);
return deferred.promise;
};
var promise1 = task1('1'),
promise2,
promise3;
promise2 = promise1.then(function(res) {
console.log(res);
return task2('2');
});
promise3 = promise2.then(function(res) {
console.log(res);
return task3('3');
});
promise3.then(function(res) {
console.log(res);
});
promise2 = promise1.then(function(res) {
console.log(res);
return task2('2');
});
</code></pre>
<h3>簡化</h3>
<p>由於return的值都是promise,因此可以簡化成以下的樣子。</p>
<pre><code>task1('1')
.then(function(res) {
console.log(res);
return task2('2');
})
.then(function(res) {
console.log(res);
return task3('3');
})
.done(function(res){
console.log(res);
});
</code></pre>
<h3>結果</h3>
<pre><code>Complete task1 with Hello World1
Complete task2 with Hello World2
Complete task3 with Hello World3
</code></pre>
<h2><code>Q.all[]</code></h2>
<p>但若要等待多個工作呢?例如:等待task 1與task 2(任意順序)完成後,再繼續完成task 3。</p>
<pre><code>var Q = require('q');
function task1(value1){
var response = "Complete task with " + value1,
deferred = Q.defer();
deferred.resolve(response);
return deferred.promise;
};
function task2(value2){
var response = "Complete task with " + value2,
deferred = Q.defer();
deferred.resolve(response);
return deferred.promise;
};
function task3(result1, result2) {
console.log(result1);
console.log(result2);
};
var value1 = "Hello",
value2 = "World",
promise = Q.all([task1(value1), task2(value2)]);
promise.then(function(results){
task3(results[0], results[1]);
});
</code></pre>
<p>將taks 1和task 2放到 <code>Q.all[]</code>中,當task 1或task 2完成時,會回傳promise。當task 1與task 2都回傳promise時,就會經由 <code>promise.then()</code> 執行接下來的工作,也就是task 3。</p>
<h3>結果</h3>
<pre><code>Complete task with Hello
Complete task with World
</code></pre>
<hr />
<h2>References</h2>
<ul>
<li><a target="_blank" href="https://github.com/kriskowal/q">kriskowal/q</a></li>
<li><a target="_blank" href="http://eddychang.me/blog/javascript/55-promise-q.html">Promise - Q函式庫</a></li>
<li><a target="_blank" href="http://huli.logdown.com/posts/292655-javascript-promise-generator-async-es6">Javascript - Promise, generator, async與ES6</a></li>
<li><a target="_blank" href="http://nya.io/Node-js/promise-in-nodejs-get-rid-of-callback-hell">在Node.js中使用promise擺脫回調金字塔</a></li>
<li><a target="_blank" href="http://www.ituring.com.cn/article/54547">在Node.js 中用Q 實現Promise – Callbacks之外的另一種選擇</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/08/27/node-promise-q/" target="_blank" title="Node.js 中使用 Promise Q">Node.js 中使用 Promise Q</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-34579160222131843422016-08-21T17:21:00.000+08:002017-08-08T13:36:12.180+08:00加速行動版網頁(AMP, Accelerated Mobile Pages)<!DOCTYPE html>
<html>
<head>
<title>加速行動版網頁(AMP, Accelerated Mobile Pages)</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p><img src="https://lh3.googleusercontent.com/JKU39H2cUWXsWAb_0Hy3WAJvtW-RlPLvKQtSV5lrDW-SbfdMcAifZ_uhtjh3UzXwsVUxk5ztCwW7Ik00W0ap27kTCjcfv62MoEOHcYAZ3Zap503Q138Nk_Hv1q2XrVsKO-yaIQXdZ5qi2W2FLwHg_M_9b9k2LFWMLiQ_ruuoT0TxnNk7-kXLnFSh0WnnT4AUwKATYeZUNsk5jgjDi6j5oAiMn-MnXpacMJxJ-PMkq8-f4svpL1n4eF4VQIyMejPgk-kVF-LpN8QQYYzXtDwuDQLqRbTcqPMJpsLtDWBxawvXE6h8iQDkT2B7glwT_lwpj6DC5jI_lqVuXIQ2CydHnfBHP9bfqvupi4sKcIR9Uowtrhfax_-pIoeDCDr1EeE_ZQ3_nBB423LxscXsCTUlVeWgEYQtlyZ-ZdVEdwY4Au3hT7CQHNqsFtuFjX5A_YfqhBvg39Wc1F-C3vqciLnfl3GP8dHyE_RIuVsuKQbhi7BB4ZnzpgglwAzZsWRQmT6qUo6Xrwg5kbYUuEpP7yqvInC_wR2PU4Q-19O50TiKEESOdAp1Ne2j4nR-MU7D6L81mHBmLpUYkJuxYYRz6uhf2R9uM59hFiL5cCPzfBDCwP5uVfIU=w800-h494-no" alt="加速行動版網頁(AMP, Accelerated Mobile Pages)" /></p>
<p>加速行動版網頁(AMP, Accelerated Mobile Pages)是Google推出的一套框架,用來使行動裝置網頁能快速載入。 在Search Console上會看到提示建議Webmaster使用與提交這樣的頁面。
</p>
<a name='more'></a>
<p><img src="https://lh3.googleusercontent.com/B_wdzNlJHpQM4-f7zz1g66pT2Leryc8fOHVb_O1xHzTXx6tN-N8muOYLh91-gaM_6IuQG3iU2thJqpwl8Rd-ISmlqcNz1YFMnvol30pxLZ4R4rqUSfPph6Zfbj6Hv5zzKT2wxqRnZiZvcNlGXAlfdoKMQehh1sSFp-vgEjw2C5Xk0fjoK0Lqc97_FnkNJzk3mYjaIblm5yXkKgdr9WPkdYdpLuILqo1Om6n9KSPDbZakTUAQeivX64LJjPRtg3PWCyJWfHj_i92gLfQEvfl1rxZHpxIFJQthpytsYshGi31EE5v7ZHr_yHXnPpyuS5PQF70EyVMcVJFfN88bimdTXZFaQlqXnGasy7osxDNq_-WmowJxOF1jW0UxfXViKJoT6h7oJFT-upHssh4uV517kLDZAw2Uc8-GHVgiI5IysWlzj60Vt6WKujyUFfIFtpkzK2efvJ-BRTfWvUPh8aW5qbXu__a7HrU_4SfiFPWkyI9-yKAT53kNqAgFyKsN8r2DAwDwFX10k-Ng3vieCynyBRlUYHgkpA-O1-K31vFu2FXJKML-EqzJMRGqgHHPbfM3kIA_8Tw0i1YUFbNiEdxFDvqJgYLhXFbf9qq7rxVTZylb4mtW=w866-h259-no" alt="加速行動版網頁(AMP, Accelerated Mobile Pages)" /></p>
<p>官網的教學會一步一步帶著完成一個AMP頁面,接下來還可以從撰寫好的範例中看看怎麼實際使用到現實專案上。 </p>
<p>AMP共分三部份:
</p>
<ul>
<li>AMP HTML:HTML必須按規格撰寫,例如:圖/影音檔、廣告、社群分享或UI元件等必須使用AMP指定的標籤,像是:<code><amp-img></code>、<code><amp-carousel></code> 等。</li>
<li>AMP JS:只能使用AMP的元件,不允許使用自己撰寫或第三方的JS,如果要使用必須放在sandboxed iframe裡面。 但AMP開發的元件選擇其實很少。</li>
<li>Google AMP Cache:格式正確的AMP頁面會放到Google AMP Cache,顯示在Google Search的Top Stories版位。</li>
</ul>
<h2>優點/成效</h2>
<p>來說說使用AMP的優點,也就是使用後的成效。
</p>
<ul>
<li>頁面載入速度快</li>
<li>跨瀏覽器支援:Chrome, Firefox, Edge, Safari and Opera。 注意,如果使用Windows Phone,IE只能用 Edge噢。</li>
<li>在Google搜尋結果頂端的Top Stories以卡片呈現內容,吸引使用者點擊瀏覽該頁面。</li>
</ul>
<h2>限制</h2>
<p>使用AMP的限制非常多,而Google也解釋,這些限制是因為希望提供較佳的效能。
</p>
<ul>
<li>基本上AMP只能是靜態頁面。如果需要使用JS,只能使用AMP的元件(但元件選擇很少)。不允許使用自己撰寫或第三方的JS。若要使用第三方的JS,則必須放在sandboxed iframe裡面,避免阻擋頁面載入。其中iframe必須距離頂端600px或75%。</li>
<li>外部資源的引用(例如:圖檔、廣告、iframe等)必須在HTML中宣告大小,讓AMP確定每個元件的大小和位置,使得頁面載入時避免等待由於某些資源的載入、重新安排與計算layout的問題。</li>
<li>字型若要使用外部檔案,只能使用特定供應商的字型。</li>
<li>只允許inline styles,並且只能放在<code>header</code>的<code><style amp-custom></style></code> 之內,,大小必須小於50KB。不允許使用inline style attributes。</li>
<li>只能使用GPU-accelerated animations。</li>
<li>會對下載的資源的優先順序進行管控。例如:對需要優先下載的資源做prefetch、圖檔和廣告只有需要被看見時才載入。</li>
</ul>
<h2>電商網站的範例:eBay</h2>
<p>目前使用AMP的頁面大多是新聞網站的頁面,而很特別的是,eBay居然宣布開始使用AMP了。但我很好奇,Top Stories的類型多為新聞、食譜,目前沒有看到商品,eBay做這個是為未來Top Stories新增商品類型做準備嗎?</p>
<p>eBay製作的AMP頁面是站內搜尋頁,而它的站內搜尋頁有兩個版本:未使用AMP、使用AMP。點開下面的連結來看看使用與未使用AMP的差異:</p>
<ul>
<li><a target="_blank" href="http://m.ebay.com/sch/Mens-Clothing-/1059/i.html?epp=24&isRefine=true&itemId=0">未使用AMP</a></li>
<li><a target="_blank" href="http://m.ebay.com/sch/amp/Mens-Clothing/1059/bn_696958/i.html">使用AMP</a></li>
</ul>
<h2>實作</h2>
<p>我也幫「吃什麼,どっち」做了一個AMP的頁面,可以用手機點開來看。</p>
<p><a target="_blank" title="吃什麼,どっち - 日式料理" href="http://dotch.herokuapp.com/search/amp?query=%E6%97%A5%E5%BC%8F%E6%96%99%E7%90%86">範例</a></p>
<h2>測試與驗證</h2>
<p>網址後加上 <code>#development=1</code>,即可在瀏覽器的JavaScript Console檢視錯誤訊息。</p>
<h2>備註</h2>
<ul>
<li>做好AMP的頁面後,必須要讓搜尋引擎爬到。因此可以連接AMP與非AMP版本的頁面、使用RSS提交等方式。</li>
<li>可結合結構化資料使用,畢竟使用AMP之後要使用Microdata是很辛苦的阿。</li>
</ul>
<h2>結論</h2>
<p>目前台灣在Google SERPs上是沒有看到Top Stories(如果有看到請跟我說),所以扣除AMP可以在Top Stories吸引使用者點閱外,就只剩下使用行動裝置瀏覽時能快速載入的優點了。因此,目前對我來說,AMP就是個類似Bootstrap的前端框架,依照規格撰寫與應用,組成行動裝置版本的網頁,然後優點是效能很好。</p>
<hr />
<h2>References</h2>
<ul>
<li><a target="_blank" href="https://www.ampproject.org/how-it-works/">Accelerated Mobile Pages – A new approach to web performance</a></li>
<li><a target="_blank" href="https://github.com/ampproject">AMP Project</a></li>
<li><a target="_blank" href="https://www.keycdn.com/blog/accelerated-mobile-pages/">Diving Into Google Accelerated Mobile Pages (AMP)</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/08/21/amp/" target="_blank" title="加速行動版網頁(AMP, Accelerated Mobile Pages)">加速行動版網頁(AMP, Accelerated Mobile Pages)</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-25137602666741031282016-08-20T14:25:00.001+08:002017-08-08T13:32:37.803+08:00Gulp筆記:安裝、撰寫Task、監看、例外錯誤處理和套件使用<!DOCTYPE html>
<html>
<head>
<title>Gulp筆記:安裝、撰寫Task、監看、例外錯誤處理和套件使用</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<p>Gulp是一套任務管理工具,讓前端的工作能自動化處理,例如:js/css最小化、瀏覽器檢視、通知、檔名變更、加入版號等等。這裡做一些簡單筆記。</p>
<a name='more'></a>
<h2>安裝</h2>
<ul>
<li>全域安裝gulp套件:<code>npm install -g gulp</code></li>
<li>在個別專案資料夾下安裝gulp套件,並只用於開發環境:<code>npm install --save-dev gulp</code></li>
</ul>
<h2>撰寫 gulpfile.js</h2>
<p>在專案資料夾底下新增 gulpfile.js。執行指令 <code>gulp</code> 後,會執行預設的Task 「default」。</p>
<pre><code>var gulp = require('gulp');
gulp.task('default', function () {
console.log('執行預設工作。');
});
</code></pre>
<h2>撰寫Task</h2>
<p>定義不同的工作。</p>
<pre><code>var gulp = require('gulp');
gulp.task('default', function () {
console.log('執行預設工作。');
});
gulp.task('another', function () {
console.log('執行另一個工作。');
});
</code></pre>
<p>執行工作「another」。</p>
<pre><code>gulp another
</code></pre>
<h2>監看(Watch)</h2>
<p>監看。意即只要程式碼有變動,就立刻執行某個指定的工作。
</p>
<p>如下範例,執行指令「gulp」,就會執行預設工作「default」。而「default」會依序去執行「scripts」和「watch」。
</p>
<p>然後,「watch」會 <strong>監看</strong> 特定資料夾的特定檔案(在這裡是指定 <code>public/javascripts</code> 下的所有js檔案),<strong>檔案一有變動變執行「scripts」工作</strong>。
</p>
<p>最後,「scripts」工作會將js檔案做最小化,並將最小化後的檔案放到 <code>public/javascripts/minify</code> 底下。</p>
<pre><code>var gulp = require('gulp'),
gulpUglify = require('gulp-uglify');
gulp.task('default', ['scripts', 'watch']);
gulp.task('watch', function() {
gulp.watch('public/javascripts/*js', ['scripts']);
});
gulp.task('scripts', function() {
gulp.src('public/javascripts/*js')
.pipe(gulpUglify())
.pipe(gulp.dest('public/javascripts/minify'));
});
</code></pre>
<h2>例外/錯誤處理</h2>
<p>可使用gulp-plumber或gulp-util來處理例外或報錯,讓監看不中斷。</p>
<h2>套件(Plugins)</h2>
<p>列舉一些常用套件。</p>
<ul>
<li><a target="_blank" href="https://github.com/terinjokes/gulp-uglify">gulp-uglify</a>:最小化js檔案。</li>
<li><a target="_blank" href="https://github.com/plus3network/gulp-less">gulp-less</a>:將less轉為css。</li>
<li><a target="_blank" href="https://github.com/sindresorhus/gulp-changed">gulp-changed</a>:只處理有變動的檔案。</li>
<li><a target="_blank" href="https://github.com/vohof/gulp-livereload">gulp-livereload</a>:自動重新載入頁面。</li>
<li><a target="_blank" href="https://github.com/mikaelbr/gulp-notify">gulp-notify</a>:通知。</li>
<li><a target="_blank" href="https://github.com/BrowserSync/browser-sync">browser-sync</a>:瀏覽器同步檢視。</li>
<li><a target="_blank" href="https://github.com/hparra/gulp-rename">gulp-rename</a>:將產生的檔案重新命名。</li>
<li><a target="_blank" href="https://github.com/sindresorhus/gulp-rev">gulp-rev</a>:加入版號。</li>
<li><a target="_blank" href="https://github.com/less/less-plugin-clean-css">less-plugin-clean-css</a>:lee轉css後並最小化css檔案。</li>
<li><a target="_blank" href="https://github.com/scniro/gulp-clean-css">gulp-clean-css</a>:最小化css檔案。</li>
<li><a target="_blank" href="https://www.npmjs.com/package/gulp-image-optimization">gulp-image-optimization</a>:壓縮圖檔。</li>
<li><a target="_blank" href="https://github.com/Craga89/gulp-watch-less">gulp-watch-less</a>:監看 <code>.less</code> 與 其import的檔案。</li>
<li><a target="_blank" href="http://codyburleson.com/2015/09/11/better-error-messages-from-gulp-using-gulp-util/">gulp-util</a>:報錯處理。</li>
<li><a target="_blank" href="https://github.com/rvagg/through2">through2</a>:node stream的簡單封裝,讓transform stream操作更簡單。</li>
<li><a target="_blank" href="https://www.npmjs.com/package/gulp-plumber">gulp-plumber</a>:報錯處理。</li>
</ul>
<hr />
<h2>References</h2>
<ul>
<li><a target="_blank" href="https://github.com/gulpjs/gulp">gulpjs/gulp: The streaming build system</a></li>
<li><a target="_blank" href="https://kejyuntw.gitbooks.io/gulp-learning-notes/content/">gulp 學習筆記</a></li>
<li><a target="_blank" href="http://blog.darkthread.net/post-2014-09-25-gulp-grunt-bower-npm.aspx">Gulp, Grunt, Bower以及npm</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/08/20/gulp/" target="_blank" title="Gulp 筆記:安裝、撰寫 Task、監看、例外錯誤處理和套件使用">Gulp 筆記:安裝、撰寫 Task、監看、例外錯誤處理和套件使用</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-5908459253475290872016-08-15T15:01:00.001+08:002017-08-11T18:03:51.686+08:00T3 - 構建大型網站的JavaScript框架<html>
<head>
<title>T3 - 構建大型網站的JavaScript框架</title>
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
</head>
<body>
<img alt="T3.js" src="https://cythilya.github.io/assets/2016-08-15-t3/t3_800.png" />
<br />
T3是一個JavaScript UI Framework,主要的功能是讓程式碼更結構化。如果網站內的程式碼很雜亂的話,很適合用來整理散落在各處的程式碼(尤其是針對年紀大需要翻新的大型網站)。而整理方式就是將程式碼分成幾個部份:Application、Module、Serveice、Behavior等來處理。頁面UI整理成Module,Module彼此共用的方法或事件整理成Behavior,而非UI邏輯且多個Module會共用的部份(例如:與Server端溝通取資料)則抽出來成為Service。<br />
<a name='more'></a>
<h2>
Module</h2>
將頁面功能切成模組(Module)來運作,如果頁面無法切成多個模組就將整個頁面視為一個模組。
<br />
模組的事件有以下幾種:Message Handling、Behavior、Event Handlers(例如:onclick)。如下:頁面上的模組「test-module」,可能需要針對其他模組的反應來做出回應,因此有Message Handling;不同模組但有重覆的事情要做,則可用Behavior;模組內的事件處理可使用onclick等;非UI邏輯的部份丟給Service即可。<br />
<h3>
HTML</h3>
<pre><code><button data-module="test-module">Test</button>
</code></pre>
<h3>
JS</h3>
<pre><code>Box.Application.addModule('test-module', function(context) {
return {
messages: ['statechanged', 'statecompleted'],
onmessage: function(name, data) {
switch(name) {
case 'statechanged':
console.log('statechanged');
break;
case 'searchcomplete':
console.log('statecompleted: ' + data.numResults + ' tasks completed.');
break;
}
},
behaviors: ['getInfo'],
init: function(){
console.log('module init');
},
onclick: function(event, element, elementType){
console.log('click');
context.getService('connectSomething').connect();
}
}
});
</code></pre>
<h2>
Service</h2>
Module與Behavior負責處理UI,而非UI的部份則交給Service。Service可當成一個共用的介面,讓不同的Module和Behavior來共同呼叫。例如:使用ajax取資料。如下範例,「getInfo」這個Service負責與Server端溝通取資料,我們呼叫這個Service的method「getSearchResultByKeyword」,並將結果回傳給Behavior「getSearchResult」。<br />
<pre><code>Box.Application.addService('getInfo', function(application) {
return {
getSearchResultByKeyword: function(query) {
$.ajax({
url: '/getSearchResult',
type: 'post',
data: {
query: query
},
dataType: 'json',
success: function (response) {
return response.data;
},
error: function (xhr) {
alert('噢噢!發生錯誤了!請重新再試一次~');
}
});
}
};
});
Box.Application.addBehavior('getSearchResult', function(context) {
var query = "日式料理";
return {
init: function() {
var data = context.getService('getInfo').getSearchResultByKeyword(query);
console.log(data);
}
};
});
Box.Application.init();
</code></pre>
<h2>
Behavior</h2>
不同模組但有重覆的事情要做,則可在模組中使用Behavior。<br />
<pre><code>Box.Application.addModule('module-test-1', function(context) {
return {
behaviors: ['element-button'],
init: function(){
console.log('init');
}
}
});
Box.Application.addModule('module-test-2', function(context) {
return {
behaviors: ['element-button'],
init: function(){
console.log('init');
}
}
});
Box.Application.addBehavior('element-button', function(context) {
return {
init: function() {
console.log('add behavior');
context.getService('connectSomething').connect();
}
};
});
Box.Application.addService('connectSomething', function(application) {
return {
connect: function() {
console.log('connect something...');
}
};
});
Box.Application.init();
</code></pre>
<h2>
DOMEventDelegate</h2>
委派,範例如下。<br />
<h3>
HTML</h3>
<pre><code><button data-module="module-domeventdelegate">Test DOMEventDelegate</button>
</code></pre>
<h3>
JS</h3>
<pre><code>Box.Application.addModule('module-domeventdelegate', function(context) {
var element = context.getElement();
var delegate = new Box.DOMEventDelegate(element, {
onclick: function(event) {
console.log('DOMEventDelegate: ' + event.type);
}
});
return {
init: function(){
var element = context.getElement();
delegate.attachEvents(); //DOMEventDelegate
},
onclick: function(event, element, elementType){
console.log('click!');
}
}
});
Box.Application.init();
</code></pre>
<h2>
Context</h2>
<h3>
broadcast</h3>
模組訂閱事件,事件發生時會通知模組。可用在網站的訊息中心等。<br />
<h4>
HTML</h4>
<pre><code><div data-module="module-broadcast-1">
1</div>
<div data-module="module-broadcast-2">
2</div>
</code></pre>
<h4>
JS</h4>
<pre><code>Box.Application.addModule('module-broadcast-1', function(context) {
return {
messages: ['broadcast-a', 'broadcast-b'],
onmessage: function(name, data) {
switch(name) {
case 'broadcast-a':
console.log('broadcast-a');
Box.Application.broadcast('broadcast-b');
break;
case 'broadcast-b':
console.log('broadcast-b');
break;
}
}
}
});
Box.Application.addModule('module-broadcast-2', function(context) {
return {
messages: ['broadcast-a', 'broadcast-c'],
onmessage: function(name, data) {
switch(name) {
case 'broadcast-a':
console.log('broadcast-a');
break;
case 'broadcast-c':
console.log('broadcast-c');
break;
}
},
}
});
Box.Application.init();
Box.Application.broadcast('broadcast-a');
Box.Application.broadcast('broadcast-c');
</code></pre>
<h3>
getGlobal</h3>
取得window物件。<br />
<h4>
HTML</h4>
<pre><code><div data-module="module-test">
test</div>
</code></pre>
<h4>
JS</h4>
<pre><code>Box.Application.addModule('module-test', function(context) {
return {
init: function(){
var navigator = context.getGlobal('navigator');
console.log(window.navigator.userAgent === navigator.userAgent);
}
}
});
Box.Application.init();
</code></pre>
<h3>
getGlobalConfig</h3>
取得 <code>Box.Application.init</code> 中的設定物件。<br />
<h4>
HTML</h4>
<pre><code><div data-module="module-test">
test</div>
</code></pre>
<h4>
JS</h4>
<pre><code>Box.Application.addModule('module-test', function(context) {
return {
init: function(){
console.log(context.getGlobalConfig('userName'));
}
}
});
Box.Application.init({
userName: 'Bob'
});
</code></pre>
<hr />
以上只列出目前比較常用的部份,<a href="http://t3js.org/" target="_blank">T3</a>的文件寫得很詳細,有興趣的話可以看一下。<br />
我也將之前參加駭客松的作品<a href="https://dotch.herokuapp.com/" target="_blank">吃什麼,どっち</a>部份改版使用T3實作。<br />
以上的<a href="https://github.com/cythilya/t3-example" target="_blank">程式碼</a>都會放在Github上(含簡單的TodoList),如果有什麼建議或想法都歡迎聊聊。<br />
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/08/15/t3/" target="_blank" title="T3 - 構建大型網站的 JavaScript 框架">T3 - 構建大型網站的 JavaScript 框架</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-77836281914622968562016-07-06T10:06:00.000+08:002017-08-08T13:22:09.438+08:00Hubot x Slack,製作自動回話與工作的機器人<!DOCTYPE html>
<html>
<head>
<title>Hubot x Slack,製作自動回話與工作的機器人</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: b13fe65ca28d2e568c6ed5d7f06581183df8f2ff */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
</head>
<body>
Hubot x Slack 串接紀錄。
<h2>安裝 Hubot</h2>
<p>按照<a target="_blank" href="https://hubot.github.com/docs">官方文件</a>的指示一步步安裝。安裝完後,便可用command line與robot互動。使用 <code>bin/hubot</code> 啟動robot。可以使用 <code>hubot help</code> 看看預設有什麼功能可以使用。</p>
<h2>安裝 Hubot 的 Slack Adapter</h2>
<p>按照<a target="_blank" href="https://github.com/slackhq/hubot-slack">hubot-slack</a>安裝 Hubot 的 Slack Adapter。安裝完後,要到Slack的Hubot App設定。這邊因為找了滿久的,所以截圖詳細記錄一下。</p>
<h3>Slack的Hubot App設定</h3>
<p>先從「Apps & integrations」到「App Directory」,然後搜尋「hubot」。</p>
<h4>Apps & integrations</h4>
<p><img src="https://goo.gl/eEUxCa" alt="Apps & integrations" /></p>
<h4>App Directory,搜尋「hubot」</h4>
<p><img src="https://lh3.googleusercontent.com/EAKMLjSI8GmqdVN9NLw58uA34p-pWreYjweuHZPyzHyh0NVv0-qAcCmU-1sWqLtj5wb9QLfuULW7pDC8GIcoqGk-IDZ8E_BnND-c3l7SI6gZvXuM-QY_wga9zvnj554itPKMQFxGiq-7_DofC4V-dTKEWfRHEqE_WXlzMhtoI37D72f1YUODBGCJmGxnertpXOzhzrFYQklgCJ7TlsZX62jP-rJ2M2fkO_r4--fVKOerKphWn5SlI7nB2B4oHKHTjezJfPU3cIQGcm10z9llSW5go5dguJvenu9umD4ArPkaZvDmChBJ8r961YwJfwH1AuW4Yg_CxWWNVeRtGU1s01SHaxMGlE3-_9fh6c4tQ638AVcgFcZuAbt0t99iXZpaResWP8ODU60MF3b6agSokWfVmFE7BHV8LFxeJWiTO4g9OrZeF-ZrXdGE7Ma76rzMsaOguSZ4cFDZaw_xv8BDrjozGW4CzzAd2oGdl7FkPP3TGc62VXabNj2OUX0jtm5_gGE8nrOr49Osq81MbKrdipFW7uLElKmRZwN8_m61Lt2TgDRU4VIiIVguv-E1S7vjcPHM7x-aDXjJLXj0FFsxcb2aTKl5DhE=w800-h554-no" alt="App Directory" /></p>
<h4>新增app</h4>
<p><img src="https://goo.gl/hrYtEM" alt="hubot app" /></p>
<p>這邊因為我已經新增好了,所以只能截圖設定完畢的樣子。</p>
<h4>取得token</h4>
<p>啟動robot時必須附上token。</p>
<p><img src="https://goo.gl/Bo3W5Q" alt="取得token" /></p>
<a name='more'></a>
<h2>準備腳本</h2>
<p>在scripts資料夾裡面新增script.js,然後寫些簡單的腳本。</p>
<pre><code>module.exports = function(bot){
bot.respond(/你好嗎?/, function(res){
res.send('很開心 ^^');
});
}
</code></pre>
<h2>連線</h2>
<p>使用指令「HUBOT<em>SLACK</em>TOKEN=xoxb-9999999999-abcdefghijklmnop ./bin/hubot --adapter slack」連線。<code>xoxb-9999999999-abcdefghijklmnop</code> 這裡代入剛剛取得的token即可。</p>
<p>看到Slack聊天室裡機器人的綠燈亮了,就表示成功了噢!</p>
<h2>測試</h2>
<p><img src="https://goo.gl/kNXkrY" alt="測試" /></p>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/07/26/robot-hubot-slack/" target="_blank" title="Hubot x Slack,製作自動回話與工作的機器人">Hubot x Slack,製作自動回話與工作的機器人</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-68604303953678259222016-06-25T12:50:00.001+08:002017-08-08T13:18:39.415+08:00DNS Prefetching - 預先做DNS解析,幫助網頁載入速度更快<!DOCTYPE html>
<html>
<head>
<title>DNS Prefetching - 預先做DNS解析,幫助網頁載入速度更快</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p><img src="https://lh3.googleusercontent.com/0wyBliSh7sPNoqjwAONEmoQ5PJHXs7NLFL87NtpR5_Z2GPFx6OSVLxBKvAd24bZZ93zXEwmV7TYLAi7Q8p4k550PBqnI_goBOm8LyN7uUStSVtZGV0hGeW9OGCQt3V21yFcjnnZ9X67brIMHtlR3EaI3uov6wq1lHJbJfei13gl6vXILWfiQYC1blkGUJMOt7S4Lz3GYiK6D_jJf2_H4JdgEIoHnP6jYNCuJUR5Hp-YLeMMtVUVJDNnw5YXb2n_3mLyQvtQwfMPt8mgVg96CgigOnkzge7fXUPehv_hd_ConVIL92I9IRshXHEDFitguoAPu-DtksGJtpzIij1bllhS7CwCymJaUDjvWJZWSTqzxiWH-Vwm5VyPpcu3KziYTuJPDchuORktqe-cik7-00yfkSYwBymqJMi-UVJfbX-PCgsk1uAkLdJR4K-d0qDqJ338Tpaij6R0DodcnsshzOyE_Tsw2KiB-IZxtuHP5-F7guELZb9UcNVqXCvbQG-xLCADjwT1jdKtIQisKoW8IjK5816gXqWP7nTJKZCQhTic9YO7xMw0dv1OwC3T88EW6RQJN4Z_gsfPe877PqtluSJ2C53Jv67Y=w800-h494-no" alt="DNS Prefetching - 預先做DNS解析,幫助網頁載入速度更快" /></p>
<p>前陣子看見<a target="_blank" href="https://www.facebook.com/articlesfordevelopers/?fref=nf">Addy Osmani</a>大大在Facebook上發表了一張圖片(<a target="_blank" href="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Farticlesfordevelopers%2Fposts%2F10153845235729601%3A0&width=500">原文</a>)。</p>
<a name='more'></a>
<p><img width="800" src="https://scontent-tpe1-1.xx.fbcdn.net/t31.0-8/13422234_10153845235729601_2338574905430590016_o.png" alt="Resource Hints" />
</p>
<p>在研究淘寶SEO策略時有略略看到頁面上出現這樣的設定,但對於網站效能提升這一塊比較陌生的我,並沒有做太多的研究(只知道是做什麼用的),剛好藉這次機會來看看相關資料並做整理。</p>
<h2>什麼是DNS Prefetching?</h2>
<p>預先做DNS解析(domain name resolution),將人類可理解的domain name,轉為IP address。瀏覽器載入頁面和資源時需做DNS解析,但若等到瀏覽該頁或要下載資源時才做DNS解析就太遲了(使用者需要等待一段時間),因此可預先執行。(關於DNS Lookup流程可參考 <a target="_blank" href="http://120.105.184.250/peiyuli/network-1/DNS%E4%BC%BA%E6%9C%8D%E5%99%A8.htm">DNS 名稱解析程序</a>。)</p>
<p>預先做DNS解析的好處是使用者瀏覽之後的頁面時,可減少DNS Lookup的時間,感覺速度變快了。</p>
<h2>如何使用DNS Prefetching?</h2>
<p>在HTML的 <code><head></code> 加入 <code><link rel="dns-prefetch" href="https://my-site.com"></code> 。
使用這個指令後,頁面上 <code><a></code> 的連結也都會開啟DNS Prefetching。</p>
<p>附註,DNS Prefetching在https下是無法使用的。若要在https下開啟DNS Prefetching,必須在 <code><head></code> 加上 <code><meta http-equiv="x-dns-prefetch-control" content="on"></code> 才能啟動DNS Prefetching。但只能啟動連結,而無法啟動手動設定的資源。</p>
<p>順道一提,<code><link rel="dns-prefetch" href="//host_name_to_prefetch.com"></code> 和 <code><link rel="dns-prefetch" href="http://host_name_to_prefetch.com"></code> 意義是相同的,「//」表示網址起始是一個host name。</p>
<h3>使用DNS Prefetching的時機</h3>
<ul>
<li>
<p>該頁面有許多靜態資源但放在各個不同的domain底下,例如圖檔、CSS檔案、JS檔案等:例如,在這個頁面或這個網站將圖片放在 <code>http://a.img.com.tw</code> 和 <code>http://b.img.com.tw</code> 這些domain底下。我希望瀏覽該頁面時能先對這兩個domain name做DNS Prefetching,於是可在 <code><head></code> 加上</p>
<pre><code><link rel="dns-prefetch" href="http://a.img.com.tw">
<link rel="dns-prefetch" href="http://b.img.com.tw">
</code></pre>
</li>
<li>
<p>該頁會轉跳到不同domain下的頁面,因此可在指令先指定對轉跳的domain做DNS Prefetching</p>
</li>
</ul>
<h3>適合的對象</h3>
<ul>
<li>電商網站的商品頁大量載入不同domain下的商品圖,例如:淘寶</li>
<li>手機網頁,需要提高頁面載入完成的速度</li>
</ul>
<h2>成效評估</h2>
<h3>測試範例</h3>
<h4>拿我們家露天拍賣的測試機商品頁來看看...</h4>
<h5>尚未做DNS Prefetch之前</h5>
<p><img src="https://lh3.googleusercontent.com/iu4RLXP9RFdrRBeTLH9zkFY0GF21r7ndmH3oZUJzYJSdMkRFVHgOKL6RtiqaOXMjKlhvmzduXWCxidym4dHoF6Lw0GIeqKMg0aNDbzKWzRKv5eTkIedWrE4VYMNNkBpRoHXpGekUImTnlLAl9F_jqJnuTcTljLTBQlMcAMU75ui73or5fuk1wdFaDrx6OcNUzMsJnfqi5GShQqwKjBXK4zKeo8OpeBl9HY7jV31sTO8mTO9JZnPYfmA0aJvNgXMJAmZL5SmrGRyoEk_uK-D-13_nskJ0ZlSrWDQjOG2qCaQdoaoWoKBulHF8pKXau9z9i_MDlZUJnlrnQb8hMxP7eyW3i1tNMAsOgUKxrnq4JkJCzpfZQ8H0I3qhSmuzEIchUjqxxF5McQK4Pe2_T2tOZq6xapIQJ398SBLEIar-DgM0vFTj00KuubdbCzE27miwwEgVtsQUC3DcelSDKN4w00z7op3fAdklBDkPWHiTIM89MRRRWgSBwtcXGUKfnvvusQE3a58OzulpEb1q5yZgwYqpXTIB_a5u6FEwJPmqEawDoPzatr3GvPO67CfudeX5pokBTvRREuU416nAYYHlCqKFL--1yi4=w700-h128-rw-no" alt="DNS Prefetch" /></p>
<h5>做了DNS Prefetch之後</h5>
<p>我加了這幾條指令到 <code><head></code> 裡面...</p>
<pre><code><link rel="dns-prefetch" href="http://a.rimg.com.tw">
<link rel="dns-prefetch" href="http://b.rimg.com.tw">
<link rel="dns-prefetch" href="http://c.rimg.com.tw">
<link rel="dns-prefetch" href="http://d.rimg.com.tw">
<link rel="dns-prefetch" href="http://e.rimg.com.tw">
</code></pre>
<p><img src="https://lh3.googleusercontent.com/TpFY5RH8teilB5yZk3ru2DrbqcL5pDxk50J4Sq3pwLtk3v6t2hC4IL8fIPqpVn5PWQHZEwCG6VBGgPYvyaeJN5z4vFLlhdd6qg2ZHSdj4HxYKadCOur2SZPc8P68VhsGaaf-0oOYKgsz8E_n0nvBhmMGL456ylkTxWNRTU9XrGTBUqtM5IObyA_wj3p-dAoF4jtowRq484JrKp_l-uL0Atl1bGV1_vcvkrMKEFZmwzIptQMKibcbgmFBdts8wm4806HKEbJkSdndOptfzs0wEkf0olPkw8FmDSMLer1n_R6cJWFXDJAjYXO_6gw0xxfJP8wnDm46yvcSdPXSQO4o5lcyBPSmm0HaHQyFcWJJV5_s9pL1fnHx2ygna5xiUYhXsHMgWc8xUtmlaAj-py6E74kfy0bKcAbsJKKNKX90fnbtbqUWvjwmRysQWYsYYP3xb3I-2Z-YB0j-Cf1Kmg07lUIxIkbdBCkjHOexV6WjG5TUIpD7rAlYcoMZMPojOk1GekCYhR3Mk5Y8Teo1AhpWxX3mYBkEO3aSlRSrgTbxWjt3jGPeXYmUSTy3q90x7OlrCIziviUD-dUTrGnkx4XWgF07y07kekg=w691-h129-no" alt="DNS Prefetch" /></p>
<p>多7次DNS Prefetch,其中不需做DNS Prefetch的有7次,完全命中。由於在上一頁的時候保留了DNS Lookup的紀錄,因此省了7次的DNS Lookup。</p>
<h4>拿淘寶的商品頁來看看</h4>
<p><img src="https://lh3.googleusercontent.com/A0M_kWDnqh90mGjAiWpSVPhOxf3ISJC8Y27JzBO9xcmFVUmVMECXoyCcSL98N8_zgaRGWP-pHFphKZeZecgmlmr353-_Aj2S7EVlWd9jJ8F8fFa4eck5gMNWlJexOuu9KvUOLJQ-05BMryiXFbvz5TMJYGK8BcT7yYSjMSwz_tzrYhTNE3zs4fC7iPQ6Rud2W4fvk-zqg92TSgUYtQ3hRpYVbgDuEZlJv77cF9oLCxkSRrc1LO1ga8AQyuolj34ECepMWhT8s5H4lJ8A1ADb9ULLI72y6BdF4jQ_Sxn9p39tWAvUeG3AfLP5fGpiKitzK5kGb1_Fyw-wYWWrChqAnS90HkvntVaXepomR11MGjdBnuXHzUgjPEmVKRoHBU97moYjAw6W4QsS52LqiEsuh5PYNXYCUHvDCvEu9XLHzC2ROZDC608iHcdQfbtX6ztemIyb4YV45zBCnTba3LP37o21fCW__Q-s6qNO9R0g3ISOb-eGLHtHSaa6nB2vNF9aeGYWCJAUvHuOyN6VscZBxHHHYDbkqgcUmf_VXYa1-H6LFQfB3jL0-q_wuvgGkL120e9G0TTbriEJGxjQkfnnjvplbRT0dB0=w707-h360-no" alt="DNS Prefetch" /></p>
<p><a target="_blank" href="https://item.taobao.com/item.htm?spm=a230r.1.14.8.ykd7pT&id=530161293915&ns=1&abbucket=10#detail">先點這個範例頁面</a></p>
<p><img src="https://lh3.googleusercontent.com/Y_VuQhzhKrFKnj9Z7DnXJaK0YiTJsJBu_BfOm4XH_tDuyi17RwcVTvLLC78uOJJEOz_eGSrs543Rk1allpPlz0BPOVC1DreHiF3D_0Ne5RQX6JxasnNOTtytjD7qmhRnqjsxCb1Eg9Asjo-tCYsvo1TFH6gmYUS1RsA52CE9DQJi9vlHjtZ083oqjmK34lvx4AmvBARFcv35MOaNgEn-lnNg0PGw3pCwSE59EK-jUvWEtj5x4bGesJDUSc7BkpxIUxP4mKxsST0oX5L0vJO28_RobGLOPk2e-w6DqpKqJo50g8WpfsoObZv-kxJRHWpvysxMlaa-_m6zgSlOXxRq315IQV5h81tErJrblNmg1GS4-68bIaE8RTOx4Rtywsd-N5HVeuGrMv7yTZo6LcSw8JXed_mNO_UKgfpAXiqQFGjFANFOxPIKkDzVG8theeZ_KjJZB-55_c4kekMNqtZnusPX8tDJQcya6MyylQ5Ujg7ecnuE1y8lUYJc61GnscR8Ags99xs-KSzbih1EBiHqRmKNnCZqqsRlbF0s_LLhBpPK20sZJ_HPXsXd6gedzYkdME8idoCCPH4sCvuPee-2-NsWz9NECRk=w700-h346-no" alt="DNS Prefetch" /></p>
<p><a target="_blank" href="https://item.taobao.com/item.htm?spm=2013.1.1998246701.3.ABH7aM&scm=1007.10152.33975.1p0&id=529813424868&pvid=d507ed75-c3d8-4a84-82ec-8408a389012f">再點這個範例頁面</a></p>
<p>多9次DNS Prefetch,其中不需做DNS Prefetch的有4次,大約減少了一半。</p>
<h3>附註:可以用來觀察結果的指令</h3>
<ul>
<li><code>chrome://histograms/DNS.PrefetchQueue</code></li>
<li><code>about:histograms/DNS (可看更詳細的資料)</code></li>
<li><code>about:dns (可看更詳細的資料)</code></li>
</ul>
<h3>使用DNS Prefetching的好處?</h3>
<p>花小量的傳輸(不到100 bytes),卻平均可節省200ms。<br />
(有興趣的話可參考<a target="_blank" href="https://www.chromium.org/developers/design-documents/dns-prefetching">DNS Prefetching - The Chromium Projects</a>)
</p>
<p>對SEO來說,網站速度也是Ranking Factor之一,提高下載速度就可提高排名。
</p>
<h3>減少DNS Lookup 次數的相關議題</h3>
<p>假設在頁面上有許多資源,例如:圖片、CSS檔案、JS檔案,這些都放在同一個domain name底下,這時瀏覽器針對每個domain只會開一個連線。也就是說,假設有10個檔案放在同一個domain之下,每個檔案需要100ms才能下載完,那個預計載完資源需要 <code>10*100=1000ms</code> 。但若我們將這10個檔案分別放在2個不同的domain下,就可以開兩條連線檔案同時下載,那麼只要花 <code>5*100=500ms</code> 即可,減少了一半的時間。但也不是說domain愈多愈好,因為DNS Lookup需要時間,因此建議維持2~4個不同的domain name即可。</p>
<h2>瀏覽器比較:Chrome、Firefox、IE</h2>
<h3>Chrome</h3>
<ol>
<li>Chrome會記住最近使用的10個domain,並且在開啟瀏覽器時自動解析,因此在開啟這些常用頁面的時候,並不會有DNS Lookup的延遲狀況,大約節省了200ms或更多。打開你的Chrome,使用指令 <code>chrome://dns</code> 來看看。</li>
<li>本機作業系統對DNS的快取是有限的,大約只能暫存50~200個domain name。一旦超過了這個限制,便會移除過去使用過的domain name來存放新的domain name。而選取移除的domain name只靠瀏覽的時間決定,可能會造成經常使用的domain name查詢結果被移除,被迫常常對這個domain name進行查詢。Chrome嘗試修正這樣的機制,所以會猜測哪些domain name是使用者近期可能會用到的,然後標記為常用,使其能保存久一點。</li>
</ol>
<p>(有興趣的話可參考可參考 <a target="_blank" href="https://www.chromium.org/developers/design-documents/dns-prefetching">DNS Prefetching - The Chromium Projects</a>)。</p>
<h4>而Chrome類似其他瀏覽器的其他設定都已忽略,似乎不希望使用者更改預設值。因此直接清除。</h4>
<ol>
<li>chrome打開:<code>chrome://net-internals/#dns</code></li>
<li>點擊按鈕「clean host cache」</li>
</ol>
<h3>Firefox</h3>
<ul>
<li><code>network.dns.disablePrefetch</code>:若設定為true,則會關閉瀏覽器DNS Prefethcing的功能。</li>
<li><code>network.dns.disablePrefetchFromHTTPS</code>:若設定為false,則會開啟使用HTTPs的網站的DNS Prefethcing功能。在HTTPs的網站下,DNS Prefethcing功能預設是關閉的,必須經此手動開啟。</li>
<li><code>network.dnsCacheExpiration</code>:預設DNS暫存1分鐘</li>
<li><code>network.dnsCacheEntries</code>:預設DNS暫存20個</li>
<li><code>network.http.keep-alive.timeout</code>:TCP/IP連線idle一段時間後才會被釋放掉,預設是5分鐘。好處是避免重覆DNS Lookup太快發生。</li>
</ul>
<h3>IE</h3>
<ul>
<li><code>DnsCacheTimeout</code>:預設DNS暫存30分鐘。</li>
<li><code>KeepAliveTimeout</code>:TCP/IP連線idle一段時間後才會被釋放掉,預設是1分鐘。好處是避免重覆DNS Lookup太快發生。</li>
<li><code>ServerInfoTimeout</code>:就算沒有<code>KeepAliveTimeout</code>,假設我們查到了IP位置,並在使用一段時間內都沒有發生錯誤,那麼這段間內都不會對此domain name做DNS Lookup。預設是2分鐘。</li>
</ul>
<hr />
<h2>References</h2>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Controlling_DNS_prefetching">Controlling DNS prefetching</a></li>
<li><a target="_blank" href="http://bitsup.blogspot.tw/2008/11/dns-prefetching-for-firefox.html">DNS Prefetching for Firefox (blog post)</a></li>
<li><a target="_blank" href="http://dev.chromium.org/developers/design-documents/dns-prefetching">Google Chrome handles DNS prefetching control</a></li>
<li><a target="_blank" href="https://segmentfault.com/a/1190000003944417">预加载-有赞-DNS</a></li>
<li><a target="_blank" href="http://www.jianshu.com/p/c3a14a853c79">DNS Prefetching的两三事</a></li>
<li><a target="_blank" href="https://www.chromium.org/developers/design-documents/dns-prefetching">DNS Prefetching - The Chromium Projects</a></li>
<li><a target="_blank" href="https://gtmetrix.com/parallelize-downloads-across-hostnames.html">PageSpeed: Parallelize downloads across hostnames</a></li>
<li><a target="_blank" href="http://linux.vbird.org/linux_server/0350dns.php">完整主機名稱: Fully Qualified Domain Name (FQDN)、19.1.2 DNS 的主機名稱對應 IP 的查詢流程</a></li>
<li><a target="_blank" href="http://www.ghacks.net/2013/04/27/firefox-prefetching-what-you-need-to-know">Firefox prefetching: what you need to know</a></li>
<li><a target="_blank" href="https://www.byvoid.com/zht/blog/http-keep-alive-header">HTTP協議頭部與Keep-Alive模式詳解</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/06/25/dns-prefetching/" target="_blank" title="DNS Prefetching - 預先做 DNS 解析,幫助網頁載入速度更快">DNS Prefetching - 預先做 DNS 解析,幫助網頁載入速度更快</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-53174094059970905882016-05-17T16:37:00.000+08:002017-08-08T13:15:30.880+08:00結構化資料之「商品」範例 - 使用JSON-LD<!DOCTYPE html>
<html>
<head>
<title>結構化資料之「商品」範例 - 使用JSON-LD</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
img {
max-width: 100%
}
</style>
</head>
<body>
<p><img src="https://lh3.googleusercontent.com/d5DZ3F0Zj__0Bp5iYOFdQ9gW4smRi5SmOQ2gDVOBnmFo51iom01epo9JuEXzYlzMzIho9ARWjs5JiQGVDfV1vaQqi4EM78_CFGxl9UvL2msuz4B6N5hBc6qGWOG-GsL_ssW0PDSYKPEAjWF_GIv7DJyNxhRi8_HbYAJNXWJUfLZ3CZs97cY3Cps1noDrZeSj9lwL3pPEW7pTjG3DqN4VFlBJ1OFGK4Ock1N_bU5z_MfPscVIWZidUxiDTaiPZc6fI3EY17c65152n2sDAZXZpf1lQN0_I4lorV4y5ILS0JVLHdwYD7OqjczJwiF5HJjntX7Dl2dVouiA63cgNUf8yF4Fm9rxPxkT5tqRkVHb50FRIb1_sejXr52LK4YrM8ExZE07Ygx0UbEyZvtXbFGv0oJKe3BMooxP1aN4F2YSeN5Y59kHUzGtNYhIv3ZoXHxWmMKt1ljYi2mHDiPUNbX_ApWIpfHO4pTJ5q5lF-JDSDLhMg6tdR6sG7cGMrK_jaCotlx8fFTEbNJ6sb-qdvwwoecyfxp0BOpQvjfTd5QHqAKrx1NOKSoZuM8P2jhXMKX59SoGq0zR-q7kkf2N1iEbAPuobk3VH1s=w800-h494-no" alt="結構化資料之「商品」範例 - 使用JSON-LD" /></p>
<p>假設想在搜尋結果頁(SERPs)上對 <strong>單一商品</strong> 呈現更為吸引人的Rich Snippet效果,例如出現星等、評論數(如下圖),就需要利用結構化資料(Structured Data)來達成。</p>
<a name='more'></a>
<p><img src="https://lh3.googleusercontent.com/LHGHzHkrt5jYOol3li6moMmB_74p5WwIMRl5KOKuSzknWeCcFuvf5N3yzo0Jtv-TPuQGWZAsPMNX5S_NXNphY4BewHODMPqUOjy1WaCdafdn5BUnjKNsIp-MghHzSre2AEi8uX2iRw4o8Mbf67zAotaQU2lHLHe_hDaAehn00AWuWO-Ckhx5xaclnZvfafNZd5s8qjLj6mBe2Eo9nXS2CgeV12x_qLyusDH0GZ3xAfM3mVr-PL43LLv1jjALB4n5KvUeWnMX1x39b5D4C7OAjt-BOp7TnqbYiuMQapcIod2dHABK5r6xJHPu03o2AMBF3gM4cUXQAgxRb8oOaPaPZ2sPDZVhu0CbZeju9uWBhNiQPpe9FG5X2AadutkTXUuq3PdrdkOUv_-KF_sXSAyenqjBN56aq-LDBf1GkANhsUrZPADSwT_MKc-BL8bgfsjJq2b_tfknkCEUOhXyi-d7hlJ-efeqLw6jbVZ3pvEFb7-UPwFYW6FsrsooHZa5_uYXVlCFETXEgt2P9GF053y1xRYGu7A4SZCA3K0o1E_hffrD6jbVg-M_Y2I6GXFpQ5YQivW8crrZsTl9gzpMGQMEE1HX_wAWDZo=w514-h104-no" alt="結構化資料之「商品」範例 - 使用JSON-LD Pinkoi範例" /> </p>
<p>至於資料格式種類和需要哪些欄位,Google有很詳細的說明文件<a target="_blank" href="https://developers.google.com/structured-data/rich-snippets/products">Enabling Rich Snippets for Products</a>,當然也可以參考更詳細的<a target="_blank" href="https://schema.org/Product">Product - schema.org</a>。</p>
<h2>程式碼範例</h2>
<p>我在個人實驗網站上做了一個小商城,幫裡面的商品單頁撰寫結構化資料(使用JSON-LD)。範例頁請見<a target="_blank" href="http://cythilya.apphb.com/EShopper/Home/Product?id=6">泰迪熊盪鞦韆 (ID: 6) | 拍賣桑莫的生活。SEO遊樂場</a>。抽出商品部份如下所示。由於官方文件對於個欄位都有很詳細的解說,在此就不做說明了。</p>
<pre><code><script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Product",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 4.5,
"reviewCount": 100
},
"description": "可愛的泰迪熊在盪鞦韆呢!快買一個回家玩吧!",
"name": "泰迪熊盪鞦韆",
"productId": "6",
"offers": {
"@type": "Offer",
"availability": "http://schema.org/InStock",
"price": "99999",
"priceCurrency": "TWD"
},
"image": "http://cythilya.apphb.com/content/eshopper/images/product/bear_on_hanging_seat.jpg",
"brand": "泰迪熊盪鞦韆",
"review":
[
{
"@type": "Review",
"author": "Summer",
"datePublished": "2015-06-24",
"description": "值得買,很超值!",
"name": "泰迪熊盪鞦韆",
"reviewRating":
{
"@type": "Rating",
"bestRating": "5",
"ratingValue": "4.5",
"worstRating": "1"
}
},
{
"@type": "Review",
"author": "Lucas",
"datePublished": "2015-07-07",
"description": "再買一個都不為過!",
"name": "泰迪熊盪鞦韆",
"reviewRating":
{
"@type": "Rating",
"bestRating": "5",
"ratingValue": "4",
"worstRating": "1"
}
}
]
}
</script>
</code></pre>
<h2>測試</h2>
<p>到<a target="_blank" href="https://search.google.com/structured-data/testing-tool">Structured Data Testing Tool</a>測試一下 - 結果正確。</p>
<p><img src="https://lh3.googleusercontent.com/XThJP-7PmPj3eb0uV5icF-f7B94Fk3Z-zo4W_setLCk3PXmmBSHCAnvZjohzOV2gxcB_vT9EcER7uoenJujB9RKNKOvXkkX2_ndb4BkVxX2N9j4KD6TQNiihjxYXHr04WXma8-HYsJliAqdfOQDwVDnM8nzQgF4Kw7qltzgvPgIH51ZdsOjgNamE2Ipwx788iLWEun48Uo4kHyU6uqWopsH-5k6vm-ETXE-i6Rtut_7M5s8ht9jcxhkEiWTXvQTjmNPsfsANXXUntqIYEuMdZ0FX41qrrsAsRM_VrGA8OPQCPWtEQyqpX_jBBHQ2ZqyNZHXwvkHmFhEQoKtDx_LDiCeNLjerU8ogg3kPosH6126gDCuUdMqBKv--_5QMSwf3Ge8E0f-DVyPiB2f6-FL8RD0Z9jxQuE1CmSTs4UoIJiMjJasckWVEAn0G7KUJq2t9jPGEQci-KvNsH65BtE_2UnpT1bsBJx6xe9DvS2H6NjcVQgvREEsQ-PjoNiEqI9kJRJYnxU_uNWINl1-iecKdVBm2e7bVZykNrAsLzo-AAjWRq2_ltUvvsnL5-iuLTMZ1lKDw3Vh-dJ6MjgQCp6kpJToHUGPaM6I=w800-h404-no" alt="結構化資料之「商品」範例 - 使用JSON-LD 測試結果" /></p>
<p>注意,結構化資料的欄位是會變動的,這段時間也許是用這些欄位,過些時間或許就更新了。可以關注<a target="_blank" href="https://github.com/schemaorg/schemaorg/issues">schema.org project - Github presence for schema.org project</a>所提出的相關issues。</p>
<h2>搜尋結果</h2>
<p><img src="https://lh3.googleusercontent.com/jxmZI3D2lCgEvQmKOoZKPm3Mi6G121odld6y575E1p4mFy6X4buxpVL6vBGMeV4gVeX_qBt_AJWswiGbYqFUoCxijmZ15_mn7WLxBOy5Xv1VnUgDJEW2NqZoORM8rien4UT5_UedCIfoSbyy8_8yB1S9gM7HlwuOciJqxwu6tjbdts3Z4ebUh_balShX2IT96WfzrNFo0ay0UilenafhcMI9CnvMD1ExX-nJOjTWtlNYXaTuRnlfITiO_tSG0gnx--8U8MU-edSuEZ1YMElttI49T3K7ECA0AurhkRoc8p-R-7vvZMF03kMzplP0nxVEqddSsJVDLbp6cYpAwHCmif3ip3dvLn3hVMS4Bm6kUAIzZE5PpWyvOehCwS8xTOsEOFM1pY9yIRKjs8omOmCqOCNRxEaGlui2jSX0V9yCmrWSZZSgJuLkLAccsiAj8xTMhqucv7SWaJ8gASPf4UAmix7W-I28-8WVARc7izNwsjhfuigX4MB4ionZPZHbeUnN0B6eeiMqVJo4cl-sM1B41LTFv2NQT1z94yRqIOmWpvtfoBKCqmlKesrQX73z3PGDXiNRYntAaf_TyZG4bkyxh60mWsCPmvE=w545-h311-no" alt="結構化資料之「商品」範例 - 使用JSON-LD 搜尋結果" /></p>
<p>這些測試用的商品頁面大概是去年底做好的,但直到這個月才發現在搜尋結果頁上出現 Rich Snippet 的效果。所以,如果你幫商品頁撰寫了結構化資料,測試也沒問題,甚至webmaster上也看到index了,但就是無法在搜尋結果上看到成效--等等吧!可能會需要等好長一段時間噢!</p>
<h2>後記</h2>
<p>這篇文章<a target="_blank" href="http://webmeup.com/blog/ecommerce-rich-snippets.html">6 rich snippet formats any e-commerce site can use to its advantage</a>
整理電商了會用到的結構化資料相關應用。其中商品圖片與影片的呈現,這在目前Product的Rich Snippet上是缺少的,但可以用這篇文章提出的方式「VideoObject Schema」和「ImageObject Schema」補足。</p>
<p>自從轉行以後(其實也不是轉很大,只是從活動相關網站轉到電商而已),更是深刻體悟到一個網站的行銷概念、流程、UI設計會影響後續的數據蒐集與分析。而做一個 SEO Engineer 最有趣的地方,就是和一般只關心開發(開規格和寫程式)的工程師不同--必須參與產品規劃、思考資料蒐集與分析方法、時時調整和去除無法達成目標的變因等。幸運如我,在廣大的前端領域中找到了一個真心喜歡的小天地 :)</p>
<hr />
<h2>推薦閱讀</h2>
<ul>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2015/06/structured-data-event.html">結構化資料之「活動」範例 (Part 1)</a></li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2015/06/structured-data-event-2.html">結構化資料之「活動」範例 (Part 2) - 使用Microdata與JSON-LD</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/05/17/rich-snippet-product-jsonld/" target="_blank" title="結構化資料之「商品」範例 - 使用 JSON-LD">結構化資料之「商品」範例 - 使用 JSON-LD</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-58018679230337517182016-03-13T16:58:00.002+08:002017-08-08T13:09:27.233+08:00jQuery的陣列操作:$.map()與$.grep()<!DOCTYPE html>
<html>
<head>
<title>jQuery的陣列操作:$.map()與$.grep()</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p>這陣子在整理系統內的程式碼,發現自己曾經使用一些過時的方法(例如:native javascript操作陣列),而非使用jQuery提供的API來撰寫。以下整理並比較這兩種方法的差異。
</p>
<a name='more'></a>
<p>先製造一筆假資料當範例。</p>
<pre><code>function Person(name, sex){
this.name = name;
this.sex = sex;
};
var people = [
new Person('Apple', 'female'),
new Person('Bob', 'male'),
new Person('Helen', 'female'),
new Person('Jacky', 'male'),
new Person('Oliver', 'male')
];
</code></pre>
<p>這種產生物件的方法可參考<a target="_blank" href="http://cythilya.blogspot.tw/2016/02/all-in-one-constructor-pattern.html">All-in-one Constructor Pattern</a>。</p>
<h2>產生一個新的陣列,但只包含原陣列的特定欄位</h2>
<p>產生一個新的陣列(nameList),只包含人名(name)這個欄位。</p>
<h3>使用JavaScript操作</h3>
<p>將每個項目的人名(name)丟(push)到新的陣列裡面。</p>
<pre><code>var nameList = [];
$.each(people, function(index, value){
nameList1.push(value.name);
});
console.log(nameList); //["Apple", "Bob", "Helen", "Jacky", "Oliver"]
</code></pre>
<h3>使用jQuery操作</h3>
<p>使用$.map()將項目的特定欄位丟(return)到新的陣列裡面。</p>
<pre><code>var nameList = $.map(people, function(item, index){
return item.name;
})
console.log(nameList); //["Apple", "Bob", "Helen", "Jacky", "Oliver"]
</code></pre>
<h3>結果</h3>
<p>得到一個新的陣列,只包含人名這個欄位。</p>
<p><img src="https://goo.gl/EwzJdl" alt="$.map()" /></p>
<h2>產生一個新的陣列,但只包含合乎條件的項目</h2>
<p>取得女性項目。</p>
<h2>使用JavaScript操作</h2>
<p>先比對是否合乎條件(<code>if(value.sex === 'female')</code>)。若合乎條件,則將項目丟(push)到新的陣列裡面。</p>
<pre><code>var femaleList = [];
$.each(people, function(index, value){
if(value.sex === 'female'){
femaleList.push(value);
}
});
console.log(femaleList);
</code></pre>
<h2>使用jQuery操作</h2>
<p>使用$.grep()將符合條件的項目丟(return)到新的陣列裡面。</p>
<pre><code>var femaleList = $.grep(people, function(item, index){
return item.sex === 'female';
});
console.log(femaleList);
</code></pre>
<h3>結果</h3>
<p>得到一個新的陣列,但只包含合乎條件的項目。</p>
<p><img src="https://goo.gl/EMxIhg" alt="$.grep()" /></p>
<p>「學而時習之,不亦說乎?」常常翻新程式碼,檢視是否能優化,是讓自己更進步的好方法 :)
</p>
<hr />
<h2>參考資料</h2>
<ul>
<li><a target="_blank" href="http://api.jquery.com/jquery.map">jQuery.map() | jQuery API Documentation</a></li>
<li><a target="_blank" href="http://api.jquery.com/jquery.grep">jQuery.grep() | jQuery API Documentation</a></li>
<li><a target="_blank" href="http://blog.darkthread.net/post-2015-06-22-jquery-map-and-grep.aspx">介紹jQuery map()與grep()</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/03/13/jquery-map-grep/" target="_blank" title="jQuery的陣列操作:$.map()與$.grep()">jQuery的陣列操作:$.map()與$.grep()</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-34929282454122620282016-03-05T13:05:00.000+08:002017-05-28T15:32:10.033+08:00三天內學會CSS3 Animation<!DOCTYPE html>
<html>
<head>
<title>三天內學會CSS3 Animation</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p>一直以來我對CSS3 Animation這件事情並沒有特別專注研究(如果需要也是用javascript完成XD),直到最近因為工作需要才開始練習。以下紀錄一些我在這三天內看的資料和做的小練習(不是教學文)。</p>
<p>如果你和我一樣,平常很少使用特效,需要在短時間內熟悉而有即戰力,這篇文章也許可以給你一些方向。</p>
<a name='more'></a>
<p>一開始先到<a target="_blank" href="http://www.w3schools.com/css/css3_animations.asp">W3School</a>先把所有指令快速看一次,大概熟悉這些指令和屬性就可以了。
</p>
<ul>
<li>@keyframes:放置動畫程式碼的地方。</li>
<li>animation:總稱,包含animation-delay、animation-direction、animation-duration、animation-fill-mode、animation-iteration-count、animation-name、animation-play-state和animation-timing-function。</li>
<li>animation-delay:動畫延遲多久才開始。</li>
<li>animation-direction:動畫進行的方向。</li>
<li>animation-duration:動畫從開始到結束需要多少時間。</li>
<li>animation-fill-mode:動畫結束時停留的狀態,維持在初始或結束時等狀態。</li>
<li>animation-iteration-count:動畫重覆次數,可設定無限次。</li>
<li>animation-name:@keyframes的名稱。</li>
<li>animation-play-state:動畫撥放或暫停。</li>
<li>animation-timing-function:運動曲線函數,例如:ease、linear等。</li>
</ul>
<p>並做了一個非常簡單樸素的練習。
</p>
<p><img src="https://cythilya.github.io/assets/2016-03-05-css3-animations-1.gif" alt="CSS3 Animation" />
</p>
<p>是不是單純的可愛呢 XD</p>
<p>然後再到<a target="_blank" href="https://developer.mozilla.org/zh-TW/docs/Web/CSS/CSS_Animations/Using_CSS_animations">MDN</a>補一下細節,對指令有較深入的了解,接下來就可以找些較複雜的範例來練習了(照著打就對了)。</p>
<p>第一個範例是<a target="_blank" href="https://css-tricks.com/css3-clock">Old School Clock with CSS3 and jQuery</a>,文章內會一步一步帶著大家完成。雖然範例中沒有用到<code>@keyframes</code>,但如果你跟我一樣習慣用javascript操作物件,這是一個讓人卸下緊張害怕的心防的好練習題噢(其實也沒那麼神祕嘛!),之後要改寫成純粹的CSS3 Animation也沒問題。
</p>
<p><img src="https://cythilya.github.io/assets/2016-03-05-css3-animations-2.gif" alt="CSS3 Animation - Old School Clock with CSS3 and jQuer" /></p>
<p>再來,就是來做比較複雜的練習,把在Tutorial學到的都應用上去 - <a target="_blank" href="http://codepen.io/kowlor/pen/ZYYQoy">Solar System animation - Pure CSS</a>。</p>
<p><img src="https://cythilya.github.io/assets/2016-03-05-css3-animations-3.gif" alt="CSS3 Animation - Solar System animation - Pure CSS" /></p>
<p>再來個有時序概念的練習...<a target="_blank" href="http://www.alessioatzeni.com/blog/css3-graph-animation">CSS3 Graph Animation</a></p>
<p><img src="https://cythilya.github.io/assets/2016-03-05-css3-animations-4.gif" alt="CSS3 Graph Animation" /></p>
<p>順便推薦兩個我滿喜歡的範例...<a target="_blank" href="http://codepen.io/charisseysabel/pen/adXGMe">A love for Cats</a>和<a target="_blank" href="http://codepen.io/N_R_Web_Designer/pen/zxwVKX">Animated CSS3 Heart</a>。如果有時間的話多練習這兩個也不錯。</p>
<p><img src="https://cythilya.github.io/assets/2016-03-05-css3-animations-5.gif" alt="CSS3 Animation - A love for Cats" />
</p>
<p><img src="https://cythilya.github.io/assets/2016-03-05-css3-animations-6.gif" alt="Animated CSS3 Heart" />
</p>
<p>以上練習都會放在我的<a target="_blank" href="https://github.com/cythilya/css3-animation">github</a>上。做完範例就提槍上戰場啦。</p>
<hr />
<h2>References</h2>
<h3>Tutorial</h3>
<ul>
<li><a target="_blank" href="http://www.w3schools.com/css/css3_animations.asp">CSS3 Animations | W3School</a>:W3School的CSS3 Animations講得非常淺顯易懂,如果想要先快速有個概念,這是一個很好的選擇。</li>
<li><a target="_blank" href="https://developer.mozilla.org/zh-TW/docs/Web/CSS/CSS_Animations/Using_CSS_animations">CSS 動畫 - CSS | MDN</a>:對於每個指令有較深入的說明,可以補充和當字典查詢。</li>
</ul>
<h3>Exmaple</h3>
<ul>
<li><a target="_blank" href="https://css-tricks.com/css3-clock">Old School Clock with CSS3 and jQuery</a>:非常適合第一次接觸CSS3 Animation來做的第一個練習,結構簡單。</li>
<li><a target="_blank" href="http://codepen.io/juliangarnier/pen/idhuG">CSS 3D Solar System</a>:漂亮的太陽系動畫。</li>
<li><a target="_blank" href="http://codepen.io/kowlor/pen/ZYYQoy">Solar System animation - Pure CSS</a>:也是一個漂亮的太陽系動畫,但結構較簡單。</li>
<li><a target="_blank" href="http://www.alessioatzeni.com/blog/css3-graph-animation">CSS3 Graph Animation</a>:有時序概念。</li>
<li><a target="_blank" href="http://codepen.io/charisseysabel/pen/adXGMe">A love for Cats</a></li>
<li><a target="_blank" href="http://codepen.io/N_R_Web_Designer/pen/zxwVKX">Animated CSS3 Heart</a></li>
</ul>
<hr>
<div>由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/03/05/css3-animations/" target="_blank" title="三天內學會 CSS3 動畫">三天內學會 CSS3 動畫</a>。</div>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-8441708243752527142016-02-11T12:08:00.003+08:002017-08-08T12:41:50.890+08:00JavaScript Object Oriented Programming<!DOCTYPE html>
<html>
<head>
<title>JavaScript Object Oriented Programming</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img alt="JavaScript Object Oriented Programming" src="https://lh3.googleusercontent.com/Rn35cFnx7aHiC_isZ05i-CWmREvuhoU3Ndg5nz_pJ2A9MAkgTaMyT6dnKzt-GXgpzJOTKIS7r9Y4IYwSSLitVq1hj3W1dsNf9WJnmHfnyCGUHdURJeXyNJZIv9A5UxLDmc03wCrCYO-be4kUpzypPyFqTGchoNX2-xxPakA61ojC-CV9IZMfTlQfL0ktrNE0VigxqxOOG8RWTgByuRZ3X4F6PNqos27bBVtVbkXVpcpdcR4zTOaZkGaGLnlnV8uvlxSwyWplHSVxOYE-rTGGDurNM8iCYRP_gsNjg2gkGYhTZVATNwDuF04J0TeKFSVATi8LVXactbWjlsojV1cnPmAfNuvK0a_dZ2loNa8ZMzbPrpo-JoWiL_Qfh-f-ezJsoMEneM5hIa8lRl8vuna0icfF0jvGsVbPutsJULkki-U7aZqcS_JX1O3bgDFwip6DxOEiTTu6Ec3bny1FCN3TkZp9eNHvyLNYxkj6TpYAAQBpE7ik2IJQ4yR88XztmXi9YCqbZeRduCXdHUzW-UMZlyvHBg1F1Z5P5sfd50y6E0S5ki05iyNam9eYOSstaQtQPSbH=w800-h494-no">
<p>關於JavaScript Object Oriented Programming,會寫這一系列的文章是因為希望自己的JavaScript程式碼能更物件導向、更模組化。這一系列的文章有以下的內容...</p>
<a name='more'></a>
<ul>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/01/prototypal-inheritance.html">Prototypal Inheritance</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/prototypal-inheritance.md">Github版</a>:繼承的基本觀念。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/extending-natives.html">Extending Natives</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/extending-natives.md">Github版</a>:使用prototype來實作繼承。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/constructor.html">The "constructor" property</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/constructor.md">Github版</a>:建構子。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/instanceof.html">The "instanceof" operator</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/instanceof.md">Github版</a>:檢查是由哪個建構子所產生的物件。</li>
<li>
OOP patterns
<ul>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/general-concepts.md">General Concepts (Github版)</a>:物件導向的基本概念。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/javascript-pseudo-classical-pattern.html">Pseudo-Classical Pattern</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/pseudo-classical-pattern/pseudo-classical-pattern.md">Github版</a>:在pseudo-classical pattern中,物件是由「建構子」(constructor)這個函式所建立,並把method放到建構子的prototype中。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/all-in-one-constructor-pattern.html">All-in-one Constructor Pattern</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/all-in-one-constructor-pattern.md">Github版</a>:所有的method和property都放在consctructor中,而不使用prototype。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/factory-constructor-pattern.html">Factory Constructor Pattern</a>,<a target="_blank" href="">Github版</a>:不使用new來宣告新物件,新物件用function call來建立。</li>
</ul>
</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/early-and-late-binding.html">Early and Late Binding</a>,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/early-and-late-binding.md">Github版</a>:JavaScript在呼叫的時候設定this的值,而這個this的值有可能不是我們預期的結果。</li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2016/02/exceptions.html">Exceptions</a> ,<a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/exceptions.md">Github版</a>:例外處理。</li>
</ul>
<p>主要是參考自<a target="_blank" href="http://javascript.info/tutorial/object-oriented-programming">Object Oriented Programming | JavaScript Tutorial</a>,然後加些自己的想法和目前遇到的狀況,算是翻譯文+讀書筆記,過一陣子再複習後一定會有新的體悟,到時候再更新搂。希望大大們能不吝指教 :)</p>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/11/javascript-object-oriented-programming/" target="_blank" title="JavaScript Object Oriented Programming">JavaScript Object Oriented Programming</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-57155332868380219122016-02-10T14:05:00.001+08:002017-08-08T12:32:59.937+08:00JavaScript Object Oriented Programming - Early and Late Binding<!DOCTYPE html>
<html>
<head>
<title>JavaScript Object Oriented Programming - Early and Late Binding</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img alt="JavaScript Object Oriented Programming - Early and Late Binding" src="https://lh3.googleusercontent.com/4g3ejRDpVSvdnQWXxkgxXT6simad2C9ypq5vqLGZ0AZ6qo61ITiRtoQP7nzWDAGo8_2RWAckxVS4tAyzGHk-cfQO3bDFbe-yOzI6l24rVBAjTNAJ9TB4LKP04EUCvHVf1rDDy-W_pi8n3b11f3HfiGoYcAvPe9fwFArk95z-V0rkl56D0rm_8ZDqglHsHjP1o2HxnjfGVgFaYQxuBzrD6Ie31aqN8tvSCUhrhxdea_wBmRn6i6Oj5JQdT09prFzcbMMDRGlsyGRfnaawGMZbakmxFEA9B36YOHku7tbUAoUrYaoVTWwB-IlI1LFrILRV5h4vcaUHA71eNUtKQ50Cyp6-FJIBFgMM5sJ3gLQ27VQ4fdJUUwF-0jK9oNQBHJGmkrsVVTb6_F5tYYq2wRH2w25nIJneZ0SUh7VDdQLEAfxwGGDK1zAPslc-VZm8b2XwCFMxrO3vPo9g186VWfimmVHeu6aTQ_ZC_TRIYd3Efa4t9BMdtO6MAeMGCYASsnk_R9S8zgnxLpMQxsfu2eoaeWxrFctE1xiM0k__SiMmDMuWsctUy_eIu8lYzGbVi_kwMVQ8=w800-h494-no">
<p>JavaScript在呼叫的時候設定this的值,而這個this的值有可能不是我們預期的結果。下面有幾個範例。</p>
<h2>this的值並非預期的狀況</h2>
<p>以下都會由Menu這個建構子作為範例。</p>
<pre><code>function Menu(elem){
//...
};
var menu = new Menu(document.createElement('div'));
</code></pre>
<a name='more'></a>
<h3>setTimeout</h3>
<p>this是參考到Window,而非menu。這是因為setTimeout永遠是在window context下執行。</p>
<pre><code>function Menu(elem){
setTimeout(function(){
console.log(this); //Window
}, 500);
};
var menu = new Menu(document.createElement('div'));
</code></pre>
<h3>onclick</h3>
<p>event handler永遠是將this設定給傳入的物件(例如:在此是DOM Element「elem」 ),而非menu。但menu才是我們預期的結果。</p>
<pre><code>function Menu(elem){
elem.onclick = function(){
console.log(this); //elem, not Menu
};
};
var menu = new Menu(document.createElement('div'));
</code></pre>
<h3>Private Method / Local Function</h3>
<p>private method 或 local function內的this若沒有特別指定,則值是Window。</p>
<pre><code>function Menu(elem){
function privateMethod(){
console.log(this);
};
privateMethod(); //Window
};
var menu = new Menu(document.createElement('div'));
</code></pre>
<p>我們需要一些解法來解決這個問題 - 能在function內存取到menu物件。解法有三種 - 使用 <code>var self = this</code> 做綁定、Early binding和Late binding。</p>
<h2>Binding with var self = this</h2>
<p>在進入setTimeout之前,我們先將正確的this儲存在區域變數self裡面,避免this進入setTimeout後變成Window。然後我們便可在任何地方使用self了。除了setTimeout,event handle和private method/local function也可以使用此解法。</p>
<pre><code>function Menu(elem){
var self = this;
setTimeout(function(){
console.log(self); //Menu
});
};
new Menu(document.createElement('div'));
</code></pre>
<h2>Early Binding</h2>
<p>我們可以使用bind這樣的helper function - bind接受參數(函式func和this)並回傳一個閉包 - 將this替換成此func的內容狀態。這等於是強制替換this的值。</p>
<pre><code>function bind(func, fixThis){
return function(){
return func.apply(fixThis, arguments);
}
};
function Menu(elem){
elem.onclick = bind(function(){
console.log(this); //Menu
}, this);
};
new Menu(document.createElement('div'));
</code></pre>
<h3>Function.prototype.bind</h3>
<p>其實我們不用那麼辛苦 - 自己寫一個bind這樣的helper function,直接使用原生的<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">Function.prototype.bind</a>即可!如果舊瀏覽器不支援,我們也可以像以下這樣擴充它。</p>
<pre><code>Function.prototype.bind = Function.prototype.bind || function(fixThis){
var func = this;
return function(){
return func.apply(fixThis, arguments);
}
};
</code></pre>
<p>因此,setTimeout、event handle和private method/local function也可以使用此解法了。</p>
<h4>setTimeout</h4>
<pre><code>function Menu(elem){
setTimeout(function(){
console.log(this); //Menu
}.bind(this), 1000);
};
new Menu(document.createElement('div'));
</code></pre>
<h4>onclick</h4>
<pre><code>function Menu(elem) {
elem.onclick = function() {
console.log(this); //Menu
}.bind(this);
};
new Menu(document.createElement('div'));
</code></pre>
<h4>Private Method / Local Function</h4>
<pre><code>function Menu(elen){
var privateMethod = function (){
console.log(this);
}.bind(this);
privateMethod();
};
</code></pre>
<p>new Menu(document.createElement('div'));</p>
<h3>比較 bind 和 <code>var self = this</code> 的差異</h3>
<ul>
<li>bind不需要包在closure(閉包)之內。bind綁定是即時且永久的。bind勝!</li>
<li>bind必須要在每個要使用的funciton加上去。<code>var self = this</code> 勝!</li>
<li><code>var self = this</code>必須額外加上去。bind勝!</li>
</ul>
<h2>Late Binding</h2>
<p>呼叫的時候才做綁定的動作。</p>
<h3>Early Binding的問題</h3>
<pre><code>function bind(func, fixThis) { // using custom bind for simplicity
return function() {
return func.apply(fixThis, arguments);
}
}
function Menu(elem) {
this.sayHi = function() { console.log('Menu'); };
elem.onclick = bind(this.sayHi, this);
}
function SuperMenu(elem) {
Menu.apply(this, arguments);
this.sayHi = function() { console.log('SuperMenu'); };
}
new SuperMenu(document.body);
</code></pre>
<p>底下的程式碼預期印出 「SuperMenu」,但其實印出的是「Menu」。這是因為onclicke這個event handler使用的 <code>this.sayHi</code> 的this是指Menu,而非SuperMenu。</p>
<h3>使用Late Binding</h3>
<p>修正一下即可。</p>
<pre><code>function bindLate(funcName, fixThis) { //instead of bind
return function() {
return fixThis[funcName].apply(fixThis, arguments);
}
};
function Menu(elem) {
this.sayHi = function() { console.log('Menu'); };
elem.onclick = bindLate('sayHi', this);
};
function SuperMenu(elem) {
Menu.apply(this, arguments);
this.sayHi = function() { console.log('SuperMenu'); };
};
new SuperMenu(document.body);
</code></pre>
<h2>總結三種binding方法</h2>
<ul>
<li>將this存在區域變數中(<code>var self = this</code>)。</li>
<li>使用bind helper funciton或原生的Function.prototype.bind。</li>
<li>使用late binding。</li>
</ul>
<hr />
<h4>References</h4>
<ul>
<li><a href="http://javascript.info/tutorial/binding">Early and Late Binding | JavaScript Tutorial</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">Function.prototype.bind()</a></li>
<li><a href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/early-and-late-binding.md">JavaScript Object Oriented Programming - Early and Late Binding筆記</a>:持續更新中...</li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/10/early-and-late-binding/" target="_blank" title="JavaScript Object Oriented Programming: Early and Late Binding">JavaScript Object Oriented Programming: Early and Late Binding</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-45573030732189252542016-02-09T17:56:00.001+08:002017-08-08T12:23:59.860+08:00JavaScript Object Oriented Programming - Exceptions<!DOCTYPE html>
<html>
<head>
<title>JavaScript Object Oriented Programming - Exceptions</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img src="https://lh3.googleusercontent.com/01qHY7HcFI7ciFnM6oE_7bMExWNNn_9X7pmyUniY0YRsuqrSa3CZnrxSuc1MmfB64bZ1J0-IJNixNr7L1Q3gW8rMmzDMKRd8adYqmVosS_6dh1UEPbWUElrha9ApFj2xGjymbC9OzCzGtdBR4ou37nbUhkxXYeteZjCi7esWN6rTxFtsqPzLE3omw4jTZC4vlyiCOaW1JDOhwnw3FIYtbDN_NpwtizFje0D1Aytf7T4qhG9NldEOFT_OV64vQrY0QI9dmwh6obAgnKRCWIY8GqRjRs0tUjNn8kMZv_zxvfEvdFIbn3Fj-k7feVWXCf7IhD1avzoNF07BPTzMJ2jfpf_Cnkkdj3CdmDT6khT7XLIz_PnUZoSon95RLYKwXmFvVHgjSZ4vIYHfJ7tQ-DvEn2yHHP_HYhhLLZMay_QB1v8Bp8Mxjd_VbbbiO6nXdEKpEm0qegBYYIHfZiHXAWqykAdcFhb7pMT4xx62ehS7qBEHmZcFpzfcvG69O9_221slmvd8XCFMUFZCjuAz0YCPkWBRaJGnmRWUJn4zezmUQgLY2QSzOCi2CoOTk8Op0ANMmVDe=w800-h494-no" alt="JavaScript Object Oriented Programming - Exceptions">
<p>「例外(Exceptions)」是一個特別且重要的處理錯誤的方法。</p>
<a name='more'></a>
<h2>Check-first error handling</h2>
<p>假設我們要執行func這個method,首先會檢查這個method是否存在。</p>
<pre><code>if (window.func) {
func();
}
</code></pre>
<p>但檢查了是否存在,不代表型別正確,因此我們必須再檢查這個method的型別是否為function。</p>
<pre><code>if (typeof(func) == 'function') {
func();
}
</code></pre>
<p>但意外或錯誤是永遠檢查不完的,例如,這個method的內部可能有些問題。因此,我們可以改用「Try..Catch」來處理。</p>
<h2>The try..catch construct</h2>
<p>上一個例子是預先把可能發生的問題都檢查一次,例如:存在與否、型別是否正確,如果通過檢查再執行。而「Try..Catch」從另外一個角度來處理 - 先執行,如果有錯再處理。範例如下。</p>
<pre><code>try {
func();
}
catch(e) {
alert(e);
}
</code></pre>
<p>這個「e」會告訴我們到底發生什麼問題。</p>
<pre><code>try {
var a = 5, res = func(a);
if (res > 0) {
doA();
}
else {
doB();
}
}
catch(e) {
console.log("name: " + e.name); //name: ReferenceError
console.log("message: " + e.message); //message: doB is not defined
}
</code></pre>
<p>由錯誤訊息可知,doB這個function沒有被定義,因此無法執行而出錯。</p>
<h2>The full form of try..catch..finally</h2>
<pre><code>try {
//try statemenets ..
}
catch(exception) {
//catch statements ..
}
finally {
//finally statements ..
}
</code></pre>
<p>首先執行try區塊內的程式碼,如果沒有出錯,則catch區塊會被忽略不執行。如果有錯誤產生,則exception會被設定值,catch區塊會被執行。無論如何,最後,finally區塊都會被執行。</p>
<h3>try..catch..finally and return</h3>
<p>在try區塊執行return statments,控制權會在執行完finally區塊後才交回給原呼叫的function。所以如下程式碼所示,會先alert 「done」,再alert 「2」。</p>
<pre><code>function inc(a) {
try {
return a+1;
}
catch(e) {
//catch statements...
}
finally {
alert('done'); //1. alert "done"
}
}
alert( inc(1) ); //2. alert "2"
</code></pre>
<h2>The throw statement</h2>
<p>基本上,錯誤可以分為兩種:「Programmatic errors」和「Execution flow errors」。</p>
<ul>
<li>Programmatic errors:程式撰寫錯誤,例如:錯字。</li>
<li>Execution flow errors:執行時的錯誤,例如:使用者輸入不符合預定格式的資料,程式提示格式有誤並重新輸入。</li>
</ul>
<p>因此,使用try...catch與throw於Execution flow errors是一個不錯的選擇,範例如下。</p>
<pre><code>try {
throw 5;
}
catch(e) {
alert("Caught: " + e);
}
</code></pre>
<h3>A validator example</h3>
<p>假設我們需要一個年齡的驗證工具,幫助我們檢查使用者輸入的年齡是否合法。如果沒有輸入任何東西,則跳出;如果輸入非文字,則丟出錯誤;如果是數字,則顯示可接受的訊息。</p>
<pre><code>function validatorAge(age){
if(age === ''){
return; //no age to valid
}
age = +age;
if(isNaN(age)){
throw {
name: 'BadAge',
message: 'Age out of range'
}
}
}
try {
var age = prompt('Enter your age');
validatorAge(age);
alert('The age is accepted');
}
catch(e){
alert('Error: ' + e.message);
}
</code></pre>
<h3>Changes in the usage pattern</h3>
<p>除了try...catch的用法,當然我們也可以使用error-checking的方法。</p>
<pre><code>function validatorAge(age){
if(age === ''){
return; //no age to valid
}
age = +age;
if(isNaN(age)){
return false;
}
else{
return true;
}
}
var value = prompt('Enter your age'),
error = validatorAge(value);
if(!error){
//process error
alert('Invalid error!');
}
else{
//success
alert('The age is accepted');
}
</code></pre>
<h3>Comparison</h3>
<ul>
<li>try..catch較簡潔,可讀性較高。</li>
<li>error-checking無法檢查所有的錯誤,但try..catch確可捕捉所有錯誤,所以try..catch是唯一萬無一失的方法。尤其在檢查瀏覽器相容的錯誤的時候。</li>
</ul>
<h2>Exception analysis and rethrow</h2>
<p>有時候,程式碼可能會產生不同種類的錯誤。因此,我們使用「if」來選擇適當的動作。結構類似如下:</p>
<pre><code>try {
// 1. do smth
}
catch(e) {
if (e instanceof ValidationError) {
// 2.1 process e
}
else if (e instanceof PermissionError) {
// 2.2 process e
}
else {
// 3. we don't know how to deal with e
throw e
}
}
</code></pre>
<p>在try區塊可能丟出了某些錯誤,有些錯誤是我們已知的,例如:ValidationError和PermissionError,然後去處理這些已知的e。但有些我們並不確定,因此直接丟出e。這裡丟出的e,需要由外層的try..catch來處理。</p>
<h2>Summary</h2>
<ul>
<li>try..catch..finally將多種檢查合併在一個try區塊內執行,並將error-handlin分散在catch區塊來做錯誤的處理。</li>
<li>try..catch..finally能捕捉和處理所有的錯誤。丟出的錯誤可使用JavaScript-generated或自行定義的。</li>
<li>錯誤建議繼承基本錯誤物件。因此,可用instanceof來檢查錯誤的類型,如上範例檢查ValidationError和PermissionError。</li>
</ul>
<hr />
<h2>Reference</h2>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/exceptions">Exceptions | JavaScript Tutorial</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/exceptions.md">JavaScript Object Oriented Programming - Exceptions 筆記</a>:持續更新中...</li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/09/exceptions/" target="_blank" title="JavaScript Object Oriented Programming: Exceptions">JavaScript Object Oriented Programming: Exceptions</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-49949790682742735832016-02-08T20:37:00.001+08:002017-08-08T12:01:11.044+08:00JavaScript Object Oriented Programming - Factory Constructor Pattern<!DOCTYPE html>
<html>
<head>
<title>JavaScript Object Oriented Programming - Factory Constructor Pattern</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img src="https://lh3.googleusercontent.com/iK6eUodejS4EUDBOw894dta01Tt0NpZaKbouHwhGJ3g3hSdqY7p9GLCZ_stSx7-mJ2JY0iWHMJdTh_8HsJOSSjU6Z_Rgx1gOa1JiOX0y7kQpnm4HGgQuz59lcOvHGgRO5wZT3--6vpjB8PllMrigMuCqF7RddgB7w-udwq7F_usKU6bCmQQhXd28OZi6jBy0KRaA_FbMzUsxRobRMevbAh5LQx9rAN8JllRQd4xK5x9PC6MtDPrPuU2nB2LYiJ-fCKeS5LRDlb9PoXl1iWLiK1hTm2kQZ14wi2Rl6y5xHYk6QawUeq61XCRrMNCkzXet2wyUCIs2YhvJ2sEhOgaXCPCTDRjD8lJkuAajy6lULRKuO_gNFejQz80shEtQW8C7hoYkpDW6d17TJ9DLNeSn7LYhhPEqR5Jw3sbg4HJN6rfT8jMeaZHsU54Hx9jarEF92HyzqwZxpNAivmfIyA123_3JJyN47cPoxB6hcaItocwTWWuQO4GRCboVTCavx1kdAj6t95DtMUta_1DthYy3tqsoah4z8Ife_OPtSePgpYRR-dqO4iKTmANSEanWL3vIrcWo=w800-h494-no" alt="JavaScript Object Oriented Programming - Factory Constructor Pattern">
<p>「Factory Constructor Pattern」不使用new來宣告新物件,新物件用function call來建立,如下:</p>
<pre><code>var animal = Animal('fox');
var rabbit = Rabbit('rab');
</code></pre>
<a name='more'></a>
<h2>物件宣告</h2>
<p>建構子使用function定義,並回傳一個新物件。</p>
<pre><code>function Animal(name) {
return {
run: function() {
console.log(name + " is running!")
}
}
};
</code></pre>
<p>然後我們這樣使用這個建構子來建立新物件,並沒有使用到new。</p>
<pre><code>var animal = Animal('fox'); //fox is running
animal.run();
</code></pre>
<h2>繼承</h2>
<p>「Rabbit」這個物件是繼承「Animal」所建立的建構子,然後我們來做些變化吧。</p>
<pre><code>function Rabbit(name) {
var rabbit = Animal(name); //make animal
rabbit.bounce = function() { //mutate
this.run(); //rab is running!
console.log(name + ' bounces to the skies! :)'); //rab bounces to the skies! :)
}
return rabbit //return the result
}
var rabbit = Rabbit('rab');
rabbit.bounce()
</code></pre>
<h2>封裝:Private/protected methods</h2>
<p>public method可以被外部呼叫,例如;<code>this.move()</code>,但不能被private method呼叫。private method,例如;<code>openWings</code>,不能參考this - 因此 local function在此無法參考到這個新建立的物件。(但其實我的測試結果是沒有這個問題,如果有大大可以告訴我是發生什麼事情的話,會非常感激的!)</p>
<pre><code>function Bird(name) {
var speed = 100; //private property
function openWings() { //private method
console.log('open wings');
};
return {
fly: function() {
openWings();
this.move();
},
move: function() {
console.log('move');
}
}
};
var bird = Bird('Jack');
bird.fly();
bird.move();
</code></pre>
<p>其中一個解法是使用一個local變數儲存物件來回傳。</p>
<pre><code>function Bird(name) {
var speed = 100; //private property
function openWings() { //private method
console.log('open wings');
};
function doFly() { //private method
openWings();
self.move();
};
var self ={
fly: function() {
doFly();
},
move: function() {
console.log('move');
}
}
return self
};
var bird = Bird('Jack');
bird.fly();
bird.move();
</code></pre>
<h2>Summary</h2>
<ul>
<li>Factory Constructor Pattern不使用new來宣告新物件,新物件用function call來建立</li>
<li>繼承:建立父物件,知後修改它即可。</li>
<li>區域method和method是private的,物件必須先在閉包中儲存再回傳,這樣才能在公開的method中使用。</li>
</ul>
<h3>Comparison with <a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/all-in-one-constructor-pattern.md">All-in-one constructor</a></h3>
<p>「Factory Constructor Pattern」和「All-in-one constructor」其實結果是相同的,只是寫法上稍為有些不同,選一個你喜歡的即可。</p>
<hr />
<h4>References</h4>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/factory-constructor-pattern">Factory constructor pattern | JavaScript Tutorial</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/factory-constructor-pattern.md">JavaScript Object Oriented Programming - Factory Constructor Pattern 筆記</a>:持續更新中...</li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/08/factory-constructor-pattern/" target="_blank" title="JavaScript Object Oriented Programming: Factory Constructor Pattern">JavaScript Object Oriented Programming: Factory Constructor Pattern</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-4233976926150067292016-02-07T16:14:00.001+08:002017-08-08T11:54:18.846+08:00JavaScript Object Oriented Programming - All-in-one Constructor Pattern<!DOCTYPE html>
<html>
<head>
<title>JavaScript Object Oriented Programming - All-in-one Constructor Pattern</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img alt="JavaScript Object Oriented Programming - All-in-one Constructor Pattern" src="https://lh3.googleusercontent.com/01luIDaNdJaJPQ59QsF9CxOlQD2RFtjK0jM60EW_NpQDXekxCdZTziiPCN_j6Vx67wE_JVv_WnMOKwO6J5dJTZDy4iTrHbU7GJF7f4H2llc9v_VR77dTfRv6NtkCO-SOlGGoYSy5cRm7KWUmcVwOyGkWTurlUU40A1AQuD-x8EqpNRAUw9wAa00q3oqav-nWGKo5MoYwf_-1Jnhu4Dqa_O-QdLqBIdT-TkN-Ovb4tbG8aW5FhqLZ9h3AeHJUBIgEp2Vhch_LwLTEoKfoh00wP797jRFCP7nj7o3Xm3XZ6jZ5xF53DJVl_Wo3ip3Z_G9ns0aAWjahXjb2uAjBzbw5l6Yuf-kyl7tiwUHvTad_BtuSfD0tU00n99XPvVns-XRNOL7UNkHdXv8an8aeSCA-IRM3Gi39-R3K93iypc7J_qqccCvWAK8DLFJu5w2l-eKNMWu0u2hyW_pd5N5amfFdapR4dDFAdh5jl3MHK22eefmGj1mdZefqT62n9c2sRLucjqAq8IIyr3D2QnIP_5lQ4w18rgFBX-eONGQlYa__4fLAUoyicrMe6B2gTTQHYueYjm1I=w800-h494-no">
<p>所有的method和property都放在consctructor中,而不使用prototype。</p>
<h2>Declaration</h2>
<pre><code>function Animal(name){
this.name = name;
this.run = function(){
console.log('running '+ this.name);
}
};
var animal = new Animal('Foxie');
animal.run();
</code></pre>
<a name='more'></a>
<h2>Inheritance</h2>
<p>建立Rabbit這個建構子,並繼承Animal。</p>
<pre><code>function Rabbit(name){
Animal.apply(this, arguments);
this.bounce = function(){
console.log('bouncing ' + this.name);
};
};
var rabbit = new Rabbit('Rab');
rabbit.bounce();
rabbit.run();
</code></pre>
<h2>Overriding (polymorphism)</h2>
<pre><code>function Rabbit(name){
Animal.apply(this, arguments);
var parentRun = this.run; //keep parent method
this.run = function(){
console.log('bouncing ' + this.name);
parentRun.apply(this); //call parent method
};
};
var rabbit = new Rabbit('Rab');
rabbit.run(); //inherited method
</code></pre>
<h2>Private/protected methods (encapsulation)</h2>
<p>在建構子之內的變數和函數視為private,設定給this的視為public。</p>
<pre><code>function Animal(name) {
this.name = name;
this.run = function() {
console.log('running ' + this.name);
}
}
function Rabbit(name){
Animal.call(this, 'Mr.' + name.toUpperCase());
var created = new Date(); //private
function sayHi(){ //private
console.log("I'm talking rabbit " + name);
}
this.report = function(){
sayHi.apply(this);
console.log('Created at ' + created);
};
};
var rabbit = new Rabbit('Rab');
rabbit.report();
</code></pre>
<p>protected method並沒有實質上的支援,可參考<a target="_blank" href="http://javascript.info/tutorial/pseudo-classical-pattern#private-protected-methods-encapsulation">Private/protected methods (encapsulation)</a>。</p>
<h2>Summary</h2>
<ul>
<li>在建構子完整描述物件。</li>
<li>在目前物件呼叫父建構子後完成繼承。</li>
<li>在建構子之內的變數和函數視為private,設定給this的視為public。</li>
<li>protected method並沒有實質上的支援,撰寫規則是使用底線「_」。</li>
<li>使用在this中覆寫屬性,而也會保留原先的property來重複使用。</li>
</ul>
<h3>Comparison with pseudo-classical pattern</h3>
<ul>
<li>在All-in-one Constructor Pattern無法使用 <code>rabbit instanceof Animal</code> ,因為沒有做設定prototype的動作。</li>
<li>速度較慢且需要較多記憶體。由於每一個物件都擁有自己的method,而非使用prototype共享。</li>
<li>實作private method和property,是較為安全和快速的。</li>
</ul>
<hr />
<h4>References</h4>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/all-one-constructor-pattern">All-in-one constructor pattern | JavaScript Tutorial</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/all-in-one-constructor-pattern.md">JavaScript Object Oriented Programming - All-in-one Constructor Pattern 筆記:持續更新中...</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/07/all-in-one-constructor-pattern/" target="_blank" title="JavaScript Object Oriented Programming: All-in-one Constructor Pattern">JavaScript Object Oriented Programming: All-in-one Constructor Pattern</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-86949920503080091912016-02-06T15:30:00.004+08:002017-08-08T10:18:51.998+08:00JavaScript Object Oriented Programming - Pseudo-Classical Pattern<!DOCTYPE html>
<html>
<head>
<title>JavaScript Object Oriented Programming - Pseudo-Classical Pattern</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img alt="JavaScript Object Oriented Programming - Pseudo-Classical Pattern" src="https://lh3.googleusercontent.com/CjvaIb7vt6Uwr7fLZfHJBAMoTMGVX4pW5ORc5dJwPsJNVGt09baXrKjYj49upglcAInGmoNx94gYoxaW4w7nXQFTWpxd9_h7ZiLr4y2FRarr4DqansiGJC6IZc1hjqqDgZvrr68LMvz-8ljH06r0xVe7XpoSDjtmQAzGgTK-X4hvUaETn7PyXVWBPtd5lDJMhkLTLq9AbdjrGTVOWlt9LdzVuRtT4M0TC5yXvSfNRQKtpYAdNkJ_STlkTCbY6t93Uh4EH6AhRAeTVipWQCqO65TWOjcTcrCmufk1qRzMMcrxzU0GKif8nYrvURN8Wzt1B_O8YEQZAbzZ_YIgQMSV4bBxwauCIhNACAzkZ5h3gmE59XtxKrVGx10YGdv576C_4vvWg1ZIaYBU8XeOgmvvrVEpumSLIHDmmK3-qpcoFvfHR92O7U1KbFhxLgAeWhLVCPOHDaxMuyGHgrxvG2oL5vqG9c8v-aeeqb-1zUEqPS4K5iBFlv13yH1MwX1Z_hSh2fq2mEIREiowmQR64uvEFk3xe9t8X8ZA689Y2X9pE4aG-VtQ4koXpcflri9B-X5vcUiW=w800-h494-no">
<h2>Pseudo-class declaration</h2>
<p>在pseudo-classical pattern中,物件是由「建構子」(constructor)這個函式所建立,並把method放到建構子的prototype中。</p>
<a name='more'></a>
<pre><code>function Animal(name){
this.name = name;
};
Animal.prototype = {
canWalk: true,
sit: function(){
this.canWalk = false;
console.log(this.name + ' sits down.');
}
};
var animal = new Animal('Pet'); // (1)
console.log(animal.canWalk); // true
animal.sit(); // (2)
console.log(animal.canWalk); //false
</code></pre>
<ol>
<li>當「new Animal(name)」被呼叫的時候,新物件收到參考到Animal.prototype的 <code>__proto__</code>。</li>
<li><code>animal.sit</code> 改變 <code>animal.canWalk</code> 的值,所以這個animal物件的canWalk為false,但其餘的仍為true。</li>
</ol>
<h3>備註</h3>
<ul>
<li>method和預設的屬性會放在prototype中。</li>
<li>prototype中的method,若使用this,指的是目前的物件。所以animal.sit()指的是這一個animal。</li>
</ul>
<h2>Inheritance</h2>
<p>使用 <code>Rabbit.prototype.__proto__ == Animal.prototype;</code> 來讓Rabbit繼承Animal。我們使用<a target="_blank" href="http://javascript.info/tutorial/inheritance#inherit">inherit</a>這個function來做設定。</p>
<pre><code>function inherit(proto) {
function F() {};
F.prototype = proto;
return new F;
};
function Animal(name){
this.name = name;
};
Animal.prototype = {
canWalk: true,
sit: function(){
this.canWalk = false;
console.log(this.name + ' sits down.');
}
};
function Rabbit(name){
this.name = name;
};
Rabbit.prototype = inherit(Animal.prototype);
Rabbit.prototype.jump = function(){
this.canWalk = true;
console.log(this.name + ' jumps!');
};
var rabbit = new Rabbit('John');
console.log(rabbit.canWalk); //true
rabbit.sit(); //John sits down.
console.log(rabbit.canWalk); //false
rabbit.jump(); //John jumps!
</code></pre>
<h2>Calling superclass constructor</h2>
<p>「superclass」這個建構子不是被自動呼叫的,我們使用「apply」將Animal這個函式套用到目前的物件上。這樣就能將Animal建構子的內文狀態設給目前使用的物件。</p>
<pre><code>function Rabbit(name) {
Animal.apply(this, arguments);
};
</code></pre>
<h2>Overriding a method (polymorphism)</h2>
<p>當method被覆寫,我們仍可能會希望能呼叫之前尚未被覆寫的method。</p>
<h3>Calling a parent method after overriding</h3>
<p>我們可以直接呼叫parent prototype。</p>
<pre><code>function inherit(proto) {
function F() {};
F.prototype = proto;
return new F;
};
function Animal(name){
this.name = name;
};
Animal.prototype = {
canWalk: true,
sit: function(){
this.canWalk = false;
console.log(this.name + ' sits down.');
}
};
function Rabbit(name){
this.name = name;
};
Rabbit.prototype = inherit(Animal.prototype);
var rabbit = new Rabbit('John');
Rabbit.prototype.sit = function() {
console.log(this.name + ' sits in a rabbity way.');
}
rabbit.sit(); //使用覆寫後的method
Rabbit.prototype.sit = function() {
console.log('calling superclass sit:');
Animal.prototype.sit.apply(this, arguments); //使用尚未被覆寫的method
}
rabbit.sit();
</code></pre>
<p>備註:All parent methods are called with apply/call to pass current object as this. A simple call Animal.prototype.sit() would use Animal.prototype as this. </p>
<p>我們也可以直接在rabbit這個物件上做覆寫的動作。</p>
<pre><code>rabbit.sit = function() {
alert('A special sit of this very rabbit ' + this.name);
}
</code></pre>
<h3>Sugar: removing direct reference to parent</h3>
<p>因為在重構的時候可能修改了parent,所以必須移除對parent constructor的參考。</p>
<pre><code>function extend(Child, Parent){
Child.prototype = inherit(Parent.prototype);
Child.prototype.constructor = Child;
Child.parent = Parent.prototype;
};
function inherit(proto) {
function F() {};
F.prototype = proto;
return new F;
};
function Animal(name){
this.name = name;
};
Animal.prototype = {
canWalk: true,
sit: function(){
this.canWalk = false;
console.log(this.name + ' sits down.');
},
run: function(){
this.canWalk = false;
console.log(this.name + ' run fast.');
}
};
function Rabbit(name) {
this.name = name;
Rabbit.parent.constructor.apply(this, arguments); //super constructor
};
extend(Rabbit, Animal);
Rabbit.prototype.run = function() {
Rabbit.parent.run.apply(this, arguments); //parent method
};
var rabbit = new Rabbit('John');
console.log(rabbit.canWalk); //true
rabbit.sit(); //John sits down.
console.log(rabbit.canWalk); //false
rabbit.run(); //John run fast.
</code></pre>
<p>因此,之後對Animal的修改都只需要回頭修正Animal和extend即可。</p>
<h2>Private/protected methods (encapsulation)</h2>
<p>JavaScript中的private/protected method並沒有實質上的支援,可參考<a target="_blank" href="http://javascript.info/tutorial/pseudo-classical-pattern#private-protected-methods-encapsulation">Private/protected methods (encapsulation)</a>。</p>
<h2>Static methods and properties</h2>
<p>static property/method是被直接設定給建構子的。</p>
<pre><code>function Animal() {
Animal.count++;
};
Animal.count = 0;
new Animal();
new Animal();
console.log(Animal.count); //2
</code></pre>
<h2>Summary</h2>
<p>總結以上,我們得到完整的Pseudo-Classical Pattern程式碼。</p>
<pre><code>function extend(Child, Parent){
Child.prototype = inherit(Parent.prototype);
Child.prototype.constructor = Child;
Child.parent = Parent.prototype;
};
function inherit(proto) {
function F() {};
F.prototype = proto;
return new F;
};
// --------- the base object ------------
function Animal(name) {
this.name = name;
};
// methods
Animal.prototype.run = function() {
console.log(this + " is running!");
};
Animal.prototype.toString = function() {
return this.name;
};
// --------- the child object -----------
function Rabbit(name) {
Rabbit.parent.constructor.apply(this, arguments);
};
// inherit
extend(Rabbit, Animal);
// override
Rabbit.prototype.run = function() {
Rabbit.parent.run.apply(this);
console.log(this + " bounces high into the sky!");
}
var rabbit = new Rabbit('Jumper');
rabbit.run();
</code></pre>
<hr />
<h3>References</h3>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/pseudo-classical-pattern">Pseudo-classical pattern</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">Function.prototype.apply()</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/pseudo-classical-pattern/pseudo-classical-pattern.md">Pseudo-Classical Pattern 筆記:持續更新中...</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/06/javascript-pseudo-classical-pattern/" target="_blank" title="JavaScript Object Oriented Programming: Pseudo-Classical Pattern">JavaScript Object Oriented Programming: Pseudo-Classical Pattern</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-12781907222936810222016-02-05T09:41:00.003+08:002017-08-08T10:06:44.677+08:00JavaScript Object Oriented Programming - The "instanceof" operator<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript Object Oriented Programming - The "instanceof" operator</title>
<style>
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<img src="https://lh3.googleusercontent.com/tuxYLSpDzjBApTcdoozhahbddIBMzMbaYRDLwJxBU4dgDbDVkMkKXKHkhTbq5-8InExR1kmf-DZku_M_UOqocuCYFV37YnqTojA8THivH22oqc3TzKjWcIFWJ3uavFuS45jO2WT83ZXel9Y_t019o0htlOuDvV_g1kAcNOSZEbOW-TULCNxwVtkK5fK08bETLuoZV0-o9Z9s8af0Nd9QrVK__CxAmzEu095OfFsoizIRSPFo8_SRIDfw9HBMlxpL5RNKs7Uz3xhE3n5jG5Y2wrSdV-LfEtNI4n8yzJl9Q6kqWH-p9KBjvQ--xBuKeA6cKzNYNLn-KXvZ-zLdikKfzpLVpFkuSqIDPPVERc067vOe_BmgYCSIr2aQ6vtsAAL7GZJmg33anyEv0PLPLkCJLQl8nFUaau6kK56JNi7opOhCoNhVCRTcbVQkR95laM-zlbWXa4NF2VI7zO1pY2wn7JKC-tL75TWAarfS8hrOY0ICfhe5zvUfuyzpNHVRPO0zKxqIEECsJqqmNbpX2vHleDOWZF9dM_Wy8jKN1NqJm-VFIgI2cRaqe4rTw7ehkdlHYbll=w800-h494-no" alt="JavaScript Object Oriented Programming - The instanceof operator">
<h2>The instanceof operator</h2>
<p><code>instanceof</code> 允許檢查物件是否為給定的constructor所產生的。</p>
<pre><code>function Rabbit(){};
var rabbit = new Rabbit();
console.log(rabbit instanceof Rabbit); //ture
</code></pre>
<p>rabbit的constructor的確是Rabbit。</p>
<a name='more'></a>
<h2>When instanceof lies</h2>
<p>當值是來自另外一個frame或iframe,<code>instanceof</code> 這個operator所取得的值是不準確的。例如,一個來自另外一個iframe的array。每一個frame或iframe有自己的window物件和其繼承關係。解法可參考<a href="http://javascript.info/tutorial/type-detection#class-to-differ-between-native-objects">[[Class]] to differ between native objects</a>。</p>
<h2>Summary</h2>
<ul>
<li>
<p><code>instanceof</code> 允許檢查物件是否為給定的constructor所產生的。檢查方式是走訪整個 <code>__proto__</code> 鍊,因此繼承是成立的。</p>
<pre><code> var arr = []
alert(arr instanceof Array) // true
alert(arr instanceof Object) // true
</code></pre>
</li>
<li>
<p>當值是來自另外一個frame或iframe,<code>instanceof</code> 這個operator所取得的值是不準確的。例如,一個來自另外一個iframe的array。每一個frame或iframe有自己的window物件和其繼承關係。解法可參考<a href="http://javascript.info/tutorial/type-detection#class-to-differ-between-native-objects">[[Class]] to differ between native objects</a>。</p>
</li>
</ul>
<hr>
<h4>Reference</h4>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/instanceof">The “instanceof” operator</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/instanceof.md">JavaScript筆記:The "instanceof" operator,持續更新中...</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/05/instanceof/" target="_blank" title="JavaScript Object Oriented Programming: The instanceof operator">JavaScript Object Oriented Programming: The instanceof operator</a>。
<hr>
</body></html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-2251152087419131422016-02-04T09:33:00.002+08:002017-08-08T09:59:28.329+08:00JavaScript Object Oriented Programming - The "constructor" property<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>constructor</title>
<style>
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body id="preview">
<img src="https://lh3.googleusercontent.com/fNXnjWMsbWz5l-u-9IFX1-hE8fEqp2M9kb17ZGqyfhxIJT7SOdpj6fACV0jXshaA_I4J0f42ojl2NoIL3_OYc8xdZEryoZ-6tiAOEuwRwYFQdg1fFd9mCCP1adwK2N_Jof9Tpj1H4Q-H6nzTFZ77lxwzWqv9QfWAcLhFTDxs0VkuFDXxCgEkdpnzG70ZDJhZTcN8ZOWDBIBdJ9JTIHIaK8PXIygMH8UyiidqZtvOm-r76pTzBA3kJgGXtrzk2XfNMTn9YCjF8ZJUqy7ZY9hGzKrMwWY5KlFO4r1--uhqJR8NSLjyLAddsde0PzrM4eMwPHp0QkEMQYb_hDtqu4O2w3k8HIhNPkCdXkpz5lzwyZwFAEOy2TpM3BKf_QNbDa1MhpNz-KBw5eL-yBknRoQZa8LnsLsgDAT2g32LegKkFxMnLoeYcR3yntY7u-Yq1sJedjPv5rlsOq_khjCB2ZAjbO9uEordiXruufPl-CZgts9oZP_hOXDPvvNklaEjc1sMZs2YF97R1rsOZXZRny0OQhTgqMg4AdhGJILVXmXHtLCVGn1p24a_b4BAXA_36pu1SDUR=w800-h494-no" alt="JavaScript Object Oriented Programming - The constructor property">
<p>物件有內建的屬性「constructor」,意即「參考建立此物件的function」。</p>
<h2><a target="_blank" id="_2"></a>範例</h2>
<p>我們來建立一個簡單的constructor,然後看看新的物件是否有正確的constructor的值。</p>
<pre><code>function Rabbit(){};
var rabbit = new Rabbit();
console.log(rabbit.constructor == Rabbit); //true
</code></pre>
<a name='more'></a>
<h2><a target="_blank" id="The_origins_of_constructor_9"></a>The origins of constructor</h2>
<p>當我們宣告一個函數,如下:</p>
<pre><code>function Rabbit() {
//...
};
</code></pre>
<p>interpreter建立一個新的函數物件,而此物件的prototype屬性也被建立和給值。prototype的預設值與其constructor相同,在此範例中為Rabbit。意即:</p>
<pre><code>Rabbit.prototype = { constructor: Rabbit };
</code></pre>
<p>所以,當由constructor練立一個新的物件時(例如:使用Rabbit建立rabbit),<code>Rabbit.prototype</code>成為這個被建立物件的<code>__proto__</code>的值,且constructor成為物件可被存取的屬性。如下所示:</p>
<pre><code>rabbit.__proto__ == { constructor: Rabbit };
</code></pre>
<p>我們來做一個簡單的檢查吧!</p>
<pre><code>function Rabbit(){};
var rabbit = new Rabbit();
console.log(rabbit.hasOwnProperty('constructor')); //false
console.log(rabbit.__proto__.hasOwnProperty('constructor')); //true
console.log(Rabbit.prototype.hasOwnProperty('constructor')); //true
</code></pre>
<h2><a target="_blank" id="Keeping_constructor_up_to_date_33"></a>Keeping constructor up to date</h2>
<p>呼叫 <code>rabbit.constructor</code> 事實上是回傳 <code>rabbit.__proto__.constructor</code> 。這會有一個副作用,參考以下範例。</p>
<pre><code>function Rabbit(){}; //(1)
Rabbit.prototype = {}; //(2)
var rabbit = new Rabbit();
console.log(rabbit.constructor == Object); //true
</code></pre>
<p>原先在(1)rabbit的constructor是Rabbit,但由於在(2)中將 Rabbit.prototype 的值設為空物件,因此 <code>rabbit.__proto__</code> 在 <code>Rabbit.prototype</code> 找不到 <code>constructor</code> 這個屬性的值,只好繼續往上找,最後在 Object.prototype 找到 constructor 的值為 Object。</p>
<h2><a target="_blank" id="Summary_44"></a>Summary</h2>
<ul>
<li>
<p>物件有內建的屬性「constructor」,意即參考「建立此物件的function」。</p>
</li>
<li>
<p>當prototype被修改,其constructor也會丟失。</p>
</li>
<li>
<p>如果我們希望在繼承後擁有正確的constructor,就必須自行設定,如下:</p>
<pre><code> function Rabbit(){};
Rabbit.prototype = { constructor: Rabbit }
var rabbit = new Rabbit();
console.log(rabbit.constructor == Rabbit); //true
</code></pre>
</li>
</ul>
<hr>
<h4><a target="_blank" id="_55"></a>參考資料</h4>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/constructor">The “constructor” property</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/constructor.md">JavaScript Object Oriented Programming - The "constructor" property 筆記,持續更新中...</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/04/constructor/" target="_blank" title="JavaScript Object Oriented Programming: The constructor property">JavaScript Object Oriented Programming: The constructor property</a>。
<hr>
</body></html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-39563716935812612172016-02-03T17:10:00.001+08:002017-08-07T23:18:17.957+08:00JavaScript Object Oriented Programming - Extending Natives<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Object Oriented Programming - Extending Natives</title>
<style>
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body id="preview">
<img src="https://lh3.googleusercontent.com/RujpuXt9ix4fmlzfEceEWAqjryA8rlAMidf9fc87F5NrJXEujxWiJ6CQxj-8Qco0u94VmMGTyo5sqKk09XXALu5bYaNvJRNnQS7O33yxt-f9JY41IzQ-oquIy7AEMbC-al0TTvwkZkm19pOTgM61pdMIZoOJS9wFqd7koyTRJtA-AQIdQKJXkUdo1kI45Lo0AxVG-4Q3sppsgFW_XdVw6WssXcGrvsKwyb-KEF-t1_PqZvqZE61nrHanvXXAVjAZ7l5PcT27-qpJzh1fmFhK2WkNGWezfuyrlVcqDFW--eWzgCaDq6j4ZR_wBCVSpKlQR_-2Wa9c1RhYU0cTYHmGFho7CQqU8zwYYoo5putyLknMlP9VfOXKY7axuH4NS0pYsgMG_s4gcYzp9jSQOVqdRiCslNsgze0DXKN15jxkw7D1B9oKN9Iaenzj8Xx-Y1vI-Lx7r8yq9B--e1nDVVYbp0b5gGkTzsHBnU0vUHRV5YDYtjeCf42YQO8X0tqiJTWCjEzh1fzAggDBPpwhQnv1HDhHF3xBlPS6lcaTy6yeaWkwlW7dS16xXKcSQHOPTdXH6eOM=w800-h494-no" alt="Object Oriented Programming - Extending Natives">
<p>Native JavaScript物件將method存在prototype中。例如:當一個新的物件被建立,內容為空,但為何可以使用toString這個method?(參考下面的程式碼)</p>
<pre><code>var obj = { };
console.log( obj.toString() );
</code></pre>
<p>這是因為 <code>var obj = {};</code> ( 即 <code>var obj = new Object();</code>)。而Object是Native JavaScript的建構式,obj由Object所產生,因此 <code>obj.__proto__ == Object.prototype;</code>。所以,所有在Object.prototype中的屬性,obj皆可使用(包含<code>Object.prototype.toString</code>)。這就是為什麼obj可以使用 toString 這個method了。對於Array、Function和其他物件也是相同的道理,而它們的method即在Array.prototype、Function.prototype等。</p>
<a name='more'></a>
<p>當物件的屬性被存取時,interpreter會依照下列的順序找尋屬性的值:</p>
<ul>
<li>物件本身</li>
<li>物件的 <code>__proto__</code></li>
<li>物件的 <code>__proto__</code> 的 <code>__proto__</code></li>
</ul>
<p>interpreter會尋找這個值直到找到或下一個 <code>__proto__</code> 的值為null。Object.prototype是唯一一個物件的 <code>__proto__</code> 值為null,因此一定會終止在Object.prototype。換句話說,所有的物件皆繼承自Object。</p>
<p>當基礎型別使用method會隱性的轉為物件,而其結果也會是基礎型別。例如:宣告一個變數i等於數字5,並使用toString這個method (參考下面的程式碼)。</p>
<pre><code>var i = 5;
console.log(i.toString()); //5
</code></pre>
<h2><a target="_blank" id="_Prototypes_21"></a>修改原生的 Prototypes</h2>
<p>Native Prototypes可以被修改,也可以新增新的method到prototype中。例如,我們可以新增一個新的method「each」,將物件的屬性列印出來。</p>
<pre><code>Object.prototype.each = function(f){
for(var prop in this) {
if(Object.prototype.hasOwnProperty(prop)) continue; //跳過後面的指令,進行下一次迴圈的執行。在此用於過濾protoytpe的屬性
var value = this[prop];
f.call(value, prop, value);
}
};
var obj = { name: 'John', age: 25};
obj.each(function(prop, val){
console.log(prop); //name -> age
});
</code></pre>
<p>修改內建的prototype是不好的,但如果是要將ECMA-262 5th的method實作進舊瀏覽器是可以的。例如,假設舊瀏覽器沒有 <code>Object.create</code>這個method,那我們就新增它。</p>
<pre><code>if(!Object.create){
Object.create = function(proto){
function F(){};
F.prototype = proto;
return new F;
}
}
</code></pre>
<h2><a target="_blank" id="_47"></a>繼承原生物件</h2>
<p>原生物件(Native Objects)是可以被繼承的。例如:<code>Array.prototype</code>保留了所有的method給新的Array的實例(instance)。假設我們希望能在myArray當中使用Array.prototype的method,那麼就將Array.prototype的值設定給<code>myArray.__proto__</code>即可。</p>
<pre><code>function MyArray(){
//將陣列中的每個元素用逗點合併,使其成為一字串
this.stringify = function(){
return this.join(', ');
};
};
MyArray.prototype = Array.prototype;
var myArr = new MyArray();
myArr.push('hello');
myArr.push('jack');
console.log(myArr.stringify()); //"hello, jack"
console.log(myArr); //["hello", "jack"]
console.log(myArr.length); //2
</code></pre>
<h2><a target="_blank" id="_Method_66"></a>借用 Method</h2>
<p>如果只是想要使用Array的部份功能,其實不需要繼承它 - 借用它的method即可。</p>
<pre><code>var join = Array.prototype.join,
join = [].join;
var obj = {
0: 'first',
1: 'second',
length: 2
};
console.log([].join.call(obj, ', ')); //first, second
</code></pre>
<p>Array的method常被借用來處理類似Array的物件。</p>
<h2><a target="_blank" id="Summary_82"></a>Summary</h2>
<ul>
<li>Native JavaScript物件將method存在prototype中。</li>
<li>Native prototypes可以被繼承或擴充。</li>
<li>修改頂端的Object.prototype會破壞for…in loops,因此不建議這麼做。但可實作一些先進的method而讓舊瀏覽器支援這些好用的方法,例如:Object.create、Object.keys、 Function.prototype.bind等。</li>
</ul>
<hr>
<h4><a target="_blank" id="References_88"></a>References</h4>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/native-prototypes">Extending Natives</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/extending-natives.md">JavaScript Object Oriented Programming - Extending Natives 筆記:持續更新中...</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/02/03/extending-natives/" target="_blank" title="JavaScript Object Oriented Programming: Extending Natives">JavaScript Object Oriented Programming: Extending Natives</a>。
<hr>
</body></html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-91467729026218519722016-01-31T15:51:00.000+08:002017-08-07T23:12:09.221+08:00JavaScript Object Oriented Programming - Prototypal Inheritance<!DOCTYPE html>
<html>
<head>
<title>prototypal-inheritance</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p>
<img alt="Object Oriented Programming - Prototypal Inheritance" src="https://lh3.googleusercontent.com/_n319troA3bf_CcTXfo93-RDxXeOLH767RrNffTpuTTpd8QHJpUzJaJijtQLnk1PKyKNDjFNmH6-9hTwNJnmkxaScSRUXv0SKaO9zP3Ciq3SyrSSikfEUpBn0YQ_5wIYyVfsyzG_e-NYGcLe1-3f_hry6FeC2QZHUD69gp42mC0S9aW-GkE-aApsx1f-59zku15LWLYE3vbWcC982lW8GBASGWftnZS6gqwptXRKmXXT0jN9pcBIoS4Oz9Sj1w4xRrJtp-A_3YRRhNeKGa-__pjDHvFtmvddRNKbFKZjJFssX1-gYBwtJlPoZzTQlbaqw-x-19g5PhSOF_VzdKLjHCjqoIRsRcYnD-yEQIrdc0xTOi_5jWAZ--HQ_vo7h_j6-eCo8EUGwP226hTig5sbkBgzBo-1hV86HLJEKQts0qSyLJqcGWsVnEx8H4sJHm8PEBTWcBcZloNKnucWwSBwBlYsXjkkCFCv149_epC9sdXh9uYg_c8FAQPNP-yopkXIjNCf_BxjYEBNW2SapHrUIW_AYOGo85Mtan4NAH7A1AJSgSUK0AX4UM6VWQG_BhjMnMzC=w800-h494-no">
</p>
<p>對大多數的語言來說,它們擁有「Class」和「Object」,而Class繼承其它的Class。對於JavaScript來說,繼承是使用prototype來實作的,意即沒有Class,而是由物件繼承其它的物件來達成繼承。</p>
<a name='more'></a>
<h2>Inheritance, the <strong>proto</strong></h2>
<pre><code>var animal = { eats: true },
rabbit = { jumps: true };
rabbit.__proto__ = animal; //inherit
console.log(rabbit.eats); //true
</code></pre>
<p>當存取rabbit的property「eats」時,由於無法在rabbit中找到,因此往父物件尋找,終於在animal找到,並且值是true。</p>
<p>如果存取rabbit的property可在自身找到,就不需要往父物件尋找。
例如,假設存取rabbit的property「jumps」,由於可在rabbit找到,並且值是true因此就不往父物件找了。</p>
<p>「 <code>__proto__</code>」是一個連接子物件和父物件的link,讓interpreter在子物件找不到屬性時,可以依循這條link往父物件繼續尋找。</p>
<p>由以上的範例可知,我們可以在animal放置一個method,然後子物件rabbit也可以使用這個method。</p>
<pre><code>var animal = {
eat: function(){
console.log("I'm full");
this.full = true;
}
},
rabbit = {
jump: function(){
//do something
}
};
rabbit.__proto__ = animal;
rabbit.eat(); //I'm full
</code></pre>
<p>rabbit.eat() 這個method經由兩個步驟被執行:</p>
<ul>
<li>Step 1:Interpreter在rabbit物件中尋找eat() method,但沒有找到,因此循著<code>rabbit.__proto__</code>往父物件animal尋找,並且找到了。</li>
<li>Step 2:此時的eat() method中的this是指rabbit物件。</li>
</ul>
<p>我們得到以下結論:</p>
<ul>
<li>物件呼叫其父物件的method,其中的this會被設定給這個物件。這就是繼承的意思。</li>
<li>對於物件來說,<code>__proto__</code>被稱為prototype(原型)。因此,animal是rabbit的prototype。</li>
</ul>
<h2>Object.create, Object.getPrototypeOf</h2>
<p>「<code>__proto__</code>」並非標準的屬性,只有Chrome和Firefox支援,其它瀏覽器則是隱藏此屬性於內部。所有目前流行的瀏覽器(除了Opera,IE則是版本9以上),對此屬性支援兩個標準method - Object.create和Object.getPrototypeOf。
</p>
<h3>Object.create(proto[, props])</h3>
<p>使用animal <code>__proto__</code> 建立一個空物件rabbit,而且可在rabbit中建立自己的屬性jumps。</p>
<pre><code>var animal = { eats: true },
rabbit = Object.create(animal);
rabbit.jumps = true;
console.log(rabbit.eats); //true
console.log(rabbit.jumps); //true
</code></pre>
<h3>Object.getPrototypeOf(obj)</h3>
<p>回傳 <code>obj.__proto__</code> 的值。這個method是在標準規格裡面的,所以不支援 <code>__proto__</code> 的瀏覽器仍可使用此method。</p>
<pre><code>var animal = { eats: true },
rabbit = Object.create(animal);
console.log(Object.getPrototypeOf(rabbit) === animal); //true
</code></pre>
<p>因此,幾乎所有主要瀏覽器都允許「讀取」<code>__proto__</code>的值,但不允許修改它。</p>
<h2>The prototype</h2>
<p>有一個較好且跨瀏覽器的方法來設定 <code>__proto__</code>,而這個方法需要建構式的幫忙來設定 <code>__proto__</code> 的值。</p>
<pre><code>var animal = { eats: true };
function Rabbit(name){
this.name = name;
};
Rabbit.prototype = animal; //set __proto__ = animal for all objects created by new Rabbit
var rabbit = new Rabbit('John');
console.log(rabbit.eats); //true
</code></pre>
<h2>Crossbrowser Object.create(proto)</h2>
<p>Object.create(proto)允許直接繼承給予的物件,可以使用我們自己撰寫的method - inherit 來模仿它而達到一樣的功能。因此使用 inherit(animal)的效果等同於使用 Object.create(animal) - 建立一個新的空物件,且 <code>object.__proto__ = animal</code> 。</p>
<pre><code>function inherit(proto){
function F(){}; //(1)
F.prototype = proto; //(2)
return new F; //(3)
};
var animal = { eats: true },
rabbit = inherit(animal);
console.log(rabbit.eats);
</code></pre>
<p>解說如下:</p>
<ol>
<li>建立新函式 F,新函式 F由於沒有被設定任何值,因此F會建立一個空的物件。</li>
<li>F.prototype被設定值為proto。</li>
<li>回傳由F建立的空物件。空物件的prototype為proto,意即 <code>object.__proto__ = proto</code> 。</li>
</ol>
<h2>hasOwnProperty</h2>
<p>hasOwnProperty method 允許檢查其屬性是否屬於某個物件或其prototype。</p>
<pre><code>function Rabbit(name){
this.name = name;
};
Rabbit.prototype = { eats: true };
var rabbit = new Rabbit('John');
console.log(rabbit.hasOwnProperty('eats')); //false, in prototype
console.log(rabbit.hasOwnProperty('name')); //true, in object
</code></pre>
<h2>Looping with/without inherited properties</h2>
<p>將所有的屬性利用for loop顯示出來。</p>
<pre><code>function Rabbit(name) {
this.name = name;
};
Rabbit.prototype = { eats: true };
var rabbit = new Rabbit('John');
for(var p in rabbit) {
console.log (p + " = " + rabbit[p]); // outputs both "name" and "eats"
}
</code></pre>
<p>利用method hasOwnProperty來篩選只有物件本身擁有的屬性,而非在prototype中。</p>
<pre><code>function Rabbit(name) {
this.name = name;
};
Rabbit.prototype = { eats: true };
var rabbit = new Rabbit('John');
for(var p in rabbit) {
if (!rabbit.hasOwnProperty(p)) continue; // filter out "eats"
console.log (p + " = " + rabbit[p]); // outputs only "name"
}
</code></pre>
<h2>Summary</h2>
<ul>
<li>
使用屬性 <code>__proto__</code> 來實作繼承
<ul>
<li>如果在物件中找不到此屬性,則會沿著 <code>__proto__</code> 到父物件尋找此屬性的值。</li>
<li>this的值會被設定給物件,而非prototype。</li>
<li>屬性設定:obj.prop = val;屬性刪除:delete obj.prop。 </li>
</ul>
</li>
<li>
管理 <code>__proto__</code>
<ul>
<li>Firefox/Chrome可以直接存取 <code>__proto__</code> ,而大多數現行的瀏覽器只提供讀取(利用Object.getPrototypeOf(obj))而沒有修改的權限。</li>
<li>給予特定prototype的空物件可使用Object.create(proto)來達到繼承的目的,或使用自行實作的function。</li>
</ul>
</li>
<li>
其它method
<ul>
<li>for..in loop 可列出所有此object自己和prototype的屬性。</li>
<li>obj.hasOwnProperty(prop) 在屬性屬於此物件本身(而非屬於prototype)時會回傳true。</li>
</ul>
</li>
</ul>
<hr />
<h4>參考資料</h4>
<ul>
<li><a target="_blank" href="http://javascript.info/tutorial/inheritance">Prototypal inheritance | JavaScript Tutorial</a></li>
<li><a target="_blank" href="http://cythilya.blogspot.tw/2015/06/javascript-code-reuse-patterns.html">JavaScript - Code Reuse Patterns</a></li>
<li><a target="_blank" href="https://github.com/cythilya/javascript-info/blob/master/object-oriented-programming/prototypal-inheritance.md">JavaScript Object Oriented Programming - Prototypal Inheritance 筆記,持續更新中...</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2016/01/31/prototypal-inheritance/" target="_blank" title="JavaScript Object Oriented Programming: Prototypal Inheritance">JavaScript Object Oriented Programming: Prototypal Inheritance</a>。
<hr>
</script>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0tag:blogger.com,1999:blog-16352433.post-59739207650952832762015-12-27T20:18:00.000+08:002017-08-07T23:07:43.806+08:00Plugin的撰寫<!DOCTYPE html>
<html>
<head>
<title>Plugin的撰寫</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0}body{font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:14px;line-height:1.6;color:#333;background-color:#fff;padding:20px;max-width:960px;margin:0 auto}body>:first-child{margin-top:0!important}body>:last-child{margin-bottom:0!important}p,blockquote,ul,ol,dl,table,pre{margin:15px 0}h1,h2,h3,h4,h5,h6{margin:20px 0 10px;padding:0;font-weight:700;-webkit-font-smoothing:antialiased}h1 tt,h1 code,h2 tt,h2 code,h3 tt,h3 code,h4 tt,h4 code,h5 tt,h5 code,h6 tt,h6 code{font-size:inherit}h1{font-size:28px;color:#000}h2{font-size:24px;border-bottom:1px solid #ccc;color:#000}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{color:#777;font-size:14px}body>h2:first-child,body>h1:first-child,body>h1:first-child+h2,body>h3:first-child,body>h4:first-child,body>h5:first-child,body>h6:first-child{margin-top:0;padding-top:0}a:first-child h1,a:first-child h2,a:first-child h3,a:first-child h4,a:first-child h5,a:first-child h6{margin-top:0;padding-top:0}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:10px}a{color:#4183C4;text-decoration:none}a:hover{text-decoration:underline}ul,ol{padding-left:30px}ul li > :first-child,ol li > :first-child,ul li ul:first-of-type,ol li ol:first-of-type,ul li ol:first-of-type,ol li ul:first-of-type{margin-top:0}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}dl{padding:0}dl dt{font-size:14px;font-weight:700;font-style:italic;padding:0;margin:15px 0 5px}dl dt:first-child{padding:0}dl dt>:first-child{margin-top:0}dl dt>:last-child{margin-bottom:0}dl dd{margin:0 0 15px;padding:0 15px}dl dd>:first-child{margin-top:0}dl dd>:last-child{margin-bottom:0}pre,code,tt{font-size:12px;font-family:Consolas,"Liberation Mono",Courier,monospace}code,tt{margin:0;padding:0;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px}pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent}pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px}pre code,pre tt{background-color:transparent;border:none}kbd{-moz-border-bottom-colors:none;-moz-border-left-colors:none;-moz-border-right-colors:none;-moz-border-top-colors:none;background-color:#DDD;background-image:linear-gradient(#F1F1F1,#DDD);background-repeat:repeat-x;border-color:#DDD #CCC #CCC #DDD;border-image:none;border-radius:2px 2px 2px 2px;border-style:solid;border-width:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:10px;padding:1px 4px}blockquote{border-left:4px solid #DDD;padding:0 15px;color:#777}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{clear:both;margin:15px 0;height:0;overflow:hidden;border:none;background:transparent;border-bottom:4px solid #ddd;padding:0}table th{font-weight:700}table th,table td{border:1px solid #ccc;padding:6px 13px}table tr{border-top:1px solid #ccc;background-color:#fff}table tr:nth-child(2n){background-color:#f8f8f8}img{max-width:100%}
</style>
</head>
<body>
<p><img src="https://goo.gl/9AcUYO" alt="Plugin的撰寫" /> </p>
<p>又重新看了<a target="_blank" href="http://www.amazon.com/jQuery-Novice-Ninja-Earle-Castledine/dp/0987153013">jQuery: Novice to Ninja</a>,所以做了些關於Plugin的筆記。以下使用<a target="_blank" href="https://github.com/cythilya/jquery-newsticker">jQuery Newsticker Plugin</a>作為說明範例。</p>
<a name='more'></a>
<h2>Step 1:使用閉包</h2>
<p>避免汙染全域變數,關於閉包可參考這篇<a target="_blank" href="http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html">JavaScript Scoping and Hoisting</a>,清楚說明Scope、Hoisting、Function的相關觀念。</p>
<pre><code>(function($){
...
})(jQuery);
</code></pre>
<h2>Step 2:決定使用者可以傳入的參數和使用的method</h2>
<p>以<a target="_blank" href="https://github.com/cythilya/jquery-newsticker">jQuery Newsticker Plugin</a>為例,我們允許使用者根據需求調整(意即覆寫預設值)height、speed、start、interval和move。其中move是開放讓使用者客製化的function,如果使用者不滿意目前newsticker內容由下往上移動方式,則可自行撰寫,其預設值是null。</p>
<pre><code>var config = $.extend({}, {
height: 30,
speed: 800,
start: 8,
interval: 3000,
move: null
}, opts);
</code></pre>
<h2>Step 3:撰寫內部程式</h2>
<p>接下來便是撰寫plugin的功能。</p>
<h3>功能分割</h3>
<p>功能的切割以未來好維護為原則。<p>
<p>這個newsticker會有幾個功能:</p>
<ul>
<li>初始化:初始設定,例如:設定目前要展示的項目、其他function的初始化。</li>
<li>移動:newsticker內容由下往上移動。</li>
<li>暫停:當滑鼠移到內容上方時,newsticker會停止移動,讓使用者順利閱讀;滑鼠移開再繼續移動。</li>
</ul>
<p>因此,程式區塊就看到了三個主要的function,後續便針對這三個function補滿應有的內容。</p>
<pre><code>function init(){
//初始化
...
};
function suspend(){
//暫停
...
};
function move(){
//移動
....
};
</code></pre>
<h3>執行使用者定義的method</h3>
<p>由於我們開放<code>config.move</code>讓使用者客製newsticker的移動方式,因此必須先判斷使用者是否輸入自己寫好的function,如果沒有,則執行預設的移動方式。這裡使用一個小技巧 <code>$.isFunction</code> 來幫忙做判斷。</p>
<pre><code>if($.isFunction(config.move)){
config.move.call(this);
}
else{
//執行預設的移動方式
}
</code></pre>
<h2>Step 4:初始化這個Plugin</h2>
<pre><code>// initialize every element
this.each(function() {
init($(this));
});
return this;
</code></pre>
<h2>Step 5:使用這個Plugin</h2>
<p>在這裡也可以代入要覆寫的參數,例如:speed。</p>
<pre><code>$(function() {
$('.newsticker').newsticker({
speed: 1000
});
});
</code></pre>
<h2>除了撰寫Plugin,還有其他擴充方法嗎?</h2>
<h3>擴充為jQuery的核心</h3>
<p>如果是常用的功能,我們也可以使用<code>jQuery.fn.extend()</code> 或 <code>$.fn.extend()</code> 擴充為jQuery的核心,當成一般jQuery method使用。假設我們擴充prompt這個method來做提示功用。</p>
<p>$.fn.extend({
prompt: function() {
return this.each(function() {
console.log('this is a prompt.');
});
}
});</p>
<p>這樣就可以使用了...前後都可以加上其他method使之成為chain。</p>
<pre><code>obj.show().prompt().hide();
</code></pre>
<h3>擴充原有的method,使其有新的功能</h3>
<p>擴充$.trim,使其具有把字串中所有空白都移除的功能。我們將原本的$.trim存到變數_trim中,假設在使用method時傳入「clear為true」則使用擴充的用法 - 將字串中所有空白的移除。</p>
<pre><code>(function($) {
var _trim = $.trim;
$.extend({
trim: function(text, clear) {
if (clear) {
return text.replace(/\s+/g, '');
}
return _trim.call(this, text);
}
});
})(jQuery);
</code></pre>
<p>傳入不同參數有不同的結果...</p>
<h4>直接prompt原始字串</h4>
<pre><code>console.log(' aa aa a '); // aa aa a
</code></pre>
<h4>原本的功能,只去除前後空白</h4>
<pre><code>console.log($.trim(' aa aa a ')); //aa aa a
</code></pre>
<h4>擴充的功能,將字串中所有空白的移除</h4>
<pre><code>console.log($.trim(' bb bb b ', true)); //bbbbb
</code></pre>
<hr />
<p>之後我也會陸續把以前寫過的plugin release出來啦~</p>
<hr />
<h2>Reference</h2>
<ul>
<li><a target="_blank" href="http://www.amazon.com/jQuery-Novice-Ninja-Earle-Castledine/dp/0987153013">jQuery: Novice to Ninja</a></li>
<li><a target="_blank" href="http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html">JavaScript Scoping and Hoisting</a></li>
<li><a target="_blank" href="https://github.com/cythilya/jquery-newsticker">jQuery Newsticker Plugin</a></li>
</ul>
<hr>
由於部落格搬家了,因此在新落格也放了一份,未來若有增刪會在這裡更新-<a href="https://cythilya.github.io/2015/12/27/plugin/" target="_blank" title="Plugin 的撰寫">Plugin 的撰寫</a>。
<hr>
</body>
</html>summerhttp://www.blogger.com/profile/16169683351140092085noreply@blogger.com0