2016年6月30日 星期四

HTML5 上傳圖片預覽範例

點擊下方按鈕上傳圖片檢視預覽效果:



點我檢視程式碼範例

2016年6月26日 星期日

javascript 特性與觀念整理

紀錄目前為止個人覺得蠻重要的javascript特性和觀念:
以下程式碼,你能看出console的輸出結果嗎?

1、
var var1 = 'a';

function fun1 (var1) {
    var1='b';
    console.log(var1);
}

fun1(var1);

console.log(var1);


2、
var var2 = {
    proper: 'a'
};

function fun2(var2) {
     var2.proper = 'b';
     console.log(var2.proper);
   
     var var3 = var2;

     console.log(var3.proper);
}

fun2(var2);

console.log(var2.proper);

console.log(var3.proper);


3、
var var4 = {
    proper1: 'a'
};

function fun3(var4) {
    var4 = {
        proper1: 'b',
        proper2: 'w'
    };
    console.log(var4.proper1);
    console.log(var4.proper2);
}

fun3(var4);

console.log(var4.proper1);
console.log(var4.proper2);


4、
var var5 = {
    proper1: 'a',
    proper2: {}
};

function fun4(){
    var var6 = var5,
          proper1 = var6.proper1,
          proper2 = var6.proper2;
 
    proper1 = 'b';
    console.log(var6.proper1);
    proper2.attr = 'w';

    proper2 = 'y';
    console.log(typeof var6.proper2);
    console.log(var6.proper2.attr);
    var obj = {
        properx: 'z'
    };
    var6.proper2.z = obj;
    console.log(var6.proper2.z.properx);
    return var6;
}

var var7 = fun4();

console.log(var5.proper1);
console.log(var5.proper2.attr);

var5 = '';

console.log(var7.proper2.attr);
console.log(var7.proper2.z);
console.log(var7.proper2.z.properx);


以下解答:

1、
var var1 = 'a';

function fun1 (var1) {
    var1='b';
    console.log(var1);
}

fun1(var1); //呼叫fun1並傳入global 變數var1的值(基本形態的字串'a')存入local變數var1之中,於fun1中修改了local變數var1的值為字串的'b',緊接著在console輸出local變數var1的值 'b'

console.log(var1);//輸出global變數var1的值'a'

//程式碼1執行結果驗證


2、
var var2 = {
    proper: 'a'
};

function fun2(var2) {
     var2.proper = 'b';
     console.log(var2.proper);
   
     var var3 = var2;

     console.log(var3.proper);
}

fun2(var2);//呼叫fun2並傳入global變數var2的值(global物件形態指標),存入local變數var2,在函數內部對global物件的proper屬性值做修改,改為'b',接著console輸出這個屬性的值'b',接著宣告一個local變數var3,並將local var2的值(global 物件指標)賦於var3,接著console輸出global物件的proper屬性值'b'

console.log(var2.proper);//console輸出global var2的值(global物件)的proper屬性值'b'

console.log(var3.proper);//因scope限制,無法存取local變數var3的值(global物件的指標),於console輸出var3 is not defined,也就無法進一步讀取物件的proper屬性值

//程式碼2執行結果驗證


3、
var var4 = {
    proper1: 'a'
};

function fun3(var4) {
    var4 = {
        proper1: 'b',
        proper2: 'w'
    };
    console.log(var4.proper1);
    console.log(var4.proper2);
}

fun3(var4);//呼叫fun3並傳入global變數var4的值(物件形態指標)存入local變數var4中,於函數內部對local變數var4做賦新值的動作,宣告一個local的物件存入其中,覆蓋掉原本的global物件指標,新物件擁有兩個屬性,接著console輸出local物件的兩個屬性值,分別為'b'和'w'

console.log(var4.proper1);//console輸出global物件的proper1屬性值'a'
console.log(var4.proper2);//global物件未定義proper2這個屬性,console輸出undefined

//程式碼3執行結果驗證


4、
var var5 = {
    proper1: 'a',
    proper2: {}
};

function fun4(){
    var var6 = var5,
          proper1 = var6.proper1,
          proper2 = var6.proper2;
 
    proper1 = 'b';
    console.log(var6.proper1);
    proper2.attr = 'w';

    proper2 = 'y';
    console.log(typeof var6.proper2);
    console.log(var6.proper2.attr);
    var obj = {
        properx: 'z'
    };
    var6.proper2.z = obj;
    console.log(var6.proper2.z.properx);
    return var6;
}

var var7 = fun4();//呼叫func4,於函數內宣告local變數var6,並將global變數var5的值(global物件指標)賦於它,接著宣告兩個local變數proper1, proper2並分別將global物件的proper1屬性的值('a')與proper2屬性的值(global物件指標)賦予兩者,

接著修改local變數proper1的值為文字'b',接著console輸出global物件的proper1屬性值'a',接著為置於local變數proper2中的global物件新增一個名為attr的屬性,並賦予其值為文字'w',接著修改local變數proper2的值為文字'y',覆蓋掉原本的global物件指標,

接著console輸出置於local變數var6中的global物件的proper2屬性之形態,結果為object,然後接著console輸出其attr屬性值'w',接著宣告一個local變數obj,賦於其一個local物件,此物件擁有一個properx屬性,值為文字'z',接著為global物件的proper2屬性值(global物件指標),新增一個屬性名為z,並將local變數obj的值(local物件指標)賦予它,

然後console輸出置於global物件中的global屬性變數z中的local物件之properx屬性值'z',最後回傳local變數var6的值(與global變數var5相同的global物件指標)存於global變數var7中

console.log(var5.proper1);//console輸出置於global變數var5中的global物件的proper1屬性值'a'
console.log(var5.proper2.attr);//console輸出global物件的proper2屬性值(物件指標)的attr屬性值'w'

var5 = '';//修改global變數var5的值為''

console.log(var7.proper2.attr);//console輸出global變數var7的值(global物件指標,此時var5已解除與此物件的reference)的proper2屬性值(物件指標)的attr屬性值'w'

寫到這邊忽然領悟到{}物件形態是指標,無scope限制,文中'global物件'以及'local物件'之修詞並不恰當,應統一稱'物件形態指標'

console.log(var7.proper2.z);//因scope限制,無法存取z屬性中儲存的local物件,console輸出undefined => 更正,物件形態指標不受scope影響,console輸出Object {properx: "z"}

console.log(var7.proper2.z.properx);//因上列原因,無法進一步存取local物件的properx屬性,console輸出undefined => 更正,物件形態指標不受scope影響,console輸出屬性值'z'

//程式碼4執行結果驗證

記憶體管理:

未被任何變數參照的記憶體會被GC回收,然後釋放記憶體(滿足特定條件時,不一定即時)
var ref1 = {
    proper1: 'abc'
};//ref1參照到此物件

var ref2 = ref1;//現在ref2也參照到該物件,該物件被兩個變數參照

ref1 = '';//ref1不再參照到該物件,剩ref2參照

ref2 = '';//現在物件{proper1: 'abc'}已無任何變數參照,將會被GC回收釋放記憶體

更詳細的解說可參考下列連結文章:

簡單理解 JavaScript 的記憶體管理機制

刪除變數使用delete關鍵字


2016年6月25日 星期六

javascript 語法效率實驗: 直接以陣列索引存取值以及先將值存入變數再存取

var arr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14];

var expTimes = 100000, test = 0;
//方式1
for (let i=0;i<expTimes;i++){
               test = arr[9];
}

//方式2
var temp = arr[9];
for (let i=0;i<expTimes;i++){
               test = temp;
}

何者速度會更快呢?

效率實驗

實驗結果先存入變數稍快,但差異不明顯

javascript 傳值還是傳址?

今天在網路上看到一篇文章在討論這個議題,於是在底下回覆了一下想法,在此留個紀錄

觀念釐清

以下截圖留存




javascript 語法效率實驗: 直接存取物件屬性以及先將物件屬性存入變數再存取

假設一個物件有a~z 26個屬性,存取其中一個屬性例如obj.z並做修改值的動作,先將obj.z存入變數與直接存取obj.z是否會有速度的差異呢?

實驗如下:

物件屬性存取實驗1

物件屬性存取實驗2

以結果來說兩種方式並無顯著差異


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造成,比較客觀的方式是加入其他元素,就主要會是搜尋的影響)

另外假設同樣在一堆元素中尋找一個特定元素,使用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,差別只在當瀏覽器標籤或是瀏覽器關閉時即清除

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如何取得元素物件

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,但代表的是不同的東西
   }

以上分享







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可以產生不會重置的私有變數

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;
}

2016年6月19日 星期日

每日一步: t_sql 變數宣告後未賦值的狀態下初始值是null

 t_sql 變數宣告後未賦值的狀態下初始值是null,若直接拿來做運算會導致結果為null
例如:
declare @name
slelect @name = @name + 姓名 + ' '
from 員工

select @name

查詢結果為:
null
因為@name初始值為null,null = null + 姓名 + ' ' 結果為null

應修正為:

declare @name = '' --賦予空值初始值
slelect @name = @name + 姓名 + ' '
from 員工

select @name

查詢結果就會是:

陳xx 李xx 唐xx

t_sql

區域變數宣告:區域變數範圍以GO關鍵字區隔
declare @var1 as varchar(30)

declare @var2 int, @var3 datetime

賦值:

方法1:
在宣告時直接賦值,例如 declare @var1 = 'mytext'

方法2:
set @var1 = 'book store'

set@var2 = 1

set@var3 = getdate()

方法3:
select @var1 = 'mytext'

查詢:

select @var1

select @var2

select @var3

查詢結果:
book store
1
2016-.......當時日期

例1:
declare @customer

select @customer = 客戶名稱
from 客戶
where 客戶名稱 = 4

select @customer

查詢結果:
會查詢客戶資料表中客戶名稱欄位值為4的資料,並將結果存到@customer中
最後再用select @customer 指令顯示出來


系統變數:
@@+變數名稱
例:
select @@VERSION -- 查詢SQL SERVER軟體版本

select @@LANGUAGE --查詢語言

select @@ROWCOUNT --回傳最後一次查詢結果列數

於訊息標籤輸出結果:
PRINT指令

例:
PRINT 'ABC'
PRINT 'ABC' + 'DEF'
PRINT 1
PRINT 1 + 1

訊息標籤內顯示之結果:
ABC
ABCDEF
1
2


IF判斷式:
例1:

declare @x = 1

if @x >2
   PRINT '@x>2
else
   PRINT '@x<2'
GO

輸出結果:
@x<2

例2:

if (select sum(單價) from 標標公司) > 1000
    PRINT '標標公司產品總價大於1000
esle
    PRINT '標標公司產品總價小於1000
GO

if  'Windows 使用手冊' in (select 書籍名稱 from 書籍)
    PRINT '有Windows 使用手冊'
else
    PRINT '無Windows 使用手冊'
GO

if  1000 > all (select 單價 from 標標公司)
    PRINT '標標公司產品單價最高不超過1000
GO


while 迴圈:
例:

declare @id=0, @name='', @price=0, @count=1

while @id < 500
begin
    @id+=1
    select @name = 書籍名稱, @price = 單價
    from 書籍
    where bookId = @id
    if @@ROWCOUNT = 0 break
    if @price > 400 contiune
    PRINT @name
    if @count %3 =0 PRINT '------' -- 每3筆資料分隔
    @count +=1
end
GO

以上程式碼查詢400元以上的書籍名稱,在訊息標籤輸出結果


switch條件式:以CASE關鍵字觸發
例1:

declare @a =3, @answer varchar(10)

set @answer = CASE @a
    when 1 then 'a'
    when 2 then 'b'
    when 3 then 'c'
    when 4 then 'd'
    when 5 then 'e'
end
PRINT 'answer is ' + @answer
GO

輸出結果:
answer is c

例2:

select 公司名稱, 聯絡人 + case 性別
                          when '男' then '先生'
                          when '女' then '小姐'
                          else '敬啟者'
                          end as '聯絡人'
                          , 電話
from 客戶

查詢結果:
公司名稱 / 聯絡人 / 電話
    xxx公司/xxx先生/xxxxx
    xxx公司/xxx小姐/xxxxx


程式列跳躍指令GOTO:
例1:

declare @number =3
if  (@number % 3 =0)
    GOTO THREE
else
    GOTO NOTTHREE


THREE:
    PRINT '三的倍數'

GOTO THEEND

NOTTHREE:
    PRINT '不是三的倍數'


THEEND:

GO

例2:
declare @num =0

start:
select @num +=1

if @num > 10 GOTO theend
if (@num % 2 =0) GOTO even
else GOTO odd

even:
    PRINT cast(@num as char(2)) + 'is 偶數'
GOTO start
odd:
    PRINT cast(@num as char(2)) + 'is 奇數'
GOTO start

theend:

2016年6月18日 星期六

每日一步: html javascript canvas 的 strokeStyle 陷阱

canvas 的 strokeStyle屬性賦值可以用rgb和hex,但讀取值時回傳的是hex,而css的屬性設定賦值同樣可以用rgb和hex,但讀取值時回傳的是rgb,
直覺會認為賦值時用哪種格式,讀取時就會回傳該格式,但實際上不是,因此在讀取css顏色屬性和strokeStyle比對時不小心就會變成用rgb和hex比較,導致結果不如預期,要謹記。

2016年6月16日 星期四

每日一步: 限制html input元素內容只能輸入數字

在input元素上加入以下屬性:

 onkeypress='return event.charCode >= 48 && event.charCode <= 57'

只有當輸入的內容為數字時才會顯示

但這無法限制中文輸入法產生的內容,中文輸入法需另外用js處理

每日一步: html dom node與elemet差異

html dom 語法有兩種回傳類別,一種是node,一種是element,差別在於element不含text和comment,node則全含,文字的空格也算是text node,不瞭解這點會造成意料之外的bug

例如element.children傳回指定元素的子元素,但不含text和comment
element.childNodes傳回指定元素的所有子元素node,包含text和comment

2016年6月14日 星期二

每日一步: css 強制換行word-wrap: break-word

限制文字內容寬度,當文字內容達寬度限制時,強制其換行之css設定:

word-wrap: break-word;

2016年6月10日 星期五

每日一步: css box-sizing效果

box-sizing:border-box設定讓在設定padding時不會影響總寬高,而是內部調整,例如原本width:100px,設定padding-left:5px時,總寬會變105px,若是在box-sizing:border-box設定底下,總寬會是100px,含padding-left:5px

每日一步: css 消除inline-block的元素間距

將父元素的font-size設為0即可

2016年6月9日 星期四

每日一步: css 讓border重疊,並且同時可正常顯示focus特效方法

讓border重疊可透過設定負值的margin,例如div1的右邊界要與div2的左邊界重疊,可設定div1的margin-right:-1px或是div2的margin-left:-1px即可

focus狀態邊框特效在邊界重疊的情況下通常會有一方會被遮住,解法為將被遮住的元素position設為relative,然後設定該元素的focus狀態變更其z-index,使其比另一個元素高即可,例如:

div1的右邊界和div2的左邊界重疊,focus div1時右邊界特效被div2遮住,此時設定div1的position為relative,然後設定div1:focus { z-index:n;},n要比div2的z-index高,即可正常顯示。

2016年6月5日 星期日

每日一步: padding單位用%的意義

padding: 20%

padding屬性單位用%給定時,其基準為父元素的寬度,不論top,bottom,left,right皆同

2016年6月2日 星期四

2016年6月1日 星期三

javascript let 和 var 的差別

1、scope的差別
在最外層宣告的時候,兩者都會是global scope,除此之外let範圍只在' { } ' block內部, 需要注意的是若是let使用為迴圈的遞增或遞減變數,每次執行迴圈的該變數都是獨立的scope,var 則是以function為層級

2、let不會hoist
var 的宣告會hoist,let不會


來看例子:

function test ( ) {
        //alert(y)可正常運作
        //alert(x)會出錯,x is not defined
        for(var i=0 ; i<2 ; i++ ) {
    
        //alert(y)可正常運作
        //alert(x)會出錯,x is not defined


        let x;
        var y;

        //alert(x)可正常運作
   }


}

迴圈的情況

for (let i = 1; i <6; i++) {
    setTimeout(function() {
        console.log(i)
    }, i * 1000)
}

每次的i都是獨立的

對照組

for (var i = 1; i <6; i++) {
    setTimeout(function() {
        console.log(i)
    }, i * 1000)
}

是同一個i