顯示具有 javascript 標籤的文章。 顯示所有文章
顯示具有 javascript 標籤的文章。 顯示所有文章

2019年3月16日 星期六

農曆年與天干地支轉換

<html>
    <head>
        <title>農曆年與天干地支轉換程式</title>
        <script>
            /**
            *    民國13年為甲子年,並不是民國1年為甲子年
            */
            window.onload = function() {
                document.getElementById('sl').checked = false;
            }
    
            var sky = ["", "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"];
            var land = ["", "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
            
            function sWord() {
                let sw = sky[document.getElementById('s').value];
                document.getElementById('sWord').innerText = typeof sw != 'undefined' ? sw : '';
            }
    
            function lWord() {
                let lw = land[document.getElementById('l').value];
                document.getElementById('lWord').innerText = typeof lw != 'undefined' ? lw : '';
            }
    
            /**
            *    勾選天干地支的顯示隱藏
            */
            function slCheck() {
                let sl = document.getElementById('sl').checked;
                let slView = document.getElementById('slView');
                let lunarSolarYear = document.getElementById('lunarSolarYear');
    
                if(sl) {
                    slView.style.display = 'block';
                    lunarSolarYear.style.display = 'none';
                    sWord();
                    lWord();
                } else {
                    slView.style.display = 'none';
                    lunarSolarYear.style.display = 'block';
                }
            }
    
            function display() {
                var num = document.getElementById('num').value;
                var sl = document.getElementById('sl').checked;
    
                if(!sl) {
                    // 農曆年轉天干地支
                    if(num <= 0) {
                        alert('請輸入正確的農曆年');
                        return;
                    }
                    if(num.length > 3) {
                        alert('輸入的農曆年不可超過千年');
                        return;
                    }
    
                    num = subtract60(num);
                    if(num.length == 1) num = '0' + num;
                    var skyResult = Number(num[1])-2 <= 0 ? sky[Number(num[1])+10-2]: sky[Number(num[1])-2];
                    alert(skyResult + land[getLandWord(Number(num))] + '年');
                } else {
                    // 天干地支轉農曆年
                    var s = document.getElementById('s').value;
                    var l = document.getElementById('l').value;
    
                    if(s > 10 || s <= 0) {
                        alert('天干必須在1~10之間');
                        return;
                    }
                    if(l > 12 || l <= 0) {
                        alert('地支必須在1~12之間');
                        return;
                    }
                    if((s % 2 == 1 && l % 2 == 0) || (s % 2 == 0 && l % 2 == 1)) {
                        alert('不正確的天干地支組合');
                        return;
                    }
    
                    var skyResult = Number(s) + 2;
                    if(skyResult >= 10) skyResult = skyResult - 10;
                    if(l.length == 1) l = '0' + l;
                    alert('農曆年為:' + getLandNumber(skyResult, l) + ' (或加減 60 的倍數)');
                }
            }
    
            /**
            *    取得地支數字
            */
            function getLandNumber(sky, land) {
                if(land.toString()[1] != sky) {
                    land = Number(land) + 12;
                    land = getLandNumber(sky, land)
                }
                return Number(land);
            }
    
            /**
            *    取得地支文字
            */
            function getLandWord(n){
                if(n>12) n = getLandWord(n-12);
                return n;
            }
    
            /**
            *    將農曆年縮小到60年之內
            */
            function subtract60(n){
                if(n>60) n = subtract60(n-60);
                return n.toString();
            }
        </script>
    </head>
    
    <body>
        <h1>農曆年與天干地支轉換程式</h1>
    
        是否使用天干地支轉農曆年<input type="checkbox" id="sl" onclick="slCheck()"><br />
        <span id='lunarSolarYear'>請輸入農曆年:
            <input type="number" id="num" style="width:70px" min='1' max='999' /><br />
        </span>
        <div id="slView" style='display:none'>
            請輸入天干:<input type="number" id="s" style="width:70px" min='1' max='10' onchange="sWord()" />
            <span id="sWord" style='color:blue'></span><br />
            請輸入地支:<input type="number" id="l" style="width:70px" min='1' max='12' onchange="lWord()" />
            <span id="lWord" style='color:blue'></span><br />
            1=甲, 2=乙, 3=丙, 4=丁, 5=戊, 6=己, 7=庚, 8=辛, 9=壬, 10=癸<br />
            1=子, 2=丑, 3=寅, 4=卯, 5=辰, 6=巳, 7=午, 8=未, 9=申, 10=酉, 11=戌, 12=亥<br />
        </div>
        <input type="button" value="確定" onclick="display()" />
    </body>
</html>

※農曆(陰陽合曆)13、73年為甲子年

※農曆年轉天干地支:個位數-2為天干,一直減到12之內為地支

※天干地支轉農曆年: 天干+2為個位數,地支較麻煩,以戊戌年來說
戊為5,加2為7,所以個位數為7
戌為11,11加多少的個位數會是7(天干的結果),必須加6才行,但還必須是12的倍數才行,60以下12的倍數有12、24、36、48,所以只有36,結果為11+36=47

2017年9月8日 星期五

array 的 pop、shift、splice 用法

都是刪除

※使用方法

let arr = new Array("a", "b", "c", "d", "e");
console.info(arr); // a b c d e
    
// pop 刪除最後一個元素,返回值為被刪除的元素
// let pop = arr.pop();
// console.info(arr); // a b c d
// console.info(pop); // e
    
// shift 刪除第一個元素,返回值為被刪除的元素
// let shift = arr.shift();
// console.info(arr); // b c d e
// console.info(shift); // a
    
// splice 最靈活的刪除,也可適情況新增
// 第一個參數,表示從指定的索引開始刪到最後,必要
// 第二個參數,表示從第一個參數之後的索引開始刪除幾個元素,非必要
// 第三個參數是「...」,刪除的索引可從第三個參數之後加入這裡輸入的元素,非必要
let splice = arr.splice(2, 3, "f", "g");
console.info(arr); // a b f g
console.info(splice); // c d e

※


※splice 刪除常見錯誤

let arr = new Array("a", "b", "c", "d", "e");
console.info(arr); // a b c d e
    
// for(let i=0; i<arr.length; i++) {
//     if(arr[i] == "a" || arr[i] == "b") {
//         arr.splice(i, 1);
//         // i--;
//     }
// }
    
arr.forEach((value, i) => {
    if(value == "a" || value == "b") {
        arr.splice(i, 1);
        // i--; 無效
    }
});
    
// 以下是用 jQuery 的 each,也是一樣的情形
// $(arr).each((i, value) => {
//     if(value == "a" || value == "b") {
//         arr.splice(i, 1);
//         i--; 無效
//     }
// });
    
// $.each(arr, (i, value) => {
//     if(value == "a" || value == "b") {
//         arr.splice(i, 1);
//         i--; 無效
//     }
// });
console.info(arr); // b c d e,非預期的 c d e

※以上都是 index 刪除後會自動往上一格,所以可用 i-- 來解決,但 forEach 是無效的

2017年9月5日 星期二

Prototype chain

※Prototype chain (原形鏈)

function Xxx(){}
    
let x1 = new Xxx();
Xxx.prototype.abc = 1;
    
let x2 = new Xxx();
x2.def = 2;
    
let x3 = new Xxx();
x3.ghi = 3;
    
console.log(x1.abc); // 1
console.log(x2.abc); // 1
console.log(x3.abc); // 1
    
console.log(x1.def); // undefined
console.log(x2.def); // 2
console.log(x3.def); // undefined
    
console.log(x1.ghi); // undefined
console.log(x2.ghi); // undefined
console.log(x3.ghi); // 3

※原形就是物件的公用空間,誰 new 這個物件,誰就可以使用,如下圖:
※要注意的是,原形也是物件,所以物件都有公用空間,也就是原形的原形,但只有定義兩層而已

※可以用__proto__屬性查看
function Xxx(){}
let x = new Xxx();
Xxx.prototype.abc = 2;
    
console.log(x.__proto__);
console.log(x.__proto__.__proto__);
console.log(x.__proto__.__proto__.__proto__); // null

※Chrome 的圖

※兩個紅框的內容是一樣的


※原形的原形的方法

function Xxx(){}
let x = new Xxx();
x.ooo = 1;
Xxx.prototype.abc = 2;
    
console.log("ooo" in x); // true
console.log(x.hasOwnProperty("ooo")); // true
    
console.log("abc" in x); // true
console.log(x.hasOwnProperty("abc")); // false,只找自己,不會往prototype找
    
// 還可以使用 hasOwnProperty 找 hasOwnProperty
console.log(x.hasOwnProperty("hasOwnProperty")); // false
console.log(x.__proto__.hasOwnProperty("hasOwnProperty")); // false
console.log(x.__proto__.__proto__.hasOwnProperty("hasOwnProperty")); // true

※如果定義和原形一樣的變數,會由自己先找,沒有才往原形找,再沒有又往原形的原形找,再沒有,就回傳 undefined

※直接印 x ,會去找原形的原形的 toString方法,想改可以用 x.toString = () => {return "";}; 去覆寫
但較新的瀏覽器版本已經不呼叫 toString 方法了

2017年7月3日 星期一

參數預設值、次方、解構賦值

※參數預設值

window.onload = () => {
    let num = 2;
    multiply1(num);
    multiply2(num);
    multiply3(num);
}
    
function multiply1(a, b) {
    b = (typeof b !== 'undefined') ?  b : 1;
    alert(a * b);
}
    
function multiply2(a, b = 1) {
    alert(a * b);
}
    
function multiply3(a, b = xxx()) {
    alert(a * b);
}
    
let xxx = () => {
    return 1;
}

※原本是像 multiply1 的寫法,可以在參數上做手腳,就變成了 multiply2 的寫法

※multiply3 是呼叫 method 後得到回傳值,最後也是和 multiply2 一樣
如果想用匿名的方式寫在等於後面,要在最後加上「()」

※如果給 undefined 是 OK 的,但給 null,數字相當於給 0,字串相當於給 null 字串



※次方

console.log(2 ** 3 ** 2); // 512,寫法不好,易造成誤解,用下面的
console.log(2 ** (3 ** 2)); // 512
console.log((2 ** 3) ** 2); // 64
    
// console.log(-2 ** 3); // 負數會報錯,用下面的
console.log(-(2 ** 3)); // -8

※



※解構賦值


※[]


window.onload = () => {
    // 字串
    data = "123";
    let [a, b] = data;
    console.log(a, b); // 1 2
    
    let {length: len} = data;
    console.log(len); // 3
    
    // 陣列,多餘的元素用不到
    let [a,b] = [1,2,3,4];
    console.log(a); // 1
    console.log(b); // 2
    
    // 巢狀
    let x = [1, ["a", "b", "c"], 3, 4];
    let [c, d] = x
    console.log(d); // "a", "b", "c"
    
    let [, [,,f]] = x
    console.log(f); // c
    
    // 呼叫 function
    let [a1, , b1] = fx();
    console.log(a1); // 1
    console.log(b1); // 3
    
    function fx(){
        return [1,2,3,4];
    }
    
    // 剩下的為剩餘參數
    let [c,d,...rest] = [1, 2, 3, 4, 5];
    console.log(c); // 1
    console.log(d); // 2
    console.log(rest); // [3, 4, 5]
    
    // 預設值
    let [e=5, f=7] = [1];
    console.log(e); // 1
    console.log(f); // 7
    
    // 變數內容交換
    let g = 1;
    let h = 3;
    [g, h] = [h, g];
    console.log(g); // 3
    console.log(h); // 1
}

※



※{}

// 一般
let {p, q} = {p: 42, q: true};
console.log(p); // 42
console.log(q); // true 
// p 和 q 必需和 json 裡的 key 一樣
// 因為是使用名稱區分,所以位置調換不會影響結果
// 不能使用如 {,,p}
    
// 取代變數名稱
let {xxx: x, ooo: o} = {xxx:24, ooo:false};
// console.log(xxx); // 報錯,不可再使用原來的 xxx 變數
// console.log(ooo); // 報錯,不可再使用原來的 ooo 變數
console.log(x); // 24
console.log(o); // false
// 由於 xxx 和 ooo 有可能全域就定義過這個變數名稱了,這時會報錯,所以才有這個功能
    
// 巢狀 JSON
let {aaa, bbb: {k2: b}} = {bbb: {k2: 2}, aaa: {k1: 1}};
console.log(aaa); // {k1:1}
console.log(aaa.k1); // 1
console.log(b); // 2
    
// JSON 裡有陣列
let src = {data: {ccc: [1, 2, 3]}};
let {data: {ccc: [, , c]}} = src
console.log(c); // 3
    
// function
function fx({data: {ccc: [, , c]}}) {
    console.log(c); // 3
}
fx(src);
    
// 預設值
let {a = 10, b = 5} = {a: 3};
console.log(a); // 3
console.log(b); // 5
    
// 迴圈
let data = [
    {
        xxx: "x",
        ooo: {
            a: 1,
            b: 2
        },
        zzz:'z'
    },
    {
        xxx: "xx",
        ooo: {
            a: 3,
            b: 4
        },
        zzz:'zz'
    }
];
    
for (let {xxx: s, zzz: d, ooo: {a:n} } of data) {
    console.log('xxx=' + s + ', d=' + d + ", ooo.a=" + n);
}
// xxx=x, d=z, ooo.a=1
// xxx=xx, d=zz, ooo.a=3

※



※()

window.onload = () => {
    ({a, b} = {a:1, b:2}); // 最前面不能加 let var const,{}開頭是一定要有 let var const 的
    console.log(a); // 1
    console.log(b); // 2
    
    ({a, b} = {c:1, d:2});
    console.log(a); // undefined
    console.log(b); // undefined
    // console.log(c); // 報錯
    // console.log(d); // 報錯
    
    ({a, b, ...rest} = {a:1, b:2, c:3, d:4});
    console.log(a, b, rest); // 1 2 {c:3, d:4}
}

※



2017年7月1日 星期六

Call、Apply、Closure

※Call、Apply


※this

window.onload = () => {
    new xxx();
    ooo.o();
    xxx();
    xxx.call();
    xxx.apply();
    xxx.call(ooo);
    xxx.apply(ooo);
    xxx.bind(ooo)();
}
    
let ooo = {
    o:xxx
}
    
function xxx(){
    console.log(this);
}

※結果:


※使用 new 時,this 指的是 new 的物件
JSON 裡的變數指定一個 function,此時 this 指的是這個 function
直接呼叫時,this == window
call 和 apply 沒有參數時,this == window;有參數時,this 指的是傳進去的參數

※最後的 bind 也很類似,但不會馬上執行,所以為了馬上執行,我在最後面加上「()」



※暫時變數

window.onload = () => {
    let o = new Obj(1,2);
    o.fun = plus;
    console.log(o.fun(o.x, o.y));
    delete o.fun;
    
    // console.log(plus.call(o, o.x, o.y));
    // console.log(plus.apply(o, [o.x, o.y]));
    
    // 這樣也可以
    // console.log(plus.call(Obj, 1, 2));
    // console.log(plus.apply(Obj, [1, 2]));
}
    
function plus(p1, p2){
    return p1 + p2;
}
    
function Obj (x, y) {
    this.x = x;
    this.y = y;
    return x * y;
}

※原本的作法比較麻煩,直接使用 call 或 apply ,就不用宣告暫時變數

※call 和 apply 第一個都是放物件,而 call 將要傳進去的參數一個一個放在後面;而 apply 只有二個參數,最後放陣列



※this

window.xxx = 'x';
window.onload = () => {
    window.xxx = 'xx';
    let obj = {xxx:'xxx'};
    
    showXxx.call(window); // xx
    showXxx.call(this); // xx
    showXxx.call(obj); // xxx
    
    showXxx.apply(window); // xx
    showXxx.apply(this); // xx
    showXxx.apply(obj); // xxx
}
    
function showXxx(){
    alert(this.xxx);
}

※要注意如果 onload 裡沒有 window.xxx,會抓更外面的


※還可參考高手寫的文章

※Closure

內層可以呼叫外層的變數,但外層不能呼叫內層的變數,但可以利用閉包來達到類似的功能

let name = 'outside 2';
window.onload = () => {
    let name = 'outside';
    let o = {
        name : 'inside',
        getName:function(){
            // return this.name; // inside
            return function(){
                // return xxx();
                return this.name;
            }
        }
    };
    alert(o.getName()()); // outside
}
    
function xxx(){
    return this.name;
}

※閉包首先要了解變數存活在哪



※累加

let name = 'outside';
function xxx(x) {
    let name = x;
    return function(x){
        name += x;
        return name;
    }
}
    
let o = xxx(5);
alert(o(1)); // 6
alert(o(2)); // 8
alert(o(3)); // 11

※還可以參考高手寫的

2017年6月18日 星期日

四則運算

因為 javascript 算術有誤差,主要是有小數點時,但整數不會,所以做法是先轉成整數後再恢復,但有可能進來的小數點位數不確定,所以寫程動態的方式

※
<script>
'use strict'
    window.onload = () => {
        Number.prototype.plus = function (param){ 
            return accessPlus(this, param); 
        }
    
        Number.prototype.substract = function (param){ 
            return accessSubstract(this, param); 
        }
    
        Number.prototype.multiply = function (param){ 
            return accessMultiple(this, param); 
        }
    
        Number.prototype.divide = function (param){ 
            return accessDivide(this, param); 
        }
    
        /*
         無條件捨去到n位,由於內鍵的Math.floor並沒有小數點的部分,所以加這一支
        */
        Math.floorDecimal = function (value, param){
            let temp = Math.pow(10, param);
            return Math.floor(value * temp)/temp;
        }
    
        // alert(accessDivide(7, 0.35));
        // alert(accessSubstract(n1, n2));
        // alert(accessMultiple(7, 1.35));
        // alert(accessPlus(n1, n2));
    
        let n1 = 7;
        let n2 = 1.35;
        alert(n1.plus(n2));
        alert(n1.substract(n2));
        alert(n1.multiply(n2));
        alert(n1.divide(n2));
        alert(Math.floorDecimal(2.12345679, 6));
    }
    
    let utilArithmetic = (p1, p2) => {
        // 轉字串
        let sp1 = p1.toString();
        let sp2 = p2.toString();
    
        // 小數點後的數字
        let sp1Split = sp1.split(".")[1];
        let sp2Split = sp2.split(".")[1];
    
        // 回傳小數點後的數字長度
        let len1 = typeof(sp1Split) == 'undefined' ? 0 : sp1Split.length;
        let len2 = typeof(sp2Split) == 'undefined' ? 0 : sp2Split.length;
    
        // 將小數點移除
        let np1 = Number(sp1.replace(".", ""));
        let np2 = Number(sp2.replace(".", ""));
    
        return {'len1':len1, 'len2':len2, 'np1':np1, 'np2':np2};
    }
    
    let accessPlus = (p1, p2) => { 
        let json = utilArithmetic(p1, p2);
        let pow = Math.pow(10, Math.max(Number(json.len1), Number(json.len2)));
        return (p1 * pow + p2 * pow) / pow;
    }
    
    let accessSubstract = (p1,p2) => {
        let json = utilArithmetic(p1, p2);
        let max = Math.max(Number(json.len1), Number(json.len2));
        let pow = Math.pow(10, max);
        return ((p1 * pow - p2 * pow) / pow).toFixed(max);
    } 
    
    let accessMultiple = (p1, p2) => {
        let json = utilArithmetic(p1, p2);
        return json.np1 * json.np2 / Math.pow(10, json.len2+json.len1);
    }
    
    let accessDivide = (p1, p2) => {
        let json = utilArithmetic(p1, p2);
        return json.np1 / json.np2 * Math.pow(10, json.len2-json.len1);
    }
</script>

※

2017年6月15日 星期四

Call/Pass by Reference

※Call/Pass by Reference 

Call/Pass by Value:將變數傳進另一隻方法當參數,方法對這個變數所做的事,和原本的沒有關係

Call/Pass by Reference:將變傳進另一隻方法當參數,方法對這個變數所做的事,會影響到原來的

這篇講的是 Call/Pass by Reference

※
<script>
'use strict'
    window.onload = () => {
        var s = new Set();
        zzz(s);
        alert(s.size); // 1
    }
    
    var zzz = (y) => {
        y.add('a');
    }
</script>

※


※new

<script>
'use strict'
    window.onload = () => {
        var s = new Set();
        zzz(s);
        alert(s.size); // 1
    }
    
    var zzz = (y) => {
        y.add('a');
    
        // y = null;
        // y = 'undefined';
        // y = 'xxx';
        y = new Set();
        y.add('b');
        y.add('c');
    }
</script>

※受影響的只在 new 之前,之後自己是獨立的

※如果將 y = null; y = 'undefined';y = 'sss'; 都不會對原來的產生影響

※甚至宣告一個新的 Set,然後賦值給 y 也沒有用

2016年12月24日 星期六

slice、substring、substr 差別

※
let data = '0123456789';
    
console.log('一個參數');
console.log(data.slice(4)); // 456789
console.log(data.substring(4)); // 456789
console.log(data.substr(4)); // 456789
console.log('---------------');
console.log('二個參數-正數');
console.log(data.slice(1, 4)); // 123-->從index 1到index4,但最後的不算,所以是1~3
console.log(data.substring(1, 4)); // 123-->同slice
console.log(data.substr(1, 4)); // 1234-->從index 1開始取4個
console.log('---------------');
console.log('二個參數-負數小~大');
console.log(data.slice(-2, 4)); // 空-->不支援,但也不報錯
console.log(data.substring(-2, 4)); // 0123-->等同(0, 4),也就是不支援負的
console.log(data.substr(-2, 4)); // 89-->index 9也等於-1,所以從8開始取4個,但只取到兩個就結束了
console.log('---------------');
console.log('二個參數-負數大~小');
console.log(data.slice(4, -2)); // 4567-->從index 4開始,-2表示最後兩個不算
console.log(data.substring(4, -2)); // 0123-->等同(-2, 4),但因為不支援負的,所以又等同(0, 4)
console.log(data.substr(4, -2)); // 空-->不支援,但也不報錯

※結果:
一個參數
456789
456789
456789
---------------
二個參數-正數
123
123
1234
---------------
二個參數-負數小~大

0123
89
---------------
二個參數-負數大~小
4567
0123



2016年11月25日 星期五

JS常用功能

W3C英文
W3C中文

※基本

window.onload = function(){
    let input = document.getElementsByTagName('input')[0];
    input.value = 'xxx';
    
    document.getElementById('btn').onclick = function(){
        document.getElementsByName('cb')[2].checked = true;
        document.getElementsByName('ro')[0].checked = true;
    }
    
    document.getElementsByName('cb')[1].defaultChecked = true;
    
    let cls = document.getElementsByClassName('cls')[0];
    let select = document.createElement('select');
    let op1 = document.createElement('option');
    let op2 = document.createElement('option');
    
    select.appendChild(op1);
    select.appendChild(op2);
    
    cls.appendChild(select);
    
    op1.text = 'option1';
    op1.value = 0;
    
    op2.text = 'option2';
    op2.value = 1;
    op2.defaultSelected = true;
}
--------------------
<input /><br />
<button id="btn">click me</button><br />
<input type="checkbox" name="cb" />c1
<input type="checkbox" name="cb" />c2
<input type="checkbox" name="cb" />c3<br />
<div class="cls"></div>
<input type="radio" name="ro" />r1
<input type="radio" name="ro" />r2
<input type="radio" name="ro" />r3<br />

※基本上就是對照上面的網址就很夠用了,只是要特別注意瀏覽器有沒有支援,裡面都有寫,如果是HTML5也有特別標記的圖示

※document.getElement除了Id外,其他都有加s,所以有s還要用[]指定哪一個



※表格

var table = document.getElementById('tab');
table.setAttribute('border', '1');
var tbody = document.createElement('tbody');
var tr = document.createElement('tr');
var td = document.createElement('td');
    
// 上下
var tabBody = table.appendChild(tbody);
var a00 = tabBody.appendChild(tr);
a00.appendChild(td).appendChild(document.createTextNode('00'));
    
table.insertRow().insertCell().appendChild(document.createTextNode('10'));
    
var a20 = table.insertRow();
a20.insertCell().appendChild(document.createTextNode('20'));
    
// 左右
a00.insertCell().appendChild(document.createTextNode('01'));
a20.insertCell().appendChild(document.createTextNode('21'));

※表格要注意有tbody的存在,雖然不寫也可以,但有時會有不如預期的行為

※appendChild只能使用一次,且一定要有一次(除非寫在HTML)

2016年11月22日 星期二

三級聯動

※第一步

const scn = {'south':'北', 'center':'中', 'north':'南'};
let arr = [scn.south, scn.center, scn.north];
arr[scn.south] = ['s1', 's2', 's3'];
arr[scn.center] = ['c1', 'c2'];
arr[scn.north] = ['n1', 'n2', 'n3', 'n4'];
    
arr['s1'] = ['s11', 's12', 's13', 's14', 's15'];
arr['s2'] = ['s21', 's22', 's23', 's24'];
arr['s3'] = ['s31', 's32'];
    
arr['c1'] = ['c11', 'c12'];
arr['c2'] = ['c21', 'c22', 'c23', 'c24'];
    
arr['n1'] = ['n11', 'n12', 'n13'];
arr['n2'] = ['n21', 'n22'];
arr['n3'] = ['n31', 'n32', 'n33'];
arr['n4'] = ['n41', 'n42', 'n43', 'n44', 'n45'];
    
window.onload = function(){
    //alert(arr[0]);//北
    //alert(arr[arr[0]][0]);//s1
    //alert(arr[arr[arr[0]][0]][0]);//s11
    //arr[]裡面包上一層的內容,然後看要顯示第幾個就加[number]
    
    //alert(arr.length); // 3
    //alert(arr[arr[0]].length); // 3
    //alert(arr[arr[arr[0]][0]].length); // 5
    
    //預設為:北/s1/s11
    for(let i=0; i<arr.length; i++){
        let option = new Option();
        option.value = i;
        option.text = arr[i];
        document.getElementById('first').options.add(option);
    }
    
    // arr[arr[0]] = arr[scn.south] = arr['北']
    for(let j=0; j<arr[arr[0]].length; j++){
        let option = new Option(arr[arr[0]][j], j);
        document.getElementById('second').options.add(option);
    }
    
    for(let k=0; k<arr[arr[arr[0]][0]].length; k++){
        let option = new Option(arr[arr[arr[0]][0]][k], k);
        document.getElementById('third').options.add(option);
    }
}
    
function firstChangeSecond(val){
    document.getElementById('second').options.length = 0;
    for(let j=0; j<arr[arr[val]].length; j++){
        let option = new Option(arr[arr[val]][j], j);
        document.getElementById('second').options.add(option);
    }
    
    secondChangeThird(this.getOptionValue('second'));
}
    
function secondChangeThird(val){
    /*
    alert('00=' + arr[arr[arr[0]][0]]);//s11~s15
    alert('10=' + arr[arr[arr[1]][0]]);//c11 c12
    alert('01=' + arr[arr[arr[0]][1]]);//s21~24
    */
    
    let firstSelectValue = this.getOptionValue('first');
    document.getElementById('third').options.length = 0;
    for(let k=0; k<arr[arr[arr[firstSelectValue]][val]].length; k++){
        let option = new Option(arr[arr[arr[firstSelectValue]][val]][k], k);
        document.getElementById('third').options.add(option);
    }
}
    
function getOptionValue(id){
    let ops = document.getElementById(id).options;
    for(let i=0; i<ops.length; i++){
        if(ops[i].selected){
            return i;
        }
    }
}
--------------------
<select id='first' onchange="firstChangeSecond(this.value)"></select>
<select id='second' onchange="secondChangeThird(this.value)"></select>
<select id='third'></select>

※


※第二步,將相同用法提出

window.onload = function(){
    //預設為:北/s1/s11
    changeSelect(arr, 'first');
    changeSelect(arr[arr[0]], 'second');
    changeSelect(arr[arr[arr[0]][0]], 'third');
}
    
function firstChangeSecond(val){
    changeSelect(arr[arr[val]], 'second');
    secondChangeThird(this.getOptionValue('second'));
}
    
function secondChangeThird(val){
    let firstSelectValue = this.getOptionValue('first');
    changeSelect(arr[arr[arr[firstSelectValue]][val]], 'third');
}
    
function getOptionValue(id){
    let ops = document.getElementById(id).options;
    for(let i=0; i<ops.length; i++){
        if(ops[i].selected){
            return i;
        }
    }
}
    
function changeSelect(arr, id){
    document.getElementById(id).options.length = 0;
    for(let i=0; i<arr.length; i++){
        let option = new Option(arr[i], i);
        document.getElementById(id).options.add(option);
    }
}

※

2016年11月21日 星期一

物件導向 (JavaScript 7)

※Object

Object 屬性 方法 繼承

let o = {
    id:5,
    name:function(){
        return "bruce";
    },
    set nid(id){
        this.id = id;
    },
    get nid(){
        return this.id;
    }
};
    
console.log(o.id); // 5
o.nid = 100;
console.log(o.id); // 100
console.log(o.nid); // 100
    
let o_child = Object.create(o); // 繼承
console.log(o_child.nid); // 100

※set get 取名時不能和已有的 key 相同

※set 只能剛好有一個參數; get 不能有參數

※使用["name"]這種方式也行,裡面可以有特殊符號、空格,甚至還可以是變數



set/get 方法名稱為變數

let variable = "nid";
let o = {
    id:5,
    name:function(){
        return "bruce";
    },
    set [variable](id){
        this.id = id;
    },
    get [variable](){
        return this.id;
    }
};
    
console.log(o.id); // 5
o.nid = 100;
console.log(o.id); // 100
console.log(o.nid); // 100
    
let o_child = Object.create(o);
console.log(o_child.nid); // 100

※主要看 o.id 有沒有變化
因為 o.nid 本來就可以在物件外給 key-value,如果 set 沒成功,o.id 和 o_child.id 的值會是 5



var

// var o = {
//     id:6
// };
    
var o = o || {
    id:5,
    name:function(){
        return "bruce";
    },
    set nid(id){
        this.id = id;
    },
    get nid(){
        return this.id;
    }
};
    
console.log(o.id); // 5
o.nid = 100;
console.log(o.nid); // 100
    
let o_child = Object.create(o);
console.log(o_child.nid); // 100

※如果宣告用 var 的話,還可以用 var o = o || {};,表示如果已經有 o 這個變數就用,不然就用空的,但用 const 或 let 都不行



※function

return 物件

function Xxx(){
    this.name = "x";
    // return 10;
    // return {a:1};
}
    
var x = new Xxx();
console.log(x.name);// x
// console.log(new Xxx());// Xxx {name: "x"}
// console.log(x.a);

※寫了 this.xxx 而且不管有沒有寫 return 一般的值,這兩者都可以 new 後直接.屬性/方法

※寫了 this.xxx 而且寫了 return 物件或 funtion,那 new 後直接.屬性和.方法不加「()」會是undefined,但可內部調用



function 屬性 方法 繼承

function X(){
    this.id = 5;
    this["name"] = function() {
        return "bruce";
    },
    this.setId = function(id) {
        this.id = id;
    }
    this.getId = function() {
        return this.id;
    }
}
    
let xxx = new X();
console.log(xxx.id);
xxx.setId(50);
console.log(xxx.getId());
    
function x_child(){}
x_child.prototype = new X(); // prototype 繼承
let x_child2 = Object.create(new X()); // Object 繼承
    
let x_child1 = new x_child();
console.log(x_child1.getId());
console.log(x_child2.getId());

※「.」也可用「[ ]」

※function 有兩種繼承

※如果在 function 內宣告 var 屬性和方法,只能內部調用,外面呼叫都是 undefined



新增/修改屬性、方法

function X(){
    this.id = 5;
    this["name"] = function() {
        return "bruce";
    },
    this.setId = function(id) {
        this.id = id;
    }
    this.getId = function() {
        return this.id;
    }
}
    
X.prototype.method = function(){
    return "method";
}
    
let xxx = new X();
console.log(xxx.method()); // method
    
function x_child(){}
x_child.prototype = new X(); // prototype 繼承
let x_child2 = Object.create(new X()); // Object 繼承
    
let x_child1 = new x_child();
console.log(x_child1.method()); // method
console.log(x_child2.method()); // method

※可以在 function 外直接新增,使用 function名稱.prototype.方法或屬性即可

※注意寫在 function 外的屬性和方法可以一直覆蓋下去,但本來就在 function 裡的屬性和方法,並不會覆蓋,而且也不會報錯

※宣告的 prototype 上面可加個 if 判斷,如 if(!X.prototype.method),沒有的才做,可以避開瀏覽器不支援的情形



一次新增多個屬性、方法

function X(){
    this.id = 5;
    this["name"] = function() {
        return "bruce";
    },
    this.setId = function(id) {
        this.id = id;
    }
    this.getId = function() {
        return this.id;
    }
}
    
// X.prototype.method1 = function(){
//     return "method1";
// }
    
// X.prototype.method2 = function(){
//     return "method2";
// }
    
X.prototype = {
    method1 : function(){
        return "method1";
    },
    method2 : function(){
        return "method2";
    },
};
    
let x = new X();
console.log(x.method1()); // method1
console.log(x.method2()); // method2

※當然還是會後者蓋前者



※class

class 繼承

class SonDate extends Date {
    xxx() {
        return "xxx";
    }
}
    
let son = new SonDate();
console.log(son.getDate());
console.log(son.xxx());

※由於繼承了內鍵的 Date,所以可以用它的方法

※也可以繼承自己寫的 class

※只能在方法裡寫變數(this.xxx、var xxx 都一樣)



class getter/setter constructor

class SonDate {
    xxx(){
        return this.id + "-" + this.name;
    }
    
    constructor(id, name) {
        this.id = id;
        this.name = name;
    }
    
    get abc(){
        return this.id;
    }
    
    set abc(id){
        this.id = id;
    }
}
    
let son = new SonDate(7, "mary");
console.log(son.xxx()); // 7-mary
console.log(son.abc); // 7
son.abc = 50;
console.log(son.abc); // 50

※set get 取名時不能和屬性相同

※如果有繼承,而且寫了 constructor ,那第一行就一定要寫 super(),當然參數就看繼承誰了

※最多只能有一個 constructor



set/get 方法名稱為變數

let variable = "abc";
    
class SonDate {
    xxx(){
        return this.id + "-" + this.name;
    }
    
    constructor(id, name) {
        this.id = id;
        this.name = name;
    }
    
    get [variable](){
        return this.id;
    }
    
    set [variable](id){
        this.id = id;
    }
}
    
let son = new SonDate(7, "mary");
console.log(son.xxx()); // 7-mary
console.log(son.abc); // 7
son.abc = 50;
console.log(son.abc); // 50

※主要看第二個 son.abc,失敗的話是 undefined



static

class SonDate {
    static method() {
        return SonDate.xxx();
    }
    
    static xxx() {
        return "x1";
    }
    
    xxx() {
        return "x2";
    }
}
    
console.log(SonDate.method()); // x1

※static 呼叫 static,要用「類.static 方法名」(java直接呼叫 static 方法名);
呼叫 non static 用 new

※static 不能寫屬性

※如果有繼承,還可用 super.父類方法名

2016年10月25日 星期二

日期加減、星期

活用setXXX和getXXX即可,連潤年也都可以解決,以下還加上民國年的轉換
但檢核沒什麼判斷

※日期加減(使用API)

window.onload = function(){
    mingoDateStringAddSubstract('105/10/30', '/', 'm', -2);
    mingoDateStringAddSubstract('1051030', '', 'm', -2);
}
    
/*
    mingoDateString:民國年月日字串
    split:要分割的符號,可以為空
    ymd:要針對年、月、日作用
    number:加減年、月、日數
*/
function mingoDateStringAddSubstract(mingoDateString, split, ymd, number){
    if(mingoDateString.length != 7 && mingoDateString.length != 9){
        return 'yyymmdd or yyy?mm?dd';
    }
    
    var year, month, date;
    split = split.trim();
    if(split != ''){
        var dateArray = mingoDateString.split(split);
        year = dateArray[0];
        month = dateArray[1];
        date = dateArray[2];
    } else {
        year = mingoDateString.substring(0, 3);
        month = mingoDateString.substring(3, 5);
        date = mingoDateString.substring(5);
    }
    
    // 月-1是因為月是從0開始
    var d = new Date(Number(year) + 1911, Number(month) - 1, date);
    console.log('before=' + d.toDateString());
    if('y' == ymd.toLowerCase()){
        d.setYear(d.getFullYear() + number);
    } else if('m' == ymd.toLowerCase()){
        d.setMonth(d.getMonth() + number);
    } else if('d' == ymd.toLowerCase()){
        d.setDate(d.getDate() + number);
    }
    console.log('after=' + d.toDateString());
    
    // 轉民國年,月+1是因為月是從0開始
    var rtnYear = Number(d.getFullYear()) - 1911;
    var rtnMonth = Number(d.getMonth()) + 1;
    var rtnDate = d.getDate();
    
    if(year.length == 2){
        year = '0' + year;
    } else if(year.length == 1){
        year = '00' + year;
    }
    rtnMonth = rtnMonth < 10 ? '0' + rtnMonth : rtnMonth;
    rtnDate = rtnDate < 10 ? '0' + rtnDate : rtnDate;
    
    console.log(rtnYear + split + rtnMonth + split + rtnDate);
    console.log('==========');
}

※

※日期加減(不用API)

window.onload = function(){
    console.log(mingoDateCalc('105/10/30', '/', 'm', -2));
    console.log(mingoDateCalc('1051030', '', 'm', -2));
}
    
// 是否閏年
function isLeapYear(year){
    /* 網路上找到的閏年判斷邏輯  
    4的倍數-->非100的倍數-->閏
           -->  100的倍數-->非400的倍數-->非閏
                         -->  400的倍數-->閏
    y/4 == 0 && y/100 != 0 
    y/4 == 0 && y/100 == 0 && y/400 == 0 
    
    */
    
    /*
    if(
        (year % 4 == 0 && year % 100 != 0) || 
        (year % 4 == 0 && year % 100 == 0 && year % 400 == 0)
    ){
        return true;
    } else {
        return false;
    }
    */
    
    // 簡化後
    if(year % 4 == 0){
        if(year % 100 != 0){
            return true;
        } else {
            return year % 400 == 0 ? true : false;
        }
    } else {
        return false;
    }
}
    
// 回傳當月天數
function judgeDateOfMonth(year, month){
    switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30
            break;
        default:
            if(isLeapYear(Number(year) + 1911)){
                return 29;
            } else {
                return 28;
            }
    }
}
    
function mingoDateCalc(mingoDateString, split, ymd, number){
    if(mingoDateString.length != 7 && mingoDateString.length != 9){
        return 'yyymmdd or yyy?mm?dd';
    }
    
    var year, month, date;
    split = split.trim();
    if(split != ''){
        var dateArray = mingoDateString.split(split);
        year = Number(dateArray[0]);
        month = Number(dateArray[1]);
        date = Number(dateArray[2]);
    } else {
        year = Number(mingoDateString.substring(0, 3));
        month = Number(mingoDateString.substring(3, 5));
        date = Number(mingoDateString.substring(5));
    }
    
    if(ymd == 'y'){
        year = year + number;
        if(month == 2 && date == 29){
            // 非潤年,加1或減1看需求而定(API是加1)
            if(number > 0 && !isLeapYear(Number(year) + 1911)){
                // 減1
                //date = date - 1;
    
                // 加1
                month = 3;
                date = 1;
            }
        }
    } else if(ymd == 'm'){
        if(number > 0) {
            year = year + Math.floor(number / 12);
            month = month + (number % 12);
            if(month > 12){
                year = year + 1;
                month = month - 12;
            }
        } else {
            number = Math.abs(number);
            year = year - Math.floor(number / 12);
            month = month - (number % 12);
            if(month <= 0){
                year = year - 1;
                month = month + 12;
            }
        }
    } else if(ymd == 'd'){
        if(number > 0) {
            date = date + number;
            var ymdArray = calcPlusDate(year, month, date);
            year = ymdArray[0];
            month = ymdArray[1];
            date = ymdArray[2];
        } else {
            date = date + number;
    
            // 一開始要判斷上個月的天數,所以先扣一個月
            month = month - 1;
            if(month == 0){
                year = year - 1;
                month = 12;
            }
            var ymdArray = calcMinusDate(year, month, date);
            year = ymdArray[0];
            month = ymdArray[1];
            date = ymdArray[2];
        }
    }
    
    if(year.length == 2){
        year = '0' + year;
    } else if(year.length == 1){
        year = '00' + year;
    }
    month = month < 10 ? '0' + month : month;
    date = date < 10 ? '0' + date : date;
    return year + split + month + split + date;
}
    
// 計算天數為正
function calcPlusDate(y, m, d){
    var day = judgeDateOfMonth(y, m);
    if(d > day){
        d = d - day;
        m = m + 1;
        if(m == 13){
            y = y + 1;
            m = 1;
        }
        return calcPlusDate(y, m, d);
    }
    return new Array(y, m, d);
}
    
// 計算天數為負
function calcMinusDate(y, m, d){
    var day = judgeDateOfMonth(y, m);
    if(d < 0){
        d = d + day;
        m = m - 1;
        if(m == 0){
            y = y - 1;
            m = 12;
        }
        return calcMinusDate(y, m, d);
    }
    
    // 一開始先扣了一個月,這裡要加回來
    m = m + 1;
    if(m == 13){
        y = y + 1;
        m = 1;
    }
    return new Array(y, m, d);
}

※雖然沒有測很仔細,但應該和API一樣

※檢核可用正則表達式加強一下



※星期

※API有getDay()可取得星期,自己寫的話就要設定一個寫死的日期是星期幾,然後根據這一個來判斷,如下:

window.onload = function(){
    var y = 2011; var m = 11; var d = 30;
    
    // 兩個日期相差的天數,使用new Date API
    var d1 = new Date(2012, 1 - 1, 1);//月減1是因為API是從0開始
    var d2 = new Date(y, m - 1, d);
    var oneDay = 60 * 60 * 24;
    console.log('用API算相差的天數=' + Math.abs((d1-d2) / (oneDay * 1000)));
    
    apiWeek(y, m, d);
    noApiWeek(y, m, d);
}
    
function apiWeek(year, month, date) {
    // 使用API
    // 月-1是因為月是從0開始
    var d = new Date(year, month - 1, date);
    console.log('用API算出的日期=' + d.getDay());// 0為星期日
}
    
function noApiWeek(year, month, date) {
    // 不使用API
    // 2012/1/1 星期日
    // 2010 12 13 星期一
    var baseYear = 2012;
    var baseMonth = 1;
    var totalDay = 0;
    
    //月日一定>=1,所以不用判斷
    if(year >= baseYear){
        totalDay = date;
        while(true){
            if(baseMonth > 12){
                baseYear = baseYear + 1;
                baseMonth = baseMonth - 12;
            }
    
            if(baseYear == year && baseMonth == month){
                break;
            }
    
            totalDay = totalDay + judgeDateOfMonth(baseYear, baseMonth);
            baseMonth = baseMonth + 1;
        }
        totalDay = Math.abs(totalDay - 1);
        console.log('不用API算相差的天數=' + totalDay);
        getPositiveWeek(totalDay % 7);
    } else {
        // 判斷上個月的日期
        baseMonth = 12;
        baseYear = 2011;
    
        var lastMonthDay = judgeDateOfMonth(baseYear, baseMonth);
        totalDay = Math.abs(lastMonthDay - date + 1); 
    
        while(true){
            if(baseYear == year && baseMonth == month){
                break;
            }
    
            baseMonth = baseMonth - 1;
            if(baseMonth == 0){
                baseYear = baseYear - 1;
                baseMonth = 12;
            }
            totalDay = totalDay + judgeDateOfMonth(baseYear, baseMonth);
        }
        console.log('不用API算相差的天數=' + totalDay);
        getNegativeWeek(totalDay % 7);
    }
}
    
// 是否閏年
function isLeapYear(year){
    if(year % 4 == 0){
        if(year % 100 != 0){
            return true;
        } else {
            return year % 400 == 0 ? true : false;
        }
    } else {
        return false;
    }
}
    
function getPositiveWeek(weekNumber){
    var display = '不用API算出的日期=星期';
    switch(weekNumber) {
        case 0:
            console.log(display + '日');
            break;
        case 1:
            console.log(display + '一');
            break;
        case 2:
            console.log(display + '二');
            break;
        case 3:
            console.log(display + '三');
            break;
        case 4:
            console.log(display + '四');
            break;
        case 5:
            console.log(display + '五');
            break;
        case 6:
            console.log(display + '六');
            break;
    }
}
    
function getNegativeWeek(weekNumber){
    var display = '不用API算出的日期=星期';
    switch(weekNumber) {
        case 0:
            console.log(display + '日');
            break;
        case 1:
            console.log(display + '六');
            break;
        case 2:
            console.log(display + '五');
            break;
        case 3:
            console.log(display + '四');
            break;
        case 4:
            console.log(display + '三');
            break;
        case 5:
            console.log(display + '二');
            break;
        case 6:
            console.log(display + '一');
            break;
    }
}
    
// 回傳當月天數
function judgeDateOfMonth(year, month){
    switch(month) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            return 31
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            return 30
            break;
        default:
            if(isLeapYear(year)){
                return 29;
            } else {
                return 28;
            }
    }
}

※已2012/1/1為基準,我故意選星期天為0的,因為這樣比這個日期大的日期,結果會和API一樣

※回傳當月天數和日期加減的程式碼稍有不同,因為不用民國年了,所以將+1911拿掉了

2016年7月18日 星期一

集合、Iterator (JavaScript 6)

※集合

和java的Map和Set差不多,但方法名不同
Map的key不重覆,後者會蓋前者; Set塞相同的資料,長度不會變長
沒有List,可能已經有Array了吧!

// map
var map = new Map();
map.set("a", "1");
map.set("b", "2");
map.set("c", "3");
console.log(map.size); // 3
console.log(map.get("fox")); // undefined
console.log(map.get("b")); // 2
console.log(map.has("b")); // true
map.delete("b");
console.log(map.has("b")); // false
    
for(let m of map) console.log(m); // ["a", "1"] ["c", "3"]
map.set("a", 10);
map.forEach(function(e, i, a){
    console.log(e); // 10 3
});
    
    
// set
var set = new Set();
set.add(1);
set.add("a");
set.add("b");
    
console.log(set.size); // 3
console.log(set.has(1)); // true
set.delete(1);
console.log(set.has(1)); // false
    
for (let s of set) console.log(s); // a b
console.log(set.size); // 2
set.add("a");
console.log(set.size); // 2
    
set.forEach(function(e, i, a){
    console.log(e); // a b
});

※除了Map 和 Set 外,還有 WeakMap 和 WeakSet
WeakMap 的 key 只能塞物件,Map 無限制
WeakSet 只能放物件,Set 無限制



※ Iterator


※Symbol.iterator

var xxx = {}; // []也可以
xxx[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
console.log([...xxx]); // [1, 2, 3]

※「*」一定要,可以不和function連在一起

※ Symbol.iterator是內鍵的,一個字都不能改,有這個才能用「...變數」

※yield就類似return,但它可以一直接起來

※沒辦法用lambda的寫法


※function*的yield、yield*和next()

function* gen() {
    yield* ["a", "b", "c"];
}
    
const g = gen();
console.log(g.next()); // { value:"a", done:false }
console.log(g.next()); // { value:"b", done:false }
console.log(g.next()); // { value:"c", done:false }
console.log(g.next()); // { value:undefined, done:true }
    
// 一次宣告多個變數和值
var [a, b, c] = new Set(["aa", "bb", "cc"]);
console.log(a); // "aa"
console.log(b); // "bb"

※回傳的done是內鍵的,表示完成了沒

※如果yield不加「*」,那結果是{value: Array[3], done: false},會將整個陣列裝起來,後面如果再呼叫next(),結果都是{value: undefined, done: true}

※最後的宣告改成Array,如var [a, b, c] = new Array(["aa", "bb", "cc"]);,
結果a會變成["aa", "bb", "cc"],後面的b和c都會變成undefined


※function的return

function* gen() { 
    yield 1;
    yield 2;
    yield 3;
}
    
var g = gen();
console.log(g.next());        // { value: 1, done: false }
console.log(g.next());        // { value: 2, done: false }
console.log(g.return("xxx")); // { value: "xxx", done: true }
console.log(g.next());        // { value: undefined, done: true }

※只要一return,後面的value就沒有了,done也變成true了,也就是說一return就算完成了

2016年7月16日 星期六

型態、迴圈 (JavaScript 5)

※七種型態

Boolean
null
undefined
Number
String
Symbol: ECMAScript 6 中新增,實體是唯一且不可變的
Object


※有new 和沒new的差別

var n = new Number(5);
console.log('n=' + n + ',tn=' + typeof n); // object
    
var n2 = 5;
console.log('n2=' + n2 + ',tn2=' + typeof n2); // number
    
var s = new String('a');
console.log('s=' + s + ',ts=' + typeof s); // object
    
var s2 = 'a';
console.log('s2=' + s2 + ',ts2=' + typeof s2); // string
    
var b = new Boolean(true);
console.log('b=' + b + ',tb=' + typeof b); // object
    
var b2 = true;
console.log('b2=' + b2 + ',tb2=' + typeof b2); // boolean
    
var nil = null;
console.log('nil=' + nil + ',tnil=' + typeof nil); // object
    
var u = undefined;
console.log('u=' + u + ',tu=' + typeof u); // undefined
    
var f = new Function();
console.log('f=' + f + ',tf=' + typeof f); // 看下面的圖
    
var f2 = function(){};
console.log('f2=' + f2 + ',tf2=' + typeof f2); // 看下面的圖

※結果:

※除了function外,有new的型態都是object

※字串用「'」和「"」都可以


※型態要注意的事

// var b = new Boolean(false);
var b = false;
console.log('b1=' + b);// false
console.log('b2=' + b ? 'true':'false'); // true
console.log('b3=' + b == true ? 'true':'false');// false
    
    
// null 和 undefined 區別
console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(1 + null); // 1
console.log(1 + undefined); // NaN
    
    
// 數字
var n = .1;
console.log(n); // 0.1
console.log(typeof n); // number
    
    
// 字串+數字和數字+字串
var a = '1';
var b = 2;
console.log(a + b); // 12
console.log(b + a); // 21

※要注要boolean用來判斷時的寫法,如判斷b而已,它會認為有東西就是true

※null 可以加減;undefined 不行

※數字用「.」開頭,會幫你加上0

※使用 typeof 關鍵字可顯示型態

※字串+數字和數字+字串,較舊的瀏覽器有可能變成3,我用IE11和Google51結果都一樣


※六種迴圈

// 一、do...while
var i = 0;
outer:
do {
    console.log(++i);
    if (i == 3){
        break outer;
    } else {
        continue outer;
    }
} while (i < 5);
    
    
// 二、while
var i = 0;
outer:
while (i < 5) {
    console.log(++i);
    if (i == 3){
        break outer;
    } else {
        continue outer;
    }
}
    
    
// 三、for
outer:
for(var i=1; i<5; i++){
    console.log(i);
    if (i == 3){
        break outer;
    } else {
        continue outer;
    }
}
    
    
// 四、for in
var obj = ['a', 2, 'c', 4, 'e'];
outer:
for (var i in obj) {
    console.log(i);      // 0 1 2 3
    console.log(obj[i]); // a 2 c 4
    if (i == 3){
        break outer;
    } else {
        continue outer;
    }
}
    
    
// 五、for of
var obj = ['a', 2, 'c', 4, 'e'];
for (var i of obj) {
    console.log(i); // a 2 c 4 e
}
    
    
// for in 和 for of 區別
var arr = ['a', 2, 'c'];
arr.six = 6;
for (let i in arr) {
    console.log(arr[i]); // a 2 c 6
}
    
for (let i of arr) {
    console.log(i); // a 2 c
}
    
    
// 六、forEach
var obj = ['a', 2, 'c'];
obj.forEach(xxx);
function xxx(element, index, array) {
    console.log('element=' + element); // a 2 c
    console.log('index=' + index); // 0 1 2
    console.log('array=' + array); // 都是a,2,c
}
/* 匿名寫法
obj.forEach(function (element, index, array) {
    console.log('element=' + element); // a 2 c
    console.log('index=' + index); // 0 1 2
    console.log('array=' + array); // 都是a,2,c
});
*/
/* lambda寫法
obj.forEach((element, index, array) => {
    console.log('element=' + element); // a 2 c
    console.log('index=' + index); // 0 1 2
    console.log('array=' + array); // 都是a,2,c
});
*/

※for in和for of的區別是在裡面宣告變數時,用for in是index;而for of是值
還有上面的陣列,後面給的值,for of 不會抓
for of 是 ECMAScript 6新增的

※這六種迴圈,只有forEach沒有標籤的寫法和break、continue,但可以用return true/false代替break、continue

※forEach不能呼叫var xxx = 的function

宣告變數 (JavaScript 4)

宣告變數有3個關鍵字
var:全域變數
const:唯讀的,和java的final不太一樣,java的可以給一次,有值就不能改了;const都不能改
let:區域變數
※不寫以上三個也是全域變數,但有點不太一樣,下面有例子


※var和不寫var一樣的情況

bla = 2;
console.log(bla); // 2
var bla;
console.log(bla); // 2


※var和不寫var不一樣的情況1

var a = 1;
console.log('a=' + a); // 1
delete this.a;
console.log('a=' + a); // 1
    
b = 2;
console.log('b=' + b); // 2
delete this.b;
console.log('b=' + b); // 找不到b

※const和let的結果和寫var一樣,delete沒有作用

※不寫this也是一樣的結果


※var和不寫var不一樣的情況2

var xxx = "global";
var test = function () {
    console.log(xxx); // 這行會因為下行而有變化
    var xxx = "local"; // 有沒有加var有差
    console.log(xxx);
}
console.log(xxx);
    
var test2 = function () {
    console.log(xxx);
}
----------
<button onclick="test()">t1</button>
<button onclick="test2()">t2</button>

※此例test()和test2原本都是global,但test裡也宣告了xxx變數,所以test第一次呼叫時會變成undefined

※沒寫var會變成全域變數,第3行是global,這個和大部分的人想的一樣


※var變成區域變數

var x = 0;
function f(){
    // var z = y = x = 1; // z未定義
    var x = y = z = 1; // x是區域,y是全域
    console.log(x, y, z); // 1 1 1
}
f();
console.log(x, y, z); // 0 1 1

※宣告多個變數為一行時,最左邊的在全域變數找的到才不會出錯


※let和var的差異

var a = 1;
var b = 2;
    
if (a === 1) {
    var a = 11; // 區域變數a,如果全域也有變數a,不管有沒有加var,都會覆蓋
    let b = 22; // 區域變數b,只活在if裡
    
    console.log(a);  // 11
    console.log(b);  // 22
}
console.log(a); // 11
console.log(b); // 2


※const 須注意

const c = {"key": "value"};
console.log(c.key); // value
c.key = 'v';
console.log(c.key); // v
c = {'c':'ccc'}; // 錯誤

※如果宣告成JSON的格式,值還是可以改的;但key不行


※取值須注意

var a = { b: {a: "ooo", "b": "xxx"}, 7: "zzz" };
console.log(a.b.b); // xxx
console.log(a.b['b']); // xxx
console.log(a["b"].b); // xxx
console.log(a["b"]['b']); // xxx
console.log(a[7]); // zzz
// console.log(a.7); // 語法錯誤,數字不要點
    
var t = {
    "": "empty",
    "!": "exclamation"
}
// console.log(t.""); // 不可預期的錯誤
console.log(t[""]); // empty
// console.log(t.!); // 不可預期的錯誤
console.log(t["!"]); // exclamation

※上半部沒什麼問題,下半部要注意key為空和一些特殊符號的情形

2016年7月15日 星期五

debug、版本 (JavaScript 3)

※debug

window.onload = function() {
    try {
        alrt('xxx');
    } catch(e) {
        if(e.description != null){
            alert(e.description);
        } else {
            alert(e.message);
        }
    } finally {
        console.log('一定會執行!');
    }
};

※使用alert、console.log、try…catch…finally三種方法

※try...finally也是可以的,不一定要catch,和java一樣

※console.log要按F12(大部分的瀏覽器都是這樣),再按一次可復原

※上面是IE11,下面是Chrome



※javascript版本

以下的程式碼是測試版本用的
javascript的介面是ECMA script,要對應到它的版本會比較準確,也就是有此新方法要在高版本才有,IE我用11版,還是1.3
但還是有很多試的結果不一樣的地方,如下一篇的const,我就試過有些瀏覽器居然還可以改它的值,也不會出錯
<head>
    <script language="javascript1.1">
        var ver = 1.1;
    </script>
    <script language="javascript1.2">
        var ver = 1.2;
    </script>
    <script language="javascript1.3">
        var ver = 1.3;
    </script>
    <script language="javascript1.4">
        var ver = 1.4;
    </script>
    <script language="javascript1.5">
        var ver = 1.5;
    </script>
    <script language="javascript1.6">
        var ver = 1.6;
    </script>
    <script language="javascript1.7">
        var ver = 1.7;
    </script>
    <script language="javascript1.8">
        var ver = 1.8;
    </script>
    <script language="javascript1.8.1">
        var ver = 1.8.1;
    </script>
    <script language="javascript1.8.2">
        var ver = 1.8.2;
    </script>
    <script language="javascript1.8.5">
        var ver = 1.8.5;
    </script>
</head>
    
<body>
    <script>document.write(ver)</script>
</body>

2016年6月22日 星期三

開新視窗與回傳值

此篇介紹open、opener 和 showModalDialog

※open、opener


使用open、opener,主流的瀏覽器都有支援

※dad.html

function openWindow(){  
    window.open('son.html','_blank','height=400,width=400');  
}
----------
<form name="dadForm">
    <table>
        <tr>
            <td>
                <input type="button" value="開新視窗" onclick="openWindow()">
                <input name="ooo" value="999" />
                <input name="rtn" disabled="disabled" />
            </td>
        </tr>
    </table>
</form>

※W3C文件有寫open有四個參數,第三個參數有很多,我只寫兩個,可以參考看看


※son.html

function closeWindow(){
    alert(document.sonForm.xxx.value);
    alert(window.opener.document.dadForm.ooo.value);
    // 將xxx的value給父視窗的rtn
    window.opener.document.dadForm.rtn.value = document.sonForm.xxx.value;
    window.close();
}
----------
<form name="sonForm">
    <table>
        <tr>
            <td><input name="xxx" value="20" /></td>
        </tr>
        <tr>
            <td>
                <input type="button" value="確認" onclick="closeWindow()">
            </td>
        </tr>
    </table>
</form>

※使用window.opener開頭可以呼叫父視窗的資料

※通常還有兩個事件比較常用
onunload:按右上角的X鈕會觸發
onbeforeunload:離開頁面就會觸發
注意呼叫window.close()會關閉視窗,關閉前會呼叫onbeforeunload方法,但onunload沒有呼叫到
按右上角的X兩個都會呼叫到

window.onunload = function(){
    // ...
}
    
window.onbeforeunload = function(){
    // ...
}

※內容還是可以用window.opener開頭呼叫爸爸的一些有的沒的

※其他事件可參考mozilla官網的WindowEventHandlers教學



※showModalDialog

IE有支援
參考資源

※和open、opener的差別主要有兩個

1.使用showModalDialog不能按右鍵
2.使用showModalDialog會將父視窗凍結起來

※dad.html

function modalDialog(){
    var rtn = window.showModalDialog('son.html', document.dadForm.ooo.value,'dialogHeight=400,dialogWidth=400');
    alert('return=' + rtn);
}
----------
<form name="dadForm">
    <table>
        <tr>
            <td>
                <input type="button" value="開新視窗" onclick="modalDialog()">
                <input name="ooo" value="999" />
                <input name="rtn" disabled="disabled" />
            </td>
        </tr>
    </table>
</form>

※第一個參數是必填,其他都是選填

※變數可以接收子視窗的回傳值

※第二個參數可以傳陣列,這樣就可以傳多個值了


※son.html

window.onload = function(){
    alert(window.dialogArguments);
}
    
function closeWindow(){
    alert(document.sonForm.xxx.value);
    // 將xxx的value給父視窗的rtn
    window.returnValue = document.sonForm.xxx.value;
    window.close();
}
----------
<form name="sonForm">
    <table>
        <tr>
            <td><input name="xxx" value="20" /></td>
        </tr>
        <tr>
            <td>
                <input type="button" value="確認" onclick="closeWindow()">
            </td>
        </tr>
    </table>
</form>

※window.dialogArguments可以接受父視窗的參數

※window.returnValue可以將值回傳給父視窗

※類似像showModalDialog的,我提供的超連結還有兩個showModelessDialog、show,這我沒研究,因為主流瀏覽器不是全部支援,應該是大同小異吧!