2017年1月30日 星期一

(css)上一層(postion:relative)對下一代的影響

上層對下面階層的size影響

只有在子孫在例如(position: absolute)時才會受影響





(css)若要做一個滿版的頁面

* {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
     
        html,
        body {
            width: 100%;
            height: 100%;
            position: relative;
        }
     
        body {
            overflow-y: scroll;
        }
        /* ------------------------ */
     
        .container_1 {
            width: 100%;
            height: 100%;
            position: relative;
            opacity: 0.5;
        }
     
        .container_2 {
            overflow: auto;
            height: 100%;
            width: 400%;
        }
     
        .container_2:after {
            display: table;
            height: 0;
            clear: both;
        }
     
        .container_2>.item_1 {
            float: left;
            width: 25%;
            height: 100%;
            border: 5px dotted #888;
        }
        /* ------------------------ */
     
        .div_1 {
            height: 2000px;
            width: 100%;
            background-color: #AAA;
        }


<body>
    <div class="container_1">
        <div class="container_2">
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
        </div>
    </div>
    <!-- -->
    <div class="div_1">
        ...................
    </div>
</body>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
* {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
     
        html,
        body {
            width: 100%;
            height: 100%;
            position: relative;
        }
     
        body {
            overflow-y: scroll;
        }
        /* ------------------------ */
     
        .container_1 {
            width: 400%;
            height: 100%;
            position: relative;
            display: table;
        }
     
        .container_2 {
            display: table-row;
        }
     
        .container_2>.item_1 {
            width: 25%;
            display: table-cell;
            border: 5px dotted #888;
        }
        /* ------------------------ */
     
        .div_1 {
            height: 2000px;
            width: 100%;
            background-color: #AAA;

        }

<body>
    <div class="container_1">
        <div class="container_2">
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
        </div>
    </div>
    <!-- -->
    <div class="div_1">

    </div>
</body>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

* {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
       
        html,
        body {
            width: 100%;
            height: 100%;
            position: relative;
        }
       
        body {
            overflow-y: scroll;
        }
        /* ------------------------ */
       
        .container_1 {
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            position: absolute;
            opacity: 0.5;
        }
       
        .container_2 {
            overflow: auto;
            height: 100%;
            width: 400%;
        }
       
        .container_2:after {
            display: table;
            height: 0;
            clear: both;
        }
       
        .container_2>.item_1 {
            float: left;
            width: 25%;
            height: 100%;
            border: 5px dotted #888;
        }
        /* ------------------------ */
       
        .container_1_bg {
            width: 100%;
            height: 100%;
        }
        /* ------------------------ */
       
        .div_1 {
            height: 2000px;
            width: 100%;
            background-color: #AAA;
        }

<body>
    <div class="container_1_bg">
        作為(container_1)的空間佔位
    </div>
    <div class="container_1">
        <div class="container_2">
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
            <div class="item_1">

            </div>
        </div>
    </div>

    <!-- -->
    <div class="div_1">
        ...................
    </div>
</body>

2017年1月26日 星期四

(js)用(EventEmitter)執行非同步任務

var Event = require('../lib/eventEmitter_2');


var event = Event();


/* ----------------------------- */
event.once('a1', function(data){

console.log('a1', data);

setTimeout(function(){

console.log('a1 end');
event.emit('a2', ++data);
}, 1000);
});

/* ----------------------------- */
event.once('a2', function(data){

console.log('a2', data);

setTimeout(function(){
console.log('a2 end');
event.emit('a2', ++data);
}, 1000);

});
/* ----------------------------- */

event.emit('a1', 1);

2017年1月25日 星期三

(js)箭頭函數

箭頭函數表示式 (Arrow function expression,也是所謂的 fat arrow function) 比起一般的函數表示式擁有更短的語法以及詞彙上綁定 this 變數,所有的箭頭函數都是無名函數 (anonymous function).

基本語法

(param1, param2, …, paramN) => { statements } 
(param1, param2, …, paramN) => expression
     // 等於 :  => { return expression; } 

// 只有一個參數時,括號才能不加:
(singleParam) => { statements }
singleParam => { statements }

//若無參數,就一定要加括號:
() => { statements }
詳細語法範例請參照這裡
param
參數名稱。多個參數以()包住,如不需要參數則以 () 表示, 如只有一個參數可以省略括弧 (如 foo => 1)。
statements or expression
當有多個陳述 (statement) 需用 { } 框住,當只有一個陳述則不需;其中表示式 (Expression) 最後也會是箭頭函數的返回值。

說明

箭頭函數有兩個重要的特性:更短的函數寫法與 this 變數綁定。

更短的函數寫法

比較一般寫法和箭頭函數寫法;In some functional patterns, shorter functions are welcome. Compare:
var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryl­lium"
];

var a2 = a.map(function(s){ return s.length });

var a3 = a.map( s => s.length );

this 變數綁定

在過去,函數的 this 變數在不同狀況下一直指向不同值  (例如當建構子呼叫時指向的是新建構的物件、undefined 在 strict 模式、當以物件方法呼叫時只向呼叫物件),這樣的特性對物件導向程式設計來說其實相當麻煩。
function Person() {
  // The Person() constructor defines `this` as itself.
  this.age = 0;

  setInterval(function growUp() {
    // In nonstrict mode, the growUp() function defines `this` 
    // as the global object, which is different from the `this`
    // defined by the Person() constructor.
    this.age++;
  }, 1000);
}

var p = new Person();
為了解決這個問題,常見的方法是利用 closure 將 this 變數存入另一個變數之中:
function Person() {
  var self = this; // Some choose `that` instead of `self`. 
                   // Choose one and be consistent.
  self.age = 0;

  setInterval(function growUp() {
    // The callback refers to the `self` variable of which
    // the value is the expected object.
    self.age++;
  }, 1000);
}
或者是,透過綁定函數的作法來綁定 this 變數到 growUp 函數。
不過現在箭頭函數會自動將 this 變數綁定到其定義時所在的物件,如下所示:
function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}

var p = new Person();

和 Strict 模式

由於 this 變數具有詞彙上綁定義意,所以 strict 模式的宣告對 this 的作用將被忽略。
var f = () => {'use strict'; return this};
f() === window; // or the global object
但其於 strict 模式作用仍存在。

和 call 與 apply 函數

於 this 變數已被綁定,所以透過 call 或 apply 呼叫箭頭函數只能傳入參數但無法改變 this
var adder = {
    
        base : 1,
    
        add : function(a) {
            
            var f = v => v + this.base;
            
            return f(a);
        },
        
        addThruCall: function(a) {
            
            var f = v => v + this.base;
            
            var b = {
                    base : 2
                };
            
            return f.call(b, a);
        }
    };

console.log(adder.add(1));         // This would log to 2
console.log(adder.addThruCall(1)); // This would log to 2 still

Examples

// An empty arrow function returns undefined
let empty = () => {};

(() => "foobar")() // returns "foobar" 

var simple = a => a > 15 ? 15 : a; 
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

// Easy array filtering, mapping, ...

var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b);  // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2);       // [10, 12, 26, 0, 2, 36, 46]

規範標準

SpecificationStatusComment
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Arrow Function Definitions' in that specification.
StandardInitial definition.

瀏覽器相容性

FeatureChromeFirefox (Gecko)Internet ExplorerOperaSafari
Basic supportNo support22.0 (22.0)No supportNo supportNo support

Firefox 特定註記

  • 在 Firefox 24 前,strict mode 會隨著箭頭函數定義而生效,但之後已取消,明白宣告 strict mode 是需要的。
  • 箭頭函數和 Firefox3 加入、非標準的 Expression Closures (詳細資訊: Javascript 1.8) 有所不同,針對 Expression Closures 並無綁定 this 變數。
  • 在 Firefox 39 前, 單行終結符號 (\n) 錯誤地必允許在箭頭函數,現在這項錯誤已經修正以符合 ES6 標準,類似程式碼如 () \n => {} 將導致 SyntaxError 錯誤。

文件標籤與貢獻者

 此頁面的貢獻者: linjimmy168foxbrush

(js)Promise_好版本

原出處


module.exports = Promise;

var PENDING = undefined,
    FULFILLED = 1,
    REJECTED = 2;


function Promise(_resolver, name) {
    debugger;
    if (!isFunction(_resolver)) {
        throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
    }

    if (!(this instanceof Promise)) {
        // 當作函式用的話
        return new Promise(_resolver);
    }
    this._name = name || 'then';
    this.guid = Promise.prototype.guid++;
    /* ---------------------------------- */
    var promise = this;
    promise._value;
    promise._reason;
    promise._status = PENDING;
    /* ---------------------------------- */
    promise._resolves = [];
    promise._rejects = [];
    /* ---------------------------------- */
    debugger;
    // 執行賦予的主任務
    _resolver(resolve, reject);

    return;
    /* ---------------------------------- */

    function resolve(value) {
        debugger;
        transition.apply(promise, [FULFILLED, value]);
    }

    function reject(reason) {
        debugger;
        transition.apply(promise, [REJECTED, reason]);
    }
};
/* ========================================================================== */
Promise.prototype.guid = 0;

/* ========================================================================== */
Promise.prototype.then = function(onFulfilled, onRejected) {
    debugger;
    var promise = this;
    // 每次返回一个promise,保证是可thenable的
    return new Promise(function(resolve, reject) {
        debugger;

        if (promise._status === PENDING) {
            promise._resolves.push(callback);
            promise._rejects.push(errback);
        } else if (promise._status === FULFILLED) { // 状态改变后的then操作,立刻执行
            callback(promise._value);
        } else if (promise._status === REJECTED) {
            errback(promise._reason);
        }

        return;
        /* ---------------------------------------- */

        function callback(value) {
            debugger;
            // 執行(onFulfilled)
            var ret = isFunction(onFulfilled) && onFulfilled(value) || value;

            if (isThenable(ret)) {
                ret.then(function(value) {
                    resolve(value);
                }, function(reason) {
                    reject(reason);
                });
            } else {
                resolve(ret);
            }
        }
        /* ---------------------------------------- */
        function errback(reason) {
            debugger;
            reason = isFunction(onRejected) && onRejected(reason) || reason;
            reject(reason);
        }
    });
};
/* ========================================================================== */
Promise.prototype.catch = function(onRejected) {
    return this.then(undefined, onRejected)
};
/* ========================================================================== */
Promise.prototype.delay = function(ms, val) {
    return this.then(function(ori) {
        return Promise.delay(ms, val || ori);
    })
};
/* ========================================================================== */
Promise.delay = function(ms, val) {
    return Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(val);
        }, ms);
    })
};
/* ========================================================================== */
Promise.resolve = function(arg) {
    return Promise(function(resolve, reject) {
        resolve(arg)
    })
};
/* ========================================================================== */
Promise.reject = function(arg) {
    return Promise(function(resolve, reject) {
        reject(arg)
    })
};
/* ========================================================================== */
Promise.all = function(promises) {
    if (!isArray(promises)) {
        throw new TypeError('You must pass an array to all.');
    }
    return Promise(function(resolve, reject) {
        var i = 0,
            result = [],
            len = promises.length,
            count = len

        function resolver(index) {
            return function(value) {
                resolveAll(index, value);
            };
        }

        function rejecter(reason) {
            reject(reason);
        }

        function resolveAll(index, value) {
            result[index] = value;
            if (--count == 0) {
                resolve(result)
            }
        }

        for (; i < len; i++) {
            promises[i].then(resolver(i), rejecter);
        }
    });
};
/* ========================================================================== */
Promise.race = function(promises) {
    if (!isArray(promises)) {
        throw new TypeError('You must pass an array to race.');
    }
    return Promise(function(resolve, reject) {
        var i = 0,
            len = promises.length;

        function resolver(value) {
            resolve(value);
        }

        function rejecter(reason) {
            reject(reason);
        }

        for (; i < len; i++) {
            promises[i].then(resolver, rejecter);
        }
    });
};

////////////////////////////////////////////////////////////////////////////////
function isFunction(obj) {
    return 'function' === typeof obj;
};
/* ========================================================================== */
function isArray(obj) {
    return Object.prototype.toString.call(obj) === "[object Array]";
};
/* ========================================================================== */
/**
 * 若對象含有.then()
 */
function isThenable(obj) {
    return obj && typeof obj['then'] == 'function';
};
/* ========================================================================== */
function transition(status, value) {
    debugger;
    var promise = this;

    if (promise._status !== PENDING) {
        return;
    }

    console.log('------------------');
    console.log('name = %s, guid = %d', this._name, this.guid);
    console.dir(this._resolves);
    console.dir(this._rejects);
    console.log('------------------');

    // 所以的执行都是异步调用,保证then是先执行的
    // 存疑的步驟
    setTimeout(function() {
        debugger;
        console.log('do mission: name = %s, guid = %d', promise._name, promise.guid);
        promise._status = status;

        // 執行任務
        publish.call(promise, value);
    }, 0);
};
/* ========================================================================== */
/**
 * 執行(._resolves)or(_rejects)
 */
function publish(val) {
    debugger;

    var promise = this,
        fn,
        st = promise._status === FULFILLED;

    // 任務隊列
    var queue = promise[st ? '_resolves' : '_rejects'];

    while (fn = queue.shift()) {
        val = fn.call(promise, val) || val;
    }
    promise[st ? '_value' : '_reason'] = val;

    promise['_resolves'] = undefined;
    promise['_rejects'] = undefined;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

教你一步步實現一個Promise

Promise我想現在大家都非常熟悉了,主要作用就是解決異步回調問題,這裡簡單介紹下。
Promise規範是CommonJS規範之一,而Promise規範又分了好多種,比如 Promises/A、Promises/B、Promises/Kiss等等
有興趣的可以到這多瞭解一些 http://wiki.commonjs.org/wiki/Promises
現在比較流行的是Promise/A規範,人們對它的完善和擴展,逐漸形成了Promise/A+規範,A+已脫穎而出。
說到這裡規範是什麼,可以去這裡瞭解下
現在已有瀏覽器內置支持Promise,它的api語法可以在這裡查看
可以看到它的api並不多,其實規範也不多,我覺的大致抓住幾個重要的點就夠了,
1、promise有三種狀態,等待(pending)、已完成(fulfilled)、已拒絕(rejected)
2、promise的狀態只能從「等待」轉到「完成」或者「拒絕」,不能逆向轉換,同時「完成」和「拒絕」也不能相互轉換
3、promise必須有一個then方法,而且要返回一個promise,供then的鏈式調用,也就是可thenable的
4、then接受倆個回調(成功與拒絕),在相應的狀態轉變時觸發,回調可返回promise,等待此promise被resolved後,繼續觸發then鏈
知道這幾個重要的特點,我們就可以參考瀏覽器內置的api來實現了,
我們可以不必太受規範約束,先按照自己的想法來就好了。
promise的使用大致如下
var promise = new Promise(function(resolve, reject) {
    setTimeout(function(){
        resolve("val")
    });
});
promise.then(onFulfilled,onRejected).then(onFulfilled,onRejected)
主要思路就是我們可以直接對返回的promise對象進行操作,比如then,傳入回調,
這裡的函數並不會立即執行,而是加入隊列,等待未來的某個時間resolve時被觸發執行。
有了以上說明,就可以來實現了
首先定義三個狀態
var PENDING = undefined, FULFILLED = 1, REJECTED = 2;
然後實現Promise構造函數,此函數接受一個函數參數,函數參數接受倆個我們提供的方法resolve與reject,
供使用者在未來的某個時間裡調用觸發執行我們的隊列,這裡還要初始下當前的狀態,傳遞的值,
以及then時保存到的隊列。
大概像下面這樣
var Promise = function(resolver){
    if (!isFunction(resolver))
        throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");
    if(!(this instanceof Promise)) return new Promise(resolver);

    var promise = this;
    promise._value;
    promise._reason;
    promise._status = PENDING;
    promise._resolves = [];
    promise._rejects = [];

    var resolve = function(value){
        //狀態轉換為FULFILLED 
        //執行then時保存到_resolves裡的回調,
        //如果回調有返回值,更新當前_value
    }
    var reject = function(reason){
        //狀態轉換為REJECTED
        //執行then時保存到_rejects裡的回調,
        //如果回調有返回值,更新當前_rejects
    }

    resolver(resolve,reject);
}
有了這個,我們在實現一個then就ok了,
then裡要做的就是返回一個promise供then的鏈式調用,
而且promise.then(onFulfilled,onRejected)時,我們要判斷當前promise的狀態,
如果是pending則把onFulfilled與onRejected添加到_resolves與_rejects數組裡,
否則的話根據狀態,直接觸發回調,這裡要注意的是,如果返回的是promise,我們要等到此promise被resolves時,觸發then鏈的下一個promise執行。
代碼大概是這樣
Promise.prototype.then = function(onFulfilled,onRejected){
    var promise = this;
    // 每次返回一個promise,保證是可thenable的
    return Promise(function(resolve,reject){
        function callback(value){
          var ret = isFunction(onFulfilled) && onFulfilled(value) || value;
          if(isThenable(ret)){
              // 根據返回的promise執行的結果,觸發下一個promise相應的狀態
            ret.then(function(value){
               resolve(value); 
            },function(reason){
               reject(reason);
            });
          }else{
            resolve(ret);
          }
        }
        function errback(reason){
            reason = isFunction(onRejected) && onRejected(reason) || reason;
            reject(reason);
        }
        if(promise._status === PENDING){
               promise._resolves.push(callback);
               promise._rejects.push(errback);
           }else if(promise._status === FULFILLED){ // 狀態改變後的then操作,立刻執行
               callback(promise._value);
           }else if(promise._status === REJECTED){
               errback(promise._reason);
           }
    });
}
這裡說明下
var isThenable = function(obj){
  return obj && typeof obj["then"] == "function";
}
也就是說返回的對象帶有then方法,我們就當作promise對象
到這裡我們主要的工作就完成了,其他的all,race等方法都很簡單,具體可以到這裡看完整的實現
下面我們來做幾個例子來看下效果
var getData100 = function(){
    return Promise(function(resolve,reject){
        setTimeout(function(){
            resolve("100ms");
        },100);
    });
}

var getData200 = function(){
    return Promise(function(resolve,reject){
        setTimeout(function(){
            resolve("200ms");
        },200);
    });
}

getData100().then(function(data){
    console.log(data); // 100ms
    return getData200();
}).then(function(data){
    console.log(data); // 200ms
    return data + data;
}).then(function(data){
    console.log(data) // 200ms200ms
});
當然可以直接getData100().then(getData200).then(function(val){})
then可以只傳一個,接受成功的回調,也可以用catch方法,接受失敗的回調,
catch是then的一個語法糖,相當於promise.then(undefined, onRejected)
也可以用all來並行執行
Promise.all([getData100(),getData200()]).then(function(value){
    console.log(value) // ["100ms","200ms"]
});
結果的順序與傳入的順序相同。
我們也可以直接創建一個以obj為值的成功狀態的promise,
Promise.resolve("FULFILLED").then(function(val){
    console.log(val) // FULFILLED
});
實現都相當簡單,看代碼就懂。
這裡也可以做一些好玩的,比如創建一個delay方法
Promise.prototype.delay = function(ms){
    return this.then(function(val){
        return Promise.delay(ms,val);
    })
}

Promise.delay = function(ms,val){
    return Promise(function(resolve,reject){
        setTimeout(function(){
            resolve(val);
        },ms);
    })
}
我們可以每隔多少毫秒執行一些操作
Promise.delay(1000).then(function(){
    // 一些操作
}).delay(1000).then(function(){
    // 一些操作
})
我們也可以包裝一個循環,執行多少次,每次延遲多少秒執行什麼操作
var len = 0,
    words = "你好,你是誰?";

function count(num,ms,cb){
    var pro = Promise.resolve();
    for (var i = 0; i < num; i++) {
        pro = pro.delay(ms).then(function(v){
            return cb(v);
        });
    };
}

count(words.length,800,function(){
    var w = words.substr(0,++len);
    console.log(w);
})
更多的東西等你來實現~