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月6日 星期二
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";
}
//==========================
$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月9日 星期二
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;
}
寫法二不一定是最好的方式,也一定有更好的寫法,但這篇文章想表達的是養成思考是否有更佳的實現方式的習慣,就會一直進步這個觀念。
我們來比較一下差異
======================================
寫法一:相對較差的方式
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月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)發現這塊地無人使用,也沒有任何地圖指向它,便再度開放給有需要的人使用了
所謂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都是在驗證內容值
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());
//打完收工
2016年7月1日 星期五
2016年6月30日 星期四
2016年6月29日 星期三
2016年6月25日 星期六
javascript 語法效率實驗: 直接存取物件屬性以及先將物件屬性存入變數再存取
2016年6月22日 星期三
jquery 效率實驗:將$('target')結果先儲存到變數中的益處
jquery的運作模式,簡單講就是選擇目標然後執行指定動作,這裡要討論的是選擇目標這個動作對程式執行效率的影響
$('p').hide();
上面這列語法是隱藏所有的p元素,其底層運作是搜尋html文件中的所有p元素,再逐一修改其attr的style,設定其display為none
$('css selector') 這個語法包含了搜尋動作,所以先將搜尋結果儲存到變數裡,之後就不用重複搜尋,例如:
方式1:
$('p').hide();
$('p').show();
方式2:
var jqP = $('p');
jqP.hide();
jqP.show();
方式2相較於方式1,執行速度會更快
我們可以做個實驗驗證,程式碼在以下網址,進入後點擊左上"Run"按鈕即可在右下窗格看見執行結果,點擊按鈕即可進行實驗
(建議用電腦觀看,手機可能無法正常運行實驗)
(建議用電腦觀看,手機可能無法正常運行實驗)
從以上實驗結果可以看出速度的差異,速度差異和html文件中的元素數量成正比
(因為我們實驗的動作是對全部的p寫入class,所以若增加的元素是p,對整體速度影響更大,但其中有部份是寫入class造成,比較客觀的方式是加入其他元素,就主要會是搜尋的影響)
(因為我們實驗的動作是對全部的p寫入class,所以若增加的元素是p,對整體速度影響更大,但其中有部份是寫入class造成,比較客觀的方式是加入其他元素,就主要會是搜尋的影響)
另外假設同樣在一堆元素中尋找一個特定元素,使用id尋找和使用class尋找兩者比較起來,用id速度會比用class快
有興趣可以自行修改一下上述網址程式碼,賦予其中一個p以id屬性,再將jquery語法的p改成id,跑看看時間,再將id改成class,比較一下時間差異,會發現用id尋找比較快,不過不用是用id還是class,都是先將搜尋結果儲存到變數最快
雖然一般使用差異不大,但當專案規模變大時,程式碼的效率影響力也會成正比上升,這是一個概念,這個範例只是一個簡單的例子
以上分享。
2016年6月20日 星期一
cookie, session, localStorage, sessionStorage差異
cookie:
容量較小,儲存於client端,生命每次client向server提出request時都會傳輸至server,server回應時亦會回傳
session:
儲存於server端,於client端會建立cookie儲存sessionID,預設在瀏覽器標籤或是瀏覽器關閉時即會清除
localStorage:
容量較大(約5MB),儲存於client端,單純的本地端資料儲存空間
sessionStorage:
同localStorage,差別只在當瀏覽器標籤或是瀏覽器關閉時即清除
容量較小,儲存於client端,生命每次client向server提出request時都會傳輸至server,server回應時亦會回傳
session:
儲存於server端,於client端會建立cookie儲存sessionID,預設在瀏覽器標籤或是瀏覽器關閉時即會清除
localStorage:
容量較大(約5MB),儲存於client端,單純的本地端資料儲存空間
sessionStorage:
同localStorage,差別只在當瀏覽器標籤或是瀏覽器關閉時即清除
html javascript return false的應用
在下列情況下return false可防止html元素執行預設動作:
例1:
<a onclick="return confirm('are you sure?')" href="https//google.com.tw">go to google</a>
點擊"確定"(return true)才會前往google,否則(return false)無動作
例2:
<form action="">
<button onclick="return confirm('are you sure?')">click me</button>
</form>
點擊"確定"(return true)才會送出表單,否則(return false)無動作
需注意return false的接收者必須是元素本身,以下方式是無效的
onclick="myFunction()"
function myFunctoin() {
return confirm('are you sure?');
}
相關原因說明可參考下列這篇說明:
html onclick 屬性觸發的function如何取得元素物件
例1:
<a onclick="return confirm('are you sure?')" href="https//google.com.tw">go to google</a>
點擊"確定"(return true)才會前往google,否則(return false)無動作
例2:
<form action="">
<button onclick="return confirm('are you sure?')">click me</button>
</form>
點擊"確定"(return true)才會送出表單,否則(return false)無動作
需注意return false的接收者必須是元素本身,以下方式是無效的
onclick="myFunction()"
function myFunctoin() {
return confirm('are you sure?');
}
相關原因說明可參考下列這篇說明:
html onclick 屬性觸發的function如何取得元素物件
html onclick 屬性觸發的function如何取得元素物件
假設html文件中有一個a元素如下:
<a onclick="點擊連結時觸發的javascript程式碼" href="https://google.com.tw">click me</a>
onclick屬性是當使用者點擊元素時觸發其中的程式碼
很多時候我們會希望能取得a元素本身這個物件做進一步的判斷和處理,例如點擊時變更該元素的css屬性或是刪除該元素等等,那麼該如何達到目的呢?
首先我們要知道onclick="",其實 "" 背後代表的是一個匿名函數,是綁定到這個a元素的click事件的一個匿名函數, "" 就是隱形的function () 的 大括號,因此在這個函數內取得自身元素的最簡單方法就是"this"關鍵字:
onclick="console.log(this);" //其中的this就代表a這個元素
但是很多時候在點擊之後會需要做比較多的處理,不適合在inline屬性(直接寫在html元素裡面的屬性)上寫太多程式碼,此時就需要另外定義一個函數做處理
onclick="myFunction();"
<script>
function myFunction () {
console.log(this);
//注意! 此時這個this是global物件,也就是window物件,而非a元素
}
</script>
此時要如何取得元素物件呢?
方法1:
呼叫myFunction時將a元素物件做為參數傳遞
onclick="myFunction(this);" // 在隱形的匿名函數裡this是指a元素
function myFunction (tarObj) {
console.log(tarObj);
//此時tarObj代表在呼叫時傳入的this,也就是a元素
}
方法2:
以call或apply方法呼叫函數,並指定函數owner為a元素
先說明call的用法:
function test(a,b) {
console.log(a*b);
console.log(this);
}
這是一個擁有兩個參數的函數,使用call方法呼叫的語法如下:
test.call(<某個p元素>, 5, 3)
第一個參數會成為test函數內部的this所代表的元素
所以輸出結果會如下:
15
<p></p>
現在回到主題,
onclick="myFunction.call(this);"
// 在隱形的匿名函數裡this是指a元素,而call方法的第一個參數指定函數內部的this關鍵字代表的物件
function myFunction () {
console.log(this);
//此時的this就代表a元素,因為在使用call方法呼叫myFunction這個函數時,第一個參數傳入this(在當時環境下,代表a元素),而call的第一個參數,則是讓指定的元素成為被呼叫的函數中的this,有點抽象,因為都叫this,但代表的是不同的東西
}
以上分享
<a onclick="點擊連結時觸發的javascript程式碼" href="https://google.com.tw">click me</a>
onclick屬性是當使用者點擊元素時觸發其中的程式碼
很多時候我們會希望能取得a元素本身這個物件做進一步的判斷和處理,例如點擊時變更該元素的css屬性或是刪除該元素等等,那麼該如何達到目的呢?
首先我們要知道onclick="",其實 "" 背後代表的是一個匿名函數,是綁定到這個a元素的click事件的一個匿名函數, "" 就是隱形的function () 的 大括號,因此在這個函數內取得自身元素的最簡單方法就是"this"關鍵字:
onclick="console.log(this);" //其中的this就代表a這個元素
但是很多時候在點擊之後會需要做比較多的處理,不適合在inline屬性(直接寫在html元素裡面的屬性)上寫太多程式碼,此時就需要另外定義一個函數做處理
onclick="myFunction();"
<script>
function myFunction () {
console.log(this);
//注意! 此時這個this是global物件,也就是window物件,而非a元素
}
</script>
此時要如何取得元素物件呢?
方法1:
呼叫myFunction時將a元素物件做為參數傳遞
onclick="myFunction(this);" // 在隱形的匿名函數裡this是指a元素
function myFunction (tarObj) {
console.log(tarObj);
//此時tarObj代表在呼叫時傳入的this,也就是a元素
}
方法2:
以call或apply方法呼叫函數,並指定函數owner為a元素
先說明call的用法:
function test(a,b) {
console.log(a*b);
console.log(this);
}
這是一個擁有兩個參數的函數,使用call方法呼叫的語法如下:
test.call(<某個p元素>, 5, 3)
第一個參數會成為test函數內部的this所代表的元素
所以輸出結果會如下:
15
<p></p>
現在回到主題,
onclick="myFunction.call(this);"
// 在隱形的匿名函數裡this是指a元素,而call方法的第一個參數指定函數內部的this關鍵字代表的物件
function myFunction () {
console.log(this);
//此時的this就代表a元素,因為在使用call方法呼叫myFunction這個函數時,第一個參數傳入this(在當時環境下,代表a元素),而call的第一個參數,則是讓指定的元素成為被呼叫的函數中的this,有點抽象,因為都叫this,但代表的是不同的東西
}
以上分享
javascript closure
w3schools對於closure的註解與例子:
註解:
A closure is a function having access to the parent function scope, even after the parent function has closed.
例子:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// the counter is now 3
closure可以產生不會重置的私有變數
註解:
A closure is a function having access to the parent function scope, even after the parent function has closed.
例子:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// the counter is now 3
closure可以產生不會重置的私有變數
javascript call 和 apply的差別
在js裡,function也是物件,有兩個預設的方法用來觸發作用,call和apply,兩個方法的第一個參數都是指定function的owner,也就是this代表的物件
var myObject={}, myArray=[10,2];
function test (a,b) {
console.log(this);
return a*b;
}
test.call(myObject,10,2) //this為myObject return 20
test.apply(myObject,myArray) //this為myObject return 20
call和apply的差別僅在於參數的給定方式,call一對一傳入參數,apply則是傳入一個參數陣列
w3schools上對於strict模式和non-strict對這兩個方法的影響之說明:
In JavaScript strict mode, the first argument becomes the value of this in the invoked function, even if the argument is not an object.
In "non-strict" mode, if the value of the first argument is null or undefined, it is replaced with the global object.
function除了兩個內建的觸發方法之外,還有1個內建的屬性和1個方法,arguments.length屬性和toString()方法:
arguments.length屬性傳回接收到的參數數量
function myFunction(a, b) {
return arguments.length;
}
myFunction(5,6) //傳回2
toString()方法將function以文字形態傳回
function myFunction (a, b) {
return a * b;
}
var txt = myFunction.toString();
console.log(txt)
// console輸出如下內容
function myFunction (a, b) {
return a * b;
}
訂閱:
文章 (Atom)



