2019年3月14日 星期四

堆疊樹

// 堆疊樹(由下往上)

// (1~n) m 的子節點為 2m, 2m+1
// 階層從 1階 開始
// 每階最多 (2(level-1)次方) 個節點
let data = [26, 5, 19, 1, 61, 11, 59, 15, 48, 77];

// 最後一個有子節點的節點
let lastParentIndex = Math.floor(data.length / 2) - 1;

console.log(JSON.stringify(data));
// 往前拜訪每個有孩子的節點
for (let i = lastParentIndex; i >= 0; i--) {
    debugger;
    check(i, data);
}
console.log(JSON.stringify(data));

//----------------------------
// 檢查父節點與子節點的大小
function check(parent_index, _data) {
    debugger;

    // 會不斷往下有子節點的節點探索
    while (parent_index <= lastParentIndex) {

        let leftIndex = (parent_index + 1) * 2 - 1;
        let rightIndex = leftIndex + 1;

        // 可能沒有 right 子節點
        rightIndex = (rightIndex >= _data.length) ? null : rightIndex;

        // console.log('%d = %d',  _data[leftIndex], _data[rightIndex]);

        let maxIndex = (rightIndex == null || (_data[leftIndex] > _data[rightIndex])) ? leftIndex : rightIndex;

        if (_data[parent_index] < _data[maxIndex]) {
            // 交換
            let temp = _data[maxIndex];
            _data[maxIndex] = _data[parent_index];
            _data[parent_index] = temp;
        }

        // 往下層比較
        // 確定變動的節點要比他的子節點大
        parent_index = maxIndex;
    }
}
------------------------------------------------------------------------
// 堆疊樹(由上往下)

// (1~n) m 的子節點為 2m, 2m+1
// 階層從 1階 開始
// 每階最多 (2(level-1)次方) 個節點
let data = [26, 5, 19, 1, 61, 11, 59, 15, 48, 77];

console.log(JSON.stringify(data));
// 往前拜訪每個有孩子的節點
for (let i = 0; i < data.length; i++) {
    debugger;
    check(i, data);
}
console.log(JSON.stringify(data));

//----------------------------
// 檢查父節點與子節點的大小
function check(parent_index, _data) {
    debugger;

    let prev_index;

    // 會不斷往下有子節點的節點探索
    while (parent_index >= 0) {


        if (prev_index != null) {
            // 回朔 只需比對上一個子節點
            if (_data[parent_index] < _data[prev_index]) {
                // 交換
                let temp = _data[prev_index];
                _data[prev_index] = _data[parent_index];
                _data[parent_index] = temp;
            }

        } else {
            // 第一次進入

            let leftIndex = (parent_index + 1) * 2 - 1;
            let rightIndex = leftIndex + 1;

            leftIndex = (leftIndex >= _data.length) ? null : leftIndex;
            rightIndex = (rightIndex >= _data.length) ? null : rightIndex;


            if (leftIndex == null && rightIndex == null) {
                // 都沒有子節點
                return;
            }

            console.log('%d(%d,%d)', _data[parent_index], _data[leftIndex], _data[rightIndex]);

            let maxIndex = (rightIndex == null || (_data[leftIndex] > _data[rightIndex])) ? leftIndex : rightIndex;

            if (_data[parent_index] < _data[maxIndex]) {
                // 交換
                let temp = _data[maxIndex];
                _data[maxIndex] = _data[parent_index];
                _data[parent_index] = temp;
            }
        }
        //-----------------------
        prev_index = parent_index;
        // 往下層比較
        // 確定變動的節點要比他的子節點大
        let nextCheckIndex = Math.floor((parent_index + 1) / 2) - 1;

        if (nextCheckIndex == parent_index && parent_index == 0) {
            // 只為了解決無限迴圈
            return;
        }

        // 往上
        parent_index = nextCheckIndex;
    }
}

2019年3月13日 星期三

DoublyLinkedList

module.exports = DoublyLinkedList;


// 雙向連結
function DoublyLinkedList() {

    let head;
    let tail;
    let length;

    this._head = function (node) {
        if (node == null) {
            return head;
        } else {
            head = node;
        }
    };

    this._tail = function (node) {
        if (node == null) {
            return tail;
        } else {
            tail = node;
        }
    };

    this._length = function (_length) {
        if (_length == null) {
            return length;
        } else {
            length = _length;
        }
    };

}

(function () {
    this.append = function (el) {
        debugger;

        let node = new Node(el);

        if (this._head() == null) {
            this._head(node);

            this._length(1);
            this._tail(node);

        } else {
            let prev_node = this._head();
            let count = 0;

            while (prev_node.next != null) {
                prev_node = prev_node.next;
                count++;
            }

            prev_node.next = node;
            node.prev = prev_node;

            this._length(++count);
            this._tail(prev_node);
        }

    };

    this.insert = function (position, el) {

    };

    this.remove = function (el) {

    };

    this.removeAt = function (position) {

    };

    this.indexOf = function (position) {

    };

    this.size = function () {
        return this._length();
    };

    this.toJSON = function () {
        debugger;
        let res = [];
        let node = this._head();

        do {
            if (node == null) {
                break;
            } else {
                res.push(JSON.stringify(node.element));
            }
        } while ((node = node.next) != null);

        return res.join(',');
    };

}).call(DoublyLinkedList.prototype);


//==============================================================================

function Node(el) {
    this.element = el;
    this.prev = null;
    this.next = null;
}

2019年3月12日 星期二

javascript array.sort()

如果 compareFunction 沒有被應用,元素將被轉換為字串並以 Unicode 編碼位置進行比較來排序。舉例來說,"Banana" 會被排在 "cherry" 之前。在數值排序中,9 排在 80 前面,但因為數字被轉換成字串,在 Unicode 順序中 "80" 會在 "9" 的前面。
如果 compareFunction 被應用,陣列元素們將根據比較函式之回傳值來排序。如果 ab 為被比較之兩元素,則:
  • compareFunction(a, b) 的回傳值小於 0,則會把 a 排在小於 b 之索引的位置,即 a 排在 b 前面。
  • compareFunction(a, b) 回傳 0,則 ab 皆不會改變彼此的順序,但會與其他全部的元素比較來排序。備註:ECMAscript 標準並不保證這個行為,因此不是所有瀏覽器(如 Mozilla 版本在 2003 以前)都遵守此行為。
  • compareFunction(a, b) 的回傳值大於 0,則會把 b 排在小於 a 之索引的位置,即 b 排在 a 前面。
  • compareFunction(a, b) 在給予一組特定元素 a 及 b 為此函數之兩引數時必須總是回傳相同的值。若回傳值不一致,排序順序則為 undefined。



sort(a, b) 預設是從小排到大

 compareFunction(a, b) > 0 認為 (a - b > 0) 所以 a 與 b 交換

2019年3月8日 星期五

一個 promise 再包裝的例子



'use strict';

/**
 * Promise
 *
 * Inspired by https://gist.github.com/RubaXa/8501359 from RubaXa <trash@rubaxa.org>
 *
 * @param {Function} handler   Called as handler(resolve: Function, reject: Function)
 * @param {Promise} [parent]   Parent promise for propagation of cancel and timeout
 */
function Promise(handler, parent) {
    var me = this;

    if (!(this instanceof Promise)) {
        throw new SyntaxError('Constructor must be called with the new operator');
    }

    if (typeof handler !== 'function') {
        throw new SyntaxError('Function parameter handler(resolve, reject) missing');
    }

    var _onSuccess = [];
    var _onFail = [];

    // status
    this.resolved = false;
    this.rejected = false;
    this.pending = true;

    /**
     * Process onSuccess and onFail callbacks: add them to the queue.
     * Once the promise is resolve, the function _promise is replace.
     * @param {Function} onSuccess
     * @param {Function} onFail
     * @private
     */
    var _process = function (onSuccess, onFail) {
        _onSuccess.push(onSuccess);
        _onFail.push(onFail);
    };

    /**
     * Add an onSuccess callback and optionally an onFail callback to the Promise
     * @param {Function} onSuccess
     * @param {Function} [onFail]
     * @returns {Promise} promise
     */
    this.then = function (onSuccess, onFail) {
        return new Promise(function (resolve, reject) {
            var s = onSuccess ? _then(onSuccess, resolve, reject) : resolve;
            var f = onFail ? _then(onFail, resolve, reject) : reject;

            _process(s, f);
        }, me);
    };

    /**
     * Resolve the promise
     * @param {*} result
     * @type {Function}
     */
    var _resolve = function (result) {
        // update status
        me.resolved = true;
        me.rejected = false;
        me.pending = false;

        _onSuccess.forEach(function (fn) {
            fn(result);
        });

        _process = function (onSuccess, onFail) {
            onSuccess(result);
        };

        _resolve = _reject = function () { };

        return me;
    };

    /**
     * Reject the promise
     * @param {Error} error
     * @type {Function}
     */
    var _reject = function (error) {
        // update status
        me.resolved = false;
        me.rejected = true;
        me.pending = false;

        _onFail.forEach(function (fn) {
            fn(error);
        });

        _process = function (onSuccess, onFail) {
            onFail(error);
        };

        _resolve = _reject = function () { }

        return me;
    };

    /**
     * Cancel te promise. This will reject the promise with a CancellationError
     * @returns {Promise} self
     */
    this.cancel = function () {
        if (parent) {
            parent.cancel();
        } else {
            _reject(new CancellationError());
        }
        return me;
    };

    /**
     * Set a timeout for the promise. If the promise is not resolved within
     * the time, the promise will be cancelled and a TimeoutError is thrown.
     * If the promise is resolved in time, the timeout is removed.
     * @param {number} delay     Delay in milliseconds
     * @returns {Promise} self
     */
    this.timeout = function (delay) {
        if (parent) {
            parent.timeout(delay);
        } else {
            var timer = setTimeout(function () {
                _reject(new TimeoutError('Promise timed out after ' + delay + ' ms'));
            }, delay);

            me.always(function () {
                clearTimeout(timer);
            });
        }
        return me;
    };

    // attach handler passing the resolve and reject functions
    handler(function (result) {
        _resolve(result);
    }, function (error) {
        _reject(error);
    });
}

/**
 * Execute given callback, then call resolve/reject based on the returned result
 * @param {Function} callback
 * @param {Function} resolve
 * @param {Function} reject
 * @returns {Function}
 * @private
 */
function _then(callback, resolve, reject) {
    return function (result) {
        try {
            var res = callback(result);
            if (res && typeof res.then === 'function' && typeof res['catch'] === 'function') {
                // method returned a promise
                res.then(resolve, reject);
            } else {
                resolve(res);
            }
        } catch (error) {
            reject(error);
        }
    }
}

/**
 * Add an onFail callback to the Promise
 * @param {Function} onFail
 * @returns {Promise} promise
 */
Promise.prototype['catch'] = function (onFail) {
    return this.then(null, onFail);
};

// TODO: add support for Promise.catch(Error, callback)
// TODO: add support for Promise.catch(Error, Error, callback)

/**
 * Execute given callback when the promise either resolves or rejects.
 * @param {Function} fn
 * @returns {Promise} promise
 */
Promise.prototype.always = function (fn) {
    return this.then(fn, fn);
};

/**
 * Create a promise which resolves when all provided promises are resolved,
 * and fails when any of the promises resolves.
 * @param {Promise[]} promises
 * @returns {Promise} promise
 */
Promise.all = function (promises) {
    return new Promise(function (resolve, reject) {
        var remaining = promises.length,
                results = [];

        if (remaining) {
            promises.forEach(function (p, i) {
                p.then(function (result) {
                    results[i] = result;
                    remaining--;
                    if (remaining == 0) {
                        resolve(results);
                    }
                }, function (error) {
                    remaining = 0;
                    reject(error);
                });
            });
        } else {
            resolve(results);
        }
    });
};

/**
 * Create a promise resolver
 * @returns {{promise: Promise, resolve: Function, reject: Function}} resolver
 */
Promise.defer = function () {
    var resolver = {};

    resolver.promise = new Promise(function (resolve, reject) {
        resolver.resolve = resolve;
        resolver.reject = reject;
    });

    return resolver;
};

/**
 * Create a cancellation error
 * @param {String} [message]
 * @extends Error
 */
function CancellationError(message) {
    this.message = message || 'promise cancelled';
    this.stack = (new Error()).stack;
}

CancellationError.prototype = new Error();
CancellationError.prototype.constructor = Error;
CancellationError.prototype.name = 'CancellationError';

Promise.CancellationError = CancellationError;


/**
 * Create a timeout error
 * @param {String} [message]
 * @extends Error
 */
function TimeoutError(message) {
    this.message = message || 'timeout exceeded';
    this.stack = (new Error()).stack;
}

TimeoutError.prototype = new Error();
TimeoutError.prototype.constructor = Error;
TimeoutError.prototype.name = 'TimeoutError';

Promise.TimeoutError = TimeoutError;


module.exports = Promise;

2019年3月7日 星期四

worker 環境判定

if(typeof Window !== 'undefined'){
    // browser
}else if(typeof WorkerGlobalScope !== 'undefined'){
    // browser worker
   
}else if(typeof module !== 'undefined'){
    // node.js
    const {isMainThread} = require('worker_threads');
       
    if(isMainThread){
        // MainThread
    }else{
        // worker
    }
}

2019年3月5日 星期二

所有的html標籤

所有的html標籤

不同系統的換行符號

str.replace(/(\r\n|\n)/, '')


|一、概念:

換行符‘\n’和回車符‘\r’

(1)換行符就是另起一行  — ‘\n‘ 10 換行(newline)

(2)回車符就是回到一行的開頭 — ‘\r‘ 13 回車(return)

所以我們平時編寫檔案的回車符應該確切來說叫做回車換行符 

CR: 回車(Carriage Return) \r
LF: 換行(Line Feed) \n

二、應用:
(1)在微軟的MS-DOS和Windows中,使用“回車CR(‘\r’)”和“換行LF(‘\n’)”兩個字元作為換行符;
(2)Windows系統裡面,每行結尾是 回車 換行(CR LF),即“\r\n”;
(3)Unix系統裡,每行結尾只有 換行LF,即“\n”;
(4)Mac系統裡,每行結尾是 回車CR 即’\r’。
Mac OS 9 以及之前的系統的換行符是 CR,從 Mac OS X (後來改名為“OS X”)開始的換行符是 LF即‘\n’,和Unix/Linux統一了。
三、影響:
(1)一個直接後果是,Unix/Mac系統下的檔案在Windows裡開啟的話,所有文字會變成一行;
(2)而Windows裡的檔案在Unix/Mac下開啟的話,在每行的結尾可能會多出一個^M符號。
(3)Linux儲存的檔案在windows上用記事本看的話會出現黑點。
四、可以相互轉換:
在linux下,命令unix2dos 是把linux檔案格式轉換成windows檔案格式,命令dos2unix 是把windows格式轉換成linux檔案格式。
在不同平臺間使用FTP軟體傳送檔案時, 在ascii文字模式傳輸模式下, 一些FTP客戶端程式會自動對換行格式進行轉換. 經過這種傳輸的檔案位元組數可能會發生變化.
 如果你不想ftp修改原檔案, 可以使用bin模式(二進位制模式)傳輸文字。
一個程式在windows上執行就生成CR/LF換行格式的文字檔案,而在Linux上執行就生成LF格式換行的文字檔案。