2016年11月7日 星期一

sass 重點

名詞解釋:
SASS (Syntactically Awesome Stylesheets)
SCSS (sassy CSS)



1、檔名開頭以"_"前綴,讓sass知道這是要被import的檔案,先不要compile,import後再一次compile

scss主檔(裡面會import其他scss檔案)本身命名不需要加"_"

在其中import其他scss時可省略被import的scss檔案的檔名的"_"

例如:

main.scss 是主檔

_variables.scss 是要被引入的scss檔


在主檔中,import語法如下

@import "variables" 

2016年10月10日 星期一

學習心得1

人的精神層面的各種能力是有限量的

耐心、思維力、理解力等等皆同

因為大腦連續接收處理相同性質的資訊是會疲乏的,應該均衡利用各部位

理解這個道理,妥善安排並充分利用各種能力,就能進步的很快

例如連續閱讀需要消化的程式相關知識,其實每天都有一個額度,超過了就事倍功半

比較以下兩種分配兩個工作天的方式:

a方式:
連續一整天閱讀react相關學習資源
連續一整天畫設計圖

b方式:
半天閱讀、半天畫圖
半天閱讀、半天畫圖

我認為b方式是比較高效率的

2016年10月8日 星期六

lifecycle methods 重點

1、lifecycle methods(下稱lm)是指在元件的生命周期中的特定時間點被呼叫的methods

你可以寫一個lm在一個元件第1次render之前呼叫


你可以寫一個lm在一個元件每次render之後呼叫,除了第1次render


你可以在元件的生命周期中的諸多不同時機點執行lm


2、lm有3種類別:

mounting、updating、unmounting

mounting monent:


元件在它首次render時掛載(mounts),這也是mounting類別的lm執行的時機點


mounting lm有3種:

componentWillMount//在render之前執行(html元素載入之前)
render(載入html元素)
componentDidMount//在render之後執行(html元素載入之後)

當元件掛載時依序執行這3個lm

除了render(同時屬於updating類別),另外兩個只在首次render時執行 


componentDidMount(下稱cdm)在render之後執行,如果你用ajax來取得初始資料,則cdm很適合用來執行這個工作,它也適合用來連結外部應用,例如web api或是js framework,也很適合用它來設定timers,使用setTimeout或setInterval


updating moment:


元件在首次render時沒有update,元件在每次render時update,除了第1次,每個元件每次update都會自動執行它的updating lm


updating lm有5種依執行先後順序排列如下:


componetWillRecieveProps(下稱cwrp)


shouldComponentUpdate(scu)


componentWillUpdate(cwu)


render


componentDidUpdate(cdu)


cwrp只有在元件有接收props的情況下會被呼叫,時機點如下例(引用codecademy範例)所示

// componentWillReceiveProps will get called here:

ReactDOM.render(
  <Example prop="myVal" />,
  document.getElementById('app')
);

// componentWillReceiveProps will NOT get called here:

ReactDOM.render(
  <Example />,
  document.getElementById('app')

);


cwrp自動接收一個nextProps參數,這個參數是一個物件,內容和即將傳入的props相同,一般用來事前比對,符合條件才render該prop或是做其他處理


scu在cwrp之後執行,自動接收兩個參數,nextProps, nextState,並回傳true或false,若回傳true則正常update,但若回傳的是false,則不會執行後續的3個updating lm,包含render 。通常用來比對nextProps和this.props以及nextState,thisState,依據結果回傳true or false,判斷是否update


範例: https://codepen.io/anon/pen/wqPxYX?editors=1011


cwu在scu之後執行,同樣自動接收兩個參數nextProps以及nextState,你不能在cwu中呼叫setState

cwu通常用來和react結構之外的物件互動,如果你需要在元件render之前做一些非react的設定,例如檢查window size或是和外部api互動,那麼cwu是一個適合的地方


render觸發ui重渲染,需注意的是即使未從parent component接收任何props,當parent re-render時其所有children component都會觸發render,而有時因為html架構需求,即使不需要從parent接收props,仍會有成為其child的需要,因此有時會需要使用scu判斷是否需要re-render以避免parent更新時child進行不必要的re-render



cdu在rendered html完成載入後執行,並自動接收prevProps, prevState兩個參數,它們參照到update之前的元件props和state,可以用來和"現況"的props還有state比較

cdu通常和cwu一樣是用來和react環境之外的物件互動,例如browser和api,差別只在它是render之後


unmounting moment:


一個元件的unmounting期間發生在元件從dom中移除時,這可以發生在dom rerender,但未包含該元件時,或是使用者導向到其他網頁,或是關閉瀏覽器時


unmounting moment只有一個lm:


componentWillUnmount(cwun)


cwun在元件從dom移除之前被呼叫,因此如果即將被移除的元件曾經設定需要被清理的方法(例如用setInterval設定的timer),那麼該元件的cwun就是適合你清理這些方法的時機和地方



2016年10月7日 星期五

form 重點

1、controlled component(下稱cc) and uncontrolled component(下稱ucc):

cc與ucc是描述元素是否在react控制之下

例如一般情況下 input元素維護自己的狀態,使用者輸入什麼,它就顯示什麼,我們可以透過value屬性在任何時間點向它取值

但當一個input元素是透過react render出來的,並且有賦於它value屬性,而我們又希望能在使用者keyin內容時掌控state並在特定元素上顯示,所以我們會設定一個eventhandler去監控input內容,並呼叫setState方法改變狀態,setState方法會呼叫render方法更新畫面,這時因為我們有賦於value屬性值,在重新render時等同重置內容,此時這個input便不在是自我控制,而是受react控制,所以稱之為cc,cc與ucc是對於react而言

如果我們希望cc的input元素之value值能依使用者輸入而改變,只要將value屬性值與元件的state狀態連結即可,render方法的return內容會是像下列這樣

<input type="text" onChange={handleUserInput} value={this.state.userInput} />

handleUserInput方法中會呼叫setState方法更新元件的userInput狀態

jsx input元素的value值則和userInput狀態連結

如此一來,便將一個input元素轉換為受控的react input元素

或是換個方式說,在react環境下當我們想要設定有value屬性的元素(例如input)的value初始值,同時希望能用setState方法記錄狀態,又希望能正常反映使用者的輸入內容時(否則會在setState呼叫render方法時重置為初始值,無法正常反映輸入內容),就必須這樣操作,像是把它納入react控制,所以稱之為cc,有value屬性的元素不多,有value屬性又有上述需求的元件就更少,所以大部份的元件都是ucc類型

不想轉換為cc但又想設定初始值的方式:
用以下列屬性替代value屬性設定始初值
defaultValue (for input, select元素)
defaultChecked (for checkbox, radio)


2、form的用途是讓使用者輸入資料,一般方式是使用form元素搭配submit元素,在點擊submit時一次性傳送整個form元素裡面的資料

但是在react有很不一樣的處理方式,它是在使用者輸入每一個字元時都傳輸資料(有變化的部份),所以你可以不需要一個submit按鈕,甚至也不需要一個form元素,僅僅只需要一個input元素即可讓使用者提交資 料

當然你還是會使用form元素和submit按鍵,例如在表單輸入過程中即時比對輸入內容與server端資料並做相應的處理,然後使用者按下送出的動作做為一種確認方式,區分"進行中"與"已完成",但在某些情況下,僅僅只需要簡單的input元素即可滿足需求







2016年10月4日 星期二

state 重點

1、和props不同,state不是由外部傳入,然後props一開始就存在,state則需要定義getInitialState方法並傳回一個物件,此物件會成為state物件,未初始化前讀取state是null

2、和props相同,可以在元件class說明物件(instructions)的任何屬性中使用

3、使用setState方法改變state屬性值,可局部改變,未被改變的部份保持原樣

現況
{
    hungry:false,
    mood:'great'
}

改變部份state
this.setState({
  hungry:true
});

改變後

{
   hungry:true,
   mood:'great'//維持原樣
}

4、不能在元件的render function中直接呼叫setState方法,原因如第5點

5、每次呼叫this.setState這個方法改變狀態值後,它都會自動呼叫render方法,以更新畫面

因此如果在render方法裡面直接呼叫它,它又會馬上呼叫render,render又呼叫它…會變成無窮迴圈

6、重置state的方法
在constructor 儲存一個重置用的備份
this.state = {
   a: 1,

   b: 2
};


this.baseState = this.state;

重置時:
this.setState(this.baseState);

這個方式能作用的原因為每次setState,react都是建立一個新的物件讓this.state參照

所以this.baseState和this.state在使用過setState之後就不是參照到到同一個物件了

7、傳入function到this.setState
https://codepen.io/anon/pen/dzMebJ
這個例子的差別在於雖然都是連續呼叫兩次this.setState
但BadCounter是整合後以後來的state做為結果做render,因其參考的this.state.count未變,所以兩次的結果相同,只+1。

GoodCounter以函數接受prevState,兩次呼叫間更新了state的值,但最後才做1次render。


props

1、props:在components(以下稱元件)之間傳遞的資訊

每個元件都有props,它是一個物件,用來存放和元件相關的資訊


傳遞資訊給元件的方式:


文字形態的資訊


<MyComponent anyNameYouLike="你好!" />


非文字形態的資訊都要用'{}'括起來,包含數字也是


<MyComponent info1="你好" info2={['a','b','c']} info3={2} info4={true} />


傳遞進去的資訊會存放在該元件的props物件中


亦可傳遞函數,通常傳遞做為event handler的函數

在react中,函數會定義在元件class的說明物件中

只能在render方法裡面的return內容中傳遞props給其它component instance

對於定義函數與傳遞函數給其他元件的那個元件本身,對於事件監聽函數以及傳遞的prop的名稱是有一個命名慣例存在的,不一定要遵守,但瞭解規則是有好處的


handler的名稱通常是handle+事件名稱,例如handleClick


prop的名稱通常是on+事件名稱,例如onClick


這有點容易搞混,因為jsx元素有兩個類別,一是html-like,一是component instance


在html-like元素中屬性名稱on+事件名稱(首字大寫,如onClick)是用來綁定事件監聽器


但在component instance中屬性名稱on+事件名稱只是一個通常用來傳遞事件監聽器的props屬性名稱


換句話說on+事件名稱(首字大寫)在component instance類別的jsx元素中並沒有特殊功能,並不會有綁定監聽器的動作,它就只是一個隨意的名稱


區分jsx兩種元素類別的方式很簡單,只要記住component instance標籤字首是大寫


component instance通常表示成self-closing標籤,但也可以寫成一般有開頭和結尾的形式,同樣有效,例如<MyComponent></MyComponet>,寫成開頭與結尾的方式有個好處,每個元件的props都有一個children屬性,this.props.children代表在component instance的開頭與結尾標籤中所有元素


<MyCom>

   this is a text.
</MyCom>
this.props.children等於"this is a text."

<MyCom>
<MyCom2 />
</MyCom>
this.props.children等於<MyCom2 />這個元件

要注意當開頭與結尾之間有超過一個以上的元素,則children是array

但當之間只有一個元素的時候,children就是該元素,而不會用array包裝 

被包含在component instance的開頭和結束標籤中的元素不會被render


設定props預設值的方法:


在說明書文件中定義"getDefaultProps"方法,return一個物件,該物件內容會加到props裡面,當有從外部傳遞props屬性時,會以外部傳入的值取代預設值,否則套用預設值

var Button = React.createClass({
  getDefaultProps:function(){
    return {text:'I am a button'};
  },
  render: function () {
    return (
      <button>
        {this.props.text}
      </button>
    );
  }
});

ReactDOM.render(
  <Button />,
  document.getElementById('app')
);

未傳入text prop,所以使用預設值"I am a button"


2、propTypes,props驗證與文件化

用法如下(參考codecademy):
在元件定義物件中新增一個propTypes屬性,值為物件,當一個元件預期接收props,則在此物件中加入對應prop名稱的key,值則需符合"React.PropTypes.指定的形態" 這種格式,其代表接收的指定prop需符合該形態,否則在console會有警告訊息

加上.isRequired表示是必要傳入值,若未傳入,則一樣會在console有警告訊息

一般會將propTypes定義在最上方,可以幫助檢閱這個元件的用途並加上說明

var Runner = React.createClass({
  propTypes: {
    message:   React.PropTypes.string.isRequired,
    style:     React.PropTypes.object.isRequired,
    isMetric:  React.PropTypes.bool.isRequired,
    miles:     React.PropTypes.number.isRequired,
    milesToKM: React.PropTypes.func.isRequired,
    races:     React.PropTypes.array.isRequired
//只有bool和func是簡寫表示,其餘形態都是完整寫法
  },

  render: function () {
  var miles = this.props.miles;
    var km = this.props.milesToKM(miles);
    var races = this.props.races.map(function(race, i){
      return <li key={race + i}>{race}</li>;
    });

    return (
      <div style={this.props.style}>
        <h1>{this.props.message}</h1>
        { this.props.isMetric && 
          <h2>One Time I Ran {km} Kilometers!</h2> }
        { !this.props.isMetric && 
          <h2>One Time I Ran {miles} Miles!</h2> }
        <h3>Races I've Run</h3>
        <ul id="races">{races}</ul>
      </div>
    );
  }

});


react.js 重點

1、引入其他js檔案方式為require,當require內的字串以'.' or '/' 開始時,表示給的是路徑

const Class = require('./Car.js');

若未給定副檔名,預設為".js"
所以也可以寫成這樣

const Class = require('./Car');


2、props與state都是component(元件)用來儲存動態資訊的物件,差異在於:
props儲存的是可由其他元件(除了自己)改變的資訊
state儲存的是可以由元件自身自己改變的資訊


3、在react.js v0.4以後,"this"關鍵字使用在定義component class的instruction物件中的function定義時,自動綁定為該元件,而非在function被呼叫時依owner改變

var MyComponent = React.createClass({
    functionYouDefined: function(){
         console.log(this) //此this綁定為MyComponent instance
    }
});

換個方式說,當this使用在物件的定義上時,行為就是綁定至該物件,使用在一般function時則是依function被呼叫時,其owner為何


4、react 編程模式1(programming pattern):

基本架構:

1個有狀態的父元件負責儲存資訊
1個無狀態的子元件負責顯示狀態內容
1個無狀態的子元件負責更新父元件的狀態
資訊狀態統一由父元件向下傳遞

流程如下:

1、一個有狀態(有getInitialState方法)的a元件定義一個有參數的方法,其內呼叫setState方法使用參數更新a元件的state

2、a元件傳遞這個更新state的方法給另一個無狀態的b元件

3、b元件定義一個可接收事件物件的方法,其內呼叫由a傳入的更新方法,並把事件物件的屬性傳遞進去,以更新a元件的state

4、b元件將可接收事件物件的方法做為事件handler

5、當特定事件觸發,a元件的state更新(例如從下拉式選單選擇一個值)

6、a元件傳遞其更新後state給另一個無狀態元件c

7、c元件接收a元件的state並將值嵌入jsx元素

8、渲染(render)a元件,c元件顯示state值,b元件顯示一個可供改變state值的途徑,例如下拉式選單


5、在react中不同檔案是互相獨立的,除了你require進來的部份,因此你可以有100個檔案,都有一個global變數叫style,它們也可以不互相衝突


6、react 編程模式2:

基本架構:

從表現元件(presentational)中分離容器元件(container)

分離表現邏輯商業邏輯(類似view和controller的概念)

當一個元件必須擁有狀態或是根據傳入的props進行計算,或是其他任何複雜的邏輯處理,那麼它不該再負責render html-like的jsx元素,而是應該render另一個元件,由那個元件負責render html-like的jsx元素的工作

表現元件唯一的工作就是render html-like的jsx元素,如上述,容器元件會render表現元件,再由表現元件render html-like的jsx元素

有狀態的容器元件負責邏輯處理,將處理結果傳遞給無狀態的表現元件負責顯示工作

7、控制state的元件所在階層要適當,太低與太高皆不宜:

太低無法完整控制,會有難以傳遞給平行元件的問題

太高會有不必要的update情況,父元件update(setState時)代表所有子元件都會update(雖然dom不一定會更新,但檢查是會的),當包含的內容太多時,update太頻繁會導致效能低下,反之子元件update時,上層元件並不會update,所以像是控制input內容這種工作,儘量讓底層元件控制狀態






2016年10月2日 星期日

components 重點

1、components 是小型、可重用、負責單一任務的的程式碼塊,通常包含渲染html的功能

2、components 由component class 產生,呼叫 React.createClass() 方法建立一個新的component class,class名稱開頭必須是大寫,這是通用規則,component class不是component,它比較像是一個專門生產component的工廠

const React = require('react');

const MyComponentClass = React.createClass();

createClass方法接受一個參數,此參數必須是一個js物件,這個物件像是一份說明書,告訴component class如何去生產component

這個參數物件通常會有一個render function,這個render function必須要有return陳述式,並通常會是return一個jsx陳述式

建立一個component class
const MyComponentClass = React.createClass({
  render: function () {
    return <h1>Hello world</h1>;
  }
});

使用component class產生components 的方式透過jsx語法,jsx元素可以是html tag或是component instance,js利用名稱開頭大小寫的差異分辨兩者,這就是為何component class名稱必須開頭必須是大寫的原因

使用class產生一個component的方式如下
<MyComponentClass />

每個component instance都會繼承其parent class的"說明書"中的所有屬性和方法,某component class說明書中有render方法傳回一個jsx元素,則所有該class的component instance都有render方法傳回定義好的jsx元素

要呼叫instance的render方法只要把instance丟給DOM 的render方法做為首個參數即可,DOM的render方法會告訴component instance去呼叫它的render方法,然後DOM的render方法會接收其傳回的jsx元素,並將之渲染在畫面上


將component做為DOM render方法的第一個參數傳入

ReactDOM.render(
<MyComponentClass />,
document.getElementById('app')
);

畫面上將會出現h1大小的Hello world


3、stateless functional component:

一個表現型元件通常只有一個render function,沒有其他了,這種類型的元件可以用一種特殊寫法,像寫一般js的function一樣的方式,如下:

// 元件一般定義方式
var MyComponentClass = React.createClass({
  render: function(){
    return <h1>Hello world</h1>;
  }
});

// 同樣的元件,使用無狀態功能型元件寫法:
function MyComponentClass () {
  return <h1>Hello world</h1>;
}

// 不同寫法,一樣的運作結果:
ReactDOM.render(
<MyComponentClass />,
document.getElementById('app')

);

無狀態功能型元件寫法(以下稱sfc寫法)接收props的方式是定義一個參數,習慣上取名為props,這個參數對應到props物件,傳入的prop自動對應到props參數物件的屬性與值

// 一般方式顯示props屬性值:
var MyComponentClass = React.createClass({
  render: function () {
    return <h1>{this.props.title}</h1>;
  }
});

// 無狀態功能型元件顯示props屬性值的方式:
function MyComponentClass (props) {
  return <h1>{props.title}</h1>;
}

// 一般方式使用變數顯示props屬性值的方式:
var MyComponentClass = React.createClass({
  render: function () {
  var title = this.props.title;
    return <h1>{title}</h1>;
  }
});

// 無狀態功能型元件使用變數顯示props屬性值的方式:
function MyComponentClass (props) {
var title = props.title;
  return <h1>{title}</h1>;

}

無狀態的功能型元件寫法除了簡潔,也稍稍的禪示了元件就像function,輸入可選的state與props參數,輸入html或其他元件

為sfc寫法加入propTypes的方法:

function Example (props) {
  return <h1>{props.message}</h1>;
}

Example.propTypes = {
  message: React.PropTypes.string.isRequired
};



2016年9月29日 星期四

jsx 重點


1、jsx 是html like的js語法擴充, 很像html,只是寫在js file,透過complier轉成一般js


2、jsx語法可以在任何js語法可以出現的地方出現,可以存入變數,可以傳遞給函數…



3、jsx 只能有一個最外層元素,以html比喻就是最外層標籤不能有其他sibling,這很好達成,有需要的時候只要在最外層包上一層div即可



4、當jsx語法超過一行時,需用中括號 ( ) 包起來



5、雖然jsx和html語法幾乎一樣,但還是有不同的地方:


class:

在jsx裡面要用className而不能用class,這是因為jsx會轉譯為js,而class是js的保留關鍵字

當jsx render時className就會自動轉為class屬性


self-closing element:

例如<img />, <input />這類標籤,在html "/" 反斜線可加可不加,但在jsx則是必須加,否則會出現錯誤

event listener attribute:

html語法掛載事件handler的屬性名稱是全小寫,例如onclick、onmouseover,而在jsx則是使用camelCase,例如onClick、onMouseOver,然後指定function時記得用 { } 而不是 "" ,因為是jsx語法

lists:

當一個jsx標籤中包含多個li元素時,在某些情況下,需使用jsx專有的key屬性給予唯一的值以確保排序順序的正確性,否則可能產生預期之外的排序結果,需使用key的情況如下:
1、當lists元素需記憶狀態時,例如是否有打勾
2、當lists元素順序可能被打亂時,例如搜尋結果可能是隨機排序的,而你希望他保有一致性

如果不確定是否該使用key,那就都用吧,無傷大雅

語法如下:
<ul>
  <li key="li-01">Example1</li>
  <li key="li-02">Example2</li>
  <li key="li-03">Example3</li>

</ul>



6、在jsx標籤中的js語法會被視為文字,想要在jsx標籤中注入js語法,只要用大括號 { } 將js語法包起來即可,但有一個限制是不能注入(inject) if條件式到jsx語法中,可在外部處理if判斷,或是改用三元條件式或是自觸發函數、或是&&運算

原因及範例請參考: FB官方文件 、 &&運算範例

當注入js語法在jsx標籤中,注入的js和其他js是在同一個環境中,意思是在jsx外宣告的變數,可以在jsx裡面存取



7、你可以不使用jsx,jsx實際上會轉譯為js


var h1 = <h1>Hello world</h1>;


會轉譯為以下的js語法


var h1 = React.createElement(

  "h1",
  null,
  "Hello, world"

);


所以如果你想,你可以不使用jsx語法



8、在jsx中inline style寫法為:


<div style={{ color:'purple', width: '300px'}}></div>


在jsx中設定inline style屬性的方式是傳遞一個js物件做為設定依據

當你在jsx中注入js,而且注入的內容僅僅只是一個物件表示式,那很自然的就會是以兩個大括號結束


外層大括號代表注入js,內層大括號則是js的物件定義符號,所以兩個大括號並非表示什麼特殊語法,只是容易讓人誤以為有特殊意義


如果你是另外定義一個物件再傳入,就會像下列這樣


const styles = {

    color: 'purple',
    width: '300px'
};

<div style={styles}></div>


所以要記得的是在jsx中設定inline style屬性的方式是傳遞一個js物件做為設定依據



9、在正規js中,style屬性名稱都是小寫,並用"-"連接,但要注入jsx中的style屬性物件,屬性名稱是用camelCase方式定義,如下例:

一般js的style設定物件屬性名稱語法:
{

    background-color: 'purple',
    width: '100px'
}

要注入jsx inline style的物件屬性名稱語法:
{

    backgroundColor: 'purple',
    width: '100px'

}

10、在正規js中,style屬性值都是以文字表示,而在jsx 的style定義物件中,屬性值若是以數字表示,則預設單位是"px",但一樣可以用一般js的方式表示,jsx只是提供一個方便的處理方式,如下例:

一般js:
{
     font-size: '30px',
     height: '100vh'
}


jsx inline style 屬性值:
{
     fontSize: 30,
     height: '100vh'

}


2016年9月6日 星期二

何謂sql injcetion 以及 xss 跨站攻擊

sql injection 還有 xss 都是利用網站對使用者輸入的資料未針對特殊字元做處理的漏洞,注入惡意指令在sql查詢字串或是html語法上

sql injection

假設登入驗證使用者輸入的帳號密碼直接查詢資料庫,有查到就通過

登入驗證是以下這一串sql指令
 SELECT * FROM `memberdata` WHERE m_username = '使用者輸入的帳號' and m_passwd ='使用者輸入的密碼';

假設資料庫中有一筆資料帳密都是'admin'

下面這段是正常登入,使用者輸入帳號:admin , 密碼: admin

1、SELECT * FROM `memberdata` WHERE m_username = 'admin' and m_passwd ='admin';

但改成下面這樣也可以登入, 或說也可以查到一筆admin的資料

2、SELECT * FROM `memberdata` WHERE m_username = 'admin' # and m_passwd ='';

只要在帳號處輸入 admin'#    不用密碼即可登入

可以把1、2的指令在phpMyAdmin上run看看,都能查到一筆admin資料

因為 #字號 把後面的 and m_passwd = .......註解掉了,那段等於沒有,所以條件變成只查帳號符合的

xss 攻擊

然後 如果你在帳號欄位輸入  <script> alert('我不是文字,是會執行的js碼'); </script>
然後在php檔案裡面 echo $_POST['userName'];  它不會印出文字,而是執行js程式碼
(實際上不會執行,因為內建有預防,可以按f12看訊息)

現在假設有一個網站是可以回文的,回文會被存入資料庫,每個使用者連到該頁面都會把歷史回文列出來

假設某個使用者在回文裡面輸入 <script> alert('this is demo.');</script>,那其他人拜訪該頁面時,就會執行該程式碼,看到彈出視窗顯示 'this is demo.' 訊息,<script>標籤裡面如果改成惡意程式碼,那就…
利用使用者信任該網站,透過該網站攻擊該網站的使用者,跨站攻擊的函意似乎是這樣


預防方式都是對使用者輸入的資料做特殊符號處理,像是' , " , <, > 等等的

例如:

sql injection 用 addslashes 函數 http://php.net/manual/en/function.addslashes.php

xss 用 htmlentities 函數 http://php.net/manual/en/function.htmlentities.php

2016年9月2日 星期五

PHP 面試考題 輸出費氏數列與質數

//費氏
//==========================
$pre1 = 1;
$pre2 = 0;

function fib($pre2, $pre1, $n)
{
    if ($n < 2) echo $n;
    else {
        $cur = $pre1 + $pre2;
        echo $cur;
        $pre2 = $pre1;
        $pre1 = $cur;
    }
    if ($n < 10000) {
        echo ',';
        fib($pre2, $pre1, ++$n);
    }
}

fib($pre2, $pre1, 0);

//質數
//============================

echo "1";
echo ",2";
for($i=1; $i<=100; $i+=2){

    for($j=2; $j<$i ;$j++){
        if($i%$j ==0) break;
    }
    if($j==$i) echo ",$i";
}

2016年8月1日 星期一

傳統硬碟的硬碟重整是否有效益?

最近在看youtube上哈佛的cs50課程,裡面講到了傳統硬碟的運作方式,這讓我對硬碟重整是否有效益產生好奇心,稍微思考了一下,也把概念寫成這篇文章做記錄

這個問題的效益評量指標可以從硬碟壽命和時間成本兩個面向探討,為簡化問題,我們把傳統硬碟壽命簡單以讀取頭移動距離來代表,並以單一文件的讀取為例子

假設一個硬碟壽命是可以移動100km的距離
當一個文件儲存的位置分散的時候,假設每讀取一次文件讀取頭移頭至各個位置取得數據的總移動距離是1m,花費0.1秒

接下來做了一次硬碟重整,把該文件各部份數據移動到相近的區域,這個動作花費了讀取頭500m的移動距離完成,以及2小時的時間(通常在不影響使用的時候作業,故不列入考慮)

重整後每次讀取該文件的總移動距離減少為0.6m,花費0.07秒(時間除了移動,還有讀取的動作以及所在位置處於外圈或內圈等的其他因素,所以不會是等比例,此處先不討論)

那麼這樣是有效益的嗎?

a.從硬碟壽命的角度來看:

重整花了500m的距離,每次讀取減少了0.4m
500m / 0.4m = 1250,當對這個文件的讀取超過1250次,節省的移動就超過了重整的移動成本,那麼重整就是有效益的,反之則否

b.從時間成本的角度來看:

每次讀取減少了0.3秒,假設個人時間價值是500 nt / hour,每秒價值0.139nt,每次讀取節省了0.0417nt時間成本,假設一顆硬碟價格是2500nt,壽命100km,等當40m / 1nt,500m /40m = 12.5nt , 12.5nt / 0.0417nt = 299.8,當對這個文件的讀取超過299.8次,就是有效益,反之則否

c.因為重整同時縮短移動距離與讀取時間,所以應該綜合考量

重整後每次移動節省 0.4m + 1.67m(由0.0417nt換算距離) = 2.07m

500m / 2.07m = 241.55,只要重整後讀取該文件有超過241.55次就是有效益

結論:

這個問題沒有絕對的答案,會依情況而有不同,真的要有一個統整性的參考答案,可能就是要從統計的角度衡量,若能有足夠多的樣本數據可以統計,我們就可以依統計數據評估整體而言硬碟重整普遍是正效益或是負效益

以上只是將問題簡單化探討這個問題,現實運作有很多難以量化的變因,這篇文章想表達的是要有對事物的本質的好奇心以及研究的精神,那麼就能從錯誤中學習並持續進步。

程式效率優化範例

以下是一個針對使用者輸入內容做驗證的function,分別有兩個寫法
我們來比較一下差異

======================================
寫法一:相對較差的方式

function checkForm() {
    if(document.formJoin.m_username.value ==""){
        alert("請填寫帳號!");
        document.formJoin.m_username.focus();
        return false;
    }else{
        uid=document.formJoin.m_username.value;
        if(uid.length<5 || uid.length>12){
            alert("您的帳號長度只能5至12字元");
            return false;
        }
        if(!(uid.charAt(0)>='a'  && uid.charAt(0)<='z')){
            alert("帳號第一個字元只能是小寫英文");
            return false;
        }
        for(idx=0; idx<uid.length;idx++){
            if(uid.charAt(idx)>='A' && uid.charAt(idx)<='Z'){
                alert("帳號不可有大寫字元");
                document.formJoin.m_username.focus();
                return false;
            }
            if(!((uid.charAt(idx) >='a'  && uid.charAt(idx)<='z')|| (uid.charAt(idx) >='0'  && uid.charAt(idx)<='9') || uid.charAt(idx)=="_")){
                alert("帳號只能英文或數字及_!");
                document.formJoin.m_username.focus();
                return false;
            }
            if(uid.charAt(idx)=="_" && uid.charAt(idx-1)=="_"){
                alert("符號不能相連");
                document.formJoin.m_username.focus();
                return false;
            }
        }
    }
    if(!(check_passwd(document.formJoin.m_passwd.value,document.formJoin.m_passwdrecheck.value))){
        document.formJoin.m_passwd.focus();
        return false;
    }
}

function check_passwd(pw1,pw2) {
    if(pw1==""){
        alert("密碼不可為空白");
        document.formJoin.m_passwd.focus();
        return false;
    }
    for(var idx=0;idx<pw1.length;idx++){
        if(pw1.charAt(idx)==" " || pw1.charAt(idx)=="\""){
            alert("密碼不可有空白或雙引號\!n");
            document.formJoin.m_passwd.focus();
            return false;
        }
        if(pw1.length<5 || pw1.length>10){
            alert("密碼介於5到10個字元!\n");
            document.formJoin.m_passwd.focus();
            return false;
        }
        if(pw1!=pw2){
            alert("密碼兩次輸入不一樣");
            document.formJoin.m_passwd.focus();
            return false;
        }
    }
    return true;
}


======================================
寫法二:比較好的方式,和寫法一的差異以註解說明

function checkForm() {
    //將查詢結果儲存到變數中,避免多餘的查詢
    var inpM_username = document.formJoin.m_username;
    if (inpM_username.value == '') {
        alert('請輸入帳號!');
        inpM_username.focus();
        return false;
    } else {
        //將id字串長度計算結果儲存到變數中,避免重複計算,尤其是在for迴圈判斷結束點時
        var uid = inpM_username.value,
            uidLength = uid.length;
        if (uidLength < 5 || uidLength > 12) {
            alert('帳號長度需介於5~12個字元');
            inpM_username.focus();
            return false;
        } else {
            //字元的比較會自動轉為ascii碼比大小
            //以local變數做為for迴圈的累加運算變數,而不是global變數(未使用var或let宣告的變數就是global變數)
            for (var i = 0; i < uidLength; i++) {
                //將取得特定索引位置字元的結果儲存到變數,避免重複運算
                var uidIndexWord = uid.charAt(i);
                if (uidIndexWord >= 'A' && uidIndexWord <= 'Z') {
                    alert('帳號不可以含有大寫字元');
                    inpM_username.focus();
                    return false;
                }
                if (!((uidIndexWord >= 'a' && uidIndexWord <= 'z') || (uidIndexWord >= '0' && uidIndexWord <= '9') || uidIndexWord == '_')) {
                    alert('帳號只能數字,英文字母及「_」');
                    inpM_username.focus();
                    return false;
                }
                if (uidIndexWord == "_" && uid.charAt(i - 1) == "_") {
                    alert('「_」符號不可以相連');
                    inpM_username.focus();
                    return false;
                }
            }
        }
    }
    //將查詢結果儲存到變數中,避免多餘的查詢
    var inpM_passwd = document.formJoin.m_passwd,
        pw1 = inpM_passwd.value,
        inpM_passwdrecheck = document.formJoin.m_passwdrecheck,
        pw2 = inpM_passwdrecheck.value;
    if (!checkPassword(pw1, pw2)) {
        inpM_username.focus();
        return false;
    }
}
function checkPassword(pw1, pw2) {
    //以下順序代表著效率的優化

    //先簡單確認是否為空白
    if (pw1 == '') {
        alert('密碼不可以空白');
        return false;
    }
    //先儲存長度再執行for迴圈避免重複計算長度
    var pwLength = pw1.length;
    //既然已取得長度,就先比長度是否符合限制,因為速度快,且寫在for外面也是避免無意義的重複
    if (pwLength < 5 || pwLength > 12) {
        alert('密碼需介於5~12個字元');
        return false;
    }
    //再來執行for迴圈 word by word比對,只要一個不符合即return結束,也是很快
    for (var i = 0; i < pwLength; i++) {
        var pwIndexWord = pw1.charAt(i);
        if (pwIndexWord == ' ' || pwIndexWord == '\"') {
            alert('密碼不可以包含空格或雙引號');
            return false;
        }
    }
    //最後才是字串的比對,寫在for外面避免無意義的重複比對
    if (pw1 != pw2) {
        alert('密碼確認不一致,請修正');
        return false;
    }
    return true;
}

寫法二不一定是最好的方式,也一定有更好的寫法,但這篇文章想表達的是養成思考是否有更佳的實現方式的習慣,就會一直進步這個觀念。


2016年7月29日 星期五

到數計時效果


//方式1:用jquery過場動畫

var jqDivContainer = $('#divContainer');

function countdown(time){
    jqDivContainer.prepend('<h1 id="h1Countdown"></h1>').css('position', 'relative');
    var jqH1Countdown = $('#h1Countdown');
    jqH1Countdown.css({
        color:'white',
        'line-height': '300px',
        width: '100%',
        'text-align': 'center',
        'margin-top': 0,
        'font-size': 64,
        position: 'absolute',
    });

function repeat(time){
        jqH1Countdown.text(time);
       
        jqH1Countdown.fadeIn(500,'swing',function(){
            jqH1Countdown.fadeOut(500,'swing',function(){
                time--;
                if (time > 0) repeat(time);
                else {
                    jqH1Countdown.remove();
                    jqDivContainer.css('position', 'static');
                }
            });
        });
    }

    repeat(time);

}

countdown(10);

點我看demo

//方式2:用css transition

var jqDivContainer = $('#divContainer');

function countdown(time){
    jqDivContainer.prepend('<h1 id="h1Countdown"></h1>').css('position', 'relative');
    var jqH1Countdown = $('#h1Countdown');
    jqH1Countdown.text(time).toggleClass('show');
    function repeat(time) {
        setTimeout(function(){
            var isShowing = jqH1Countdown.hasClass('show');
            if(!isShowing) time--;
            if(time > 0){
                if(!isShowing) jqH1Countdown.text(time);
                jqH1Countdown.toggleClass('show');
                repeat(time);
            }else {
                jqH1Countdown.remove();
                jqDivDrawingJumbo.css('position', 'static');
            }
        },500);
    }

    repeat(time);


}

countdown(10);

點我看demo

2016年7月22日 星期五

asp web MVC memo

App_Start資料夾裡的RouteConfig.cs 設定起始controller,並指定於該controller內的起始action

每個controller在Views資料夾中都有一個對應名稱的資料夾
裡面置放對應controller的action的view檔案

於action中可以do  something you want 並傳遞內容至view,在view裡面可以用razor語法取值做處理

在Views資料夾裡有一個_ViewStart檔案可以指定view母版,於母版中可用razor語法 @RenderBody()  將view置入

2016年7月12日 星期二

php namesapce 概念 用法 範例

//namespace是用來區隔識別id,例如常數、函數、類別名稱,但不包含變數
//namespace是以' \ ' 模擬階層概念
//預設的namespac是' \ ',所有其他自定義namespace都是歸在其底下,可以省略,
//所以未使用自定義namespace的一般php檔案,識別名稱都不需要加' \ ',但要加也行
//要注意開頭未加上' \ ' 表示相對namespace位置,有加' \ '表示絕對namespace位置
//首個自定義namespcae必須是php檔案的第一列有效程式碼

//關鍵字 'use' 可替過長的命名空間中的 '類別 (class)' 定義簡寫,亦可取別名 (alias),
//例如 use a\b\c\Cat 如此即可以Cat存取到目標 ; use a\b\c\Cat as nsCat 如此即可以nsCat存取到目標
//use 定義即使不加' \ '亦是絕對namespace位置
//namespace參考資料 : http://pydoing.blogspot.tw/2013/03/PHP-namespace.html

//使用外部檔案以include或require引入,
//等同在指定位置貼上該檔案程式碼片段,但其namespace定義已完成,不會在引入處再次作用,除此之外namespace運作規則不變
//include與require差異參考資料: http://syunguo.blogspot.tw/2013/04/phpinclude-require.html

//當類別庫愈來愈完整,需要引入的檔案就愈多,一個一個引入太沒效率,這時可使用autoload功能
//autoload功能參考資料: http://justericgg.logdown.com/posts/196891-php-series-autoload

//以下namespace使用簡例

//定義namespace
namespace a\b\c;
//於其中定義識別名稱,此處以常數為例
const Demo = 2;

//定義另一個namespace
namespace a\b;
const Demo = 1;
$test = 3;
//存取常數內容

//相對namespace位置,以最近的namespace為所在位置指定識別名稱,類似 '目前資料夾' 概念,
//於特定namespace中以相對namespace位置指定識別名稱時要注意階層關係
echo Demo;
echo c\Demo;
//絕對namespace位置,以完整namespace階層指定識別名稱,類似 '資料夾絕對路徑' 概念
echo \a\b\Demo;
echo \a\b\c\Demo;

2016年7月4日 星期一

OOP 物件導向 用不同的方式理解instance(實體,實例)

物件導向中,instance一般稱為實例、副本,或稱之為實體

所謂instance就是實際在記憶體佔有位置的物件,以new關鍵字創造instance時,是在記憶體畫一塊空間給該物件,儲存該物件的相關資訊,每個屬性和方法都在該空間佔有一席之地,儲存對應的值

在使用new關鍵字建立新的instance時,需給予一個變數儲存其指標,例如:

var obj = new Object( );

在javascript中,上列語法等同下列:

var obj = {};

' { } ' 這個大括弧就好像在記憶體上畫一塊地,說這是我的地盤,需要注意的是obj代表的只是這塊地的地圖,而不是這塊地本身

obj.name = '';
obj.age = '';

這就好比是在自己的地盤上再切割小的區塊,並分別為其取名為name和age

obj.name = 'Ryu';
obj.age = 33;

接著我們在name區塊上寫下Ryu這三個字,在age區塊上寫下33這個數字

var map = obj;

這並未在記憶體上再畫一塊地給map,而只是將地圖複製了一份到map上

map 和 obj指向的都是同一塊地,這塊地有著name和age兩個區域

obj = '';

這清除了存放在obj中的地圖,無法再使用它找到那塊地,但那塊地依然存在

obj.name

找不到那塊地,自然也找不到name區域

map.name

還好,有先備份了一份地圖,還可以靠它找到那片土地,找到其中的name區域,並取得寫在該區域上的東西

obj = new Object();

接著我們在記憶體上畫了一塊新的空地,並把地圖放進obj中

現在我們總共擁有兩塊地了,並分別將位置記錄在map和obj這兩個變數中

map = '';

一不小心,弄丟了其中一塊地的地圖,其僅存的一張地圖,從此我們再也無法到達那塊地了,也無法再看到那塊地有哪些區域,以及區域上寫些什麼了

雖然我們已經不知道如何到達那塊地,但它一直都在那裡,過了一陣子,土地管理者(garbage collector)發現這塊地無人使用,也沒有任何地圖指向它,便再度開放給有需要的人使用了





2016年7月2日 星期六

javascript 物件導向 繼承 運作方式 prototype chain 範例

//以下以實例說明javascript的物件導向繼承運作方式


//定義動物類別
function Animal (weight, height, numLegs) {
    this.weight = weight;
    this.height = height;
    this.moveable = true;
}

//定義狗類別
function Dog (name) {
    this.name = name;
    this.setName = function (name){
        this.name = name;
    };
}

//創造一個狗類別實體
var laifu = new Dog('laifu');

//透過類別的prototype新增吠叫方法,這可以直接讓既存的實體擁有吠叫的能力

Dog.prototype.bark = function (){
    console.log('woof!');  
};

laifu.bark();
//可使用bark方法
//因為實體會透過prototype chain"向上"從祖先的prototype中尋找可用的方法與屬性
//實體的__proto__屬性儲存著所屬類別的prototype內容,
//所屬類別的prototype的__proto__屬性儲存著父類別的prototype內容
//向上延伸直至原始類別Object的prototype,再往上就是null
//實際運作過程是使用new創建實體時會run一遍prototype chain,取得可用的屬性和方法,
//相同的屬性或方法以血緣近者優先取用
//可透過右側方式探索prototype chain: laigu.__proto__.__proto__...

//讓狗類別繼承Animal類別
Dog.prototype = new Animal();
//此時Dog.prototype指向的記憶體位置已不同,已是指向Animal類別的一個實體,值已從原本的{bark:function(){}}變成{weight:undefined, height:undefined, ...},而Dog.prototype.__proto__指向的是其父類別的prototype,也就是Animal.prototype

laifu.bark();
//laifu.__proto__指向的仍是原本的記憶體位置,也就是原本的Dog.prototype,值是{bark:function(){}},所以仍然可使用bark方法

//創造另一個狗類別實體
var littleBlack = new Dog('littleBlack');

//littleBlack.bark();已無法使用這個方法,因為littleBlack.__proto__指向的Dog.prototype已是新的位置,有新的值{weight:undefined, height:undefined, ...}

console.log(littleBlack.moveable);//繼承後新建的實體依循新的prototype chain取得moveable這個屬性
console.log(laifu.moveable);//繼承前既存的實體仍是依循原本的prototype chain,其中沒有moveable這個屬性,所以輸出undefined

//定義一個新的黃金獵犬類別

function GoldenRetriever() {
    this.hunt = function () {
        console.log('It\'s hunting');  
    };
}

//繼承狗類別
GoldenRetriever.prototype = new Dog();

var littleGold = new GoldenRetriever();

//實體littleGold透過littleGold.__proto__,也就是GoldenRetriever.prototype獲得name屬性並習得setName方法
//以下內容值驗證,後續的console.log都是在驗證內容值
console.log(littleGold.__proto__);
console.log(GoldenRetriever.prototype);

littleGold.setName('littleGold');
console.log(littleGold.name);

//透過littleGold.__proto__.__proto__,也就是Dog.prototype獲得moveable屬性
console.log(littleGold.__proto__.__proto__);
console.log(Dog.prototype);
console.log(littleBlack.__proto__);

console.log(littleGold.moveable);

//使用自己的方法
littleGold.hunt();

//使用prototype動態新增屬性與方法,會即時套用至prototype chain上所有既存實體
Animal.prototype.p1 = 'p1';
Dog.prototype.p2 = function (){console.log('p2')};
GoldenRetriever.prototype.p3 = 'p3';

console.log(littleGold.p1);
littleGold.p2();
console.log(littleGold.p3);


//相同的屬性或方法,血緣近者優先取用
Animal.prototype.lol = '789';
Dog.prototype.lol = '456';
GoldenRetriever.prototype.lol = '123';

console.log(littleGold.lol);

//輸出prototype chain尾端類別實體擁有的全部屬性和方法
for(prop in littleGold){
    console.log(prop); 
}

//分界線
console.log('-------------------------------');


//private 屬性和方法,物件中以this定義的屬性和方法為public,以var定義者為private
//private 屬性和方法不會在for in迴圈中顯示
function Person(name, age, weight){
    //public
    this.name = name;
    this.say = function (words){
        console.log(this.name + ' says ' + words + '.');
    };
    //private
    var age = age;
    var weight = weight;
    var think = function(question){
        return ('I think ' + question + ' is a great thing.');
    };
    //使用public方法存取private屬性和方法
    this.setAge = function (num){
        age = num;
    };
    this.tellingAge = function (friendship){
        if(friendship === 'good') return age;
        else return 'this is secret.';
    };
    this.tellingWeight = function (friendship){
        if(friendship === 'good') return weight;
        else return 'this is secret.';
    };
    this.advise = function (friendship){
        if(friendship === 'good') return think;
        else return 'sorry I have no time.';
    };
}

var Ryu = new Person('Ryu',36,75);

for(p in Ryu){
    console.log(p);
}

console.log(Ryu.name);
Ryu.say('hello!');
console.log(Ryu.tellingAge('good'));
console.log(Ryu.tellingWeight('normal'));

var thinkingMethod = Ryu.advise('good');
var thought = thinkingMethod('OOP');

console.log( thought );
console.log( thinkingMethod('OOP') );
console.log( Ryu.advise('good')('OOP') );

console.log('------------------------------');

//定義rosyPerson類別
function rosyPerson (mentality) {
    this.mentality = mentality;
    var hardshipExp = 'many years ago...';
    this.tellingStory = function (){
      return hardshipExp;  
    };
}

//繼承Person類別,private屬性和方法一樣會繼承
rosyPerson.prototype = new Person();

var rosyKen = new rosyPerson('positive');

for(p in rosyKen){
    console.log(p);
}

console.log( rosyKen.advise('good')('programming') );
rosyKen.setAge(33);
console.log( rosyKen.tellingAge('good') );

//所有物件皆繼承原始物件Object,因此擁有Object.prototype中的所有屬性和方法,例如hasOwnProperty方法
//可透過console.log(Object.prototype)檢視有哪些通用的方法和屬性
console.log(Object.prototype);

//hasOwnProperty方法確認是否擁有指定的屬性或方法,傳回true或false,繼承的屬性或方法會傳回false,
//private 屬性和方法也會傳回false

console.log(rosyKen.hasOwnProperty('mentality'));
console.log(rosyKen.hasOwnProperty('name'));
console.log(rosyKen.hasOwnProperty('hardshipExp'));
console.log(rosyKen.hasOwnProperty('tellingStory'));
console.log(rosyKen.tellingStory());


//打完收工