观察者模式

132 阅读1分钟
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<script>
// 观察者模式
let Observer = (function() {
    // 消息管道
    let _msg = {};
    return {
        // 绑定事件
        on(type, callback) {
            if (_msg[type]) {
                // 直接存储
                _msg[type].push(callback)
            } else {
                // 创建新的数组
                _msg[type] = [callback]
            }
        },
        // 发布事件
        trigger(type, ...args) {
            // 找到该类型的数组,逐一执行
            _msg[type] && _msg[type].forEach(fn => fn(...args))
        },
        // 移除事件
        off(type, callback) {
            // 传递回调函数  
            if (callback) {
                if (_msg[type]) {
                    // 获取位置
                    let index = _msg[type].indexOf(callback);
                    if (index >= 0) {
                        _msg[type].splice(index, 1)
                    }
                }
            } else if (type) {
                _msg[type] = []
            } else {
                // 全部清空
                _msg = {};
            }
        },
        // 单次绑定
        once(type, callback) {
            // 缓存this
            let me = this;
            // 包装函数
            function cb(...args) {
                // 移除cb
                me.off(type, cb);
                callback(...args)
            }
            // 订阅cb
            me.on(type, cb);
        }
    }
})();

// 测试
function demo(...args) {
    console.log('demo', args);
    // 发布了
    // Observer.trigger('ickt', 200)
}
// 单次订阅
Observer.once('ickt', demo);
Observer.trigger('ickt', 100, 200)
// Observer.trigger('ickt', 100, 200)
// Observer.trigger('ickt', 100, 200)
// Observer.on('ickt', demo);
// Observer.on('ickt', function(...args) {
//     console.log('test', args);
// })
// Observer.on('abc', function(...args) {
//     console.log('abc', args);
// })
// 移除
// Observer.off('ickt', demo)
// Observer.off('ickt')
// Observer.off()
// Observer.off('ickt', function(...args) {
//     console.log('test', args);
// })
// 发布
// Observer.trigger('ickt', 100, 200)
// Observer.trigger('abc', 100, 200)
</script>
</body>
</html>

节流和防抖

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>1-1-1</h1>
    <h1>2-2-2</h1>
    <h1>3-3-3</h1>
    <h1>4-4-4</h1>
    <h1>5-5-5</h1>
    <h1>6-6-6</h1>
    <h1>7-7-7</h1>
    <h1>8-8-8</h1>
    <h1>9-9-9</h1>
    <h1>10-10-10</h1>
    <h1>11-11-11</h1>
    <h1>12-12-12</h1>
    <h1>13-13-13</h1>
    <h1>14-14-14</h1>
    <h1>15-15-15</h1>
    <h1>16-16-16</h1>
    <h1>17-17-17</h1>
    <h1>18-18-18</h1>
    <h1>19-19-19</h1>
    <h1>20-20-20</h1>
    <h1>21-21-21</h1>
    <h1>22-22-22</h1>
    <h1>23-23-23</h1>
    <h1>24-24-24</h1>
    <h1>25-25-25</h1>
    <h1>26-26-26</h1>
    <h1>27-27-27</h1>
    <h1>28-28-28</h1>
    <h1>29-29-29</h1>
    <h1>30-30-30</h1>
<script>
// 事件回调函数
function demo(e) {
    console.log('demo', e);
}
// 监听滚动条事件
window.onscroll = demo;

// 防抖: 执行最后一次
 function ickt(fn, time = 200) {
     // 定时器句柄
     let timebar = null;
       return function(...args) {
         // 清除定时器
         clearTimeout(timebar);
         // 执行
         timebar = setTimeout(function() {
            fn(...args)
         }, time)
    }
 }
 window.onscroll = ickt(demo);

// 节流: 固定时间内,函数执行一次
function throttle(fn, time = 1000) {
    // 订阅开关
    let lock = false;
    return function(...args) {
        // 一旦上锁,无法执行
        if (lock) {
            return;
        }
        // 执行
        fn(...args);
        // 上锁
        lock = true;
        // 解锁
        setTimeout(function() {
            lock = false;
        }, time)
    }
}
window.onscroll = throttle(demo)
</script>
</body>
</html>

Promise的实现

// 实现Promise
function IcktPromise(callback) {
    // 状态: 进入fulfilled, 进入rejected
    this.state = 'padding';
    // 存储成功的回调函数
    this.successArray = [];
    // 存储失败的回调函数
    this.failArray = [];
    // 执行结果
    this.result = null;

    // 成功时候的回调函数
    let resolve = value => {
        // 改变状态
        this.state = 'fulfilled';
        // 存储结果
        this.result = value;
        // 遍历数组,执行回调函数
        this.successArray.forEach(fn => this.result = fn(this.result))
        // 清空回调函数
        this.successArray = [];
    }
    // 失败时候的回调函数
    let reject = value => {
        // 改变状态
        this.state = 'rejected';
        // 存储结果
        this.result = value;
        // 遍历数组,执行回调函数
        this.failArray.forEach(fn => this.result = fn(this.result))
        // 清空回调函数
        this.failArray = [];
    }

    // 执行回调函数
    try {
        callback(resolve, reject);
    } catch (e) {
        reject(e);
    }
}
// 监听结果
IcktPromise.prototype.then = function(success, fail) {
    // 如果是padding
    if (this.state === 'padding') {
        success && this.successArray.push(success);
        fail && this.failArray.push(fail);
    } else if (this.state === 'fulfilled') {
        success && success(this.result);
    } else {
        // 失败
        fail && fail(this.result);
    }
    // 链式调用
    return this;
}


// 使用Promise
let p = new IcktPromise((resolve, reject) => {
    // 做异步
    setTimeout(function() {
        reject('scucess')
    }, 1000)
})
// 监听结果
p.then(
    data => {
        console.log(data)
        return 100;
    },
    err => console.log('err', err)
).then(data => console.log('第二个', data))
console.log(111, p);

jQuery

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div></div>
    <div></div>
    <div></div>
<script src="./jquery.js"></script>
<script>
// 创建实例的工厂方法
var A = function() {}
// 对象对应的类
var B = function() {}

// 减少变量个数
var A = function() {}
// A上存储B
// A.B = function() {}
// 不希望外界可以直接修改它
// A.B = 123;
A.prototype = {
    num: 100,
    version: '1.0'
}
// 放在原型上
A.prototype.B = function() {}

// 换个名字
var jQuery = function() {}
// 存储数据
jQuery.prototype = {
    num: 100,
    version: '1.0'
}
// 放在原型上
jQuery.prototype.init = function() {}


// 工厂方法:创建init实例
var jQuery = function(selector) {
    // 返回init类的实例
    return new jQuery.fn.init(selector)
}
// 给prototype起个名字,访问方便
jQuery.fn = jQuery.prototype = {
    num: 100,
    // 版本号
    jquery: '1.0',
    // 添加数组方法,让其看起来更像数组
    push: Array.prototype.push,
    splice: Array.prototype.splice
}
// 放在原型上
jQuery.fn.init = function(selector) {
    // 长度
    this.length = 0;
    // 存储选择器
    this.selector = selector;
    // 获取元素
    var doms = document.querySelectorAll(selector);
    // 存储
    for (var i = 0; i < doms.length; i++) {
        // 存储元素
        this[i] = doms[i];
        // 更新长度
        this.length++;
    }
}
// 为了让init类的实例具有jQuery工厂原型上的数据,可以通过修改原型来实现.
jQuery.fn.init.prototype = jQuery.fn;
// 拓展jQuery.fn就是拓展init类的原型了
// jQuery.fn.demo = function() {}
// jQuery.fn.color = 'red';
// 拓展方法的方法
jQuery.extend = jQuery.fn.extend = function(target) {
    // this指向调用者
    // 将target属性复制给调用者
    for (var key in target) {
        this[key] = target[key];
    }
}

// 拓展静态属性
jQuery.extend({
    each: function() {},
    ajax: function() {}
})
jQuery.fn.extend({
    // 设置内容
    html: function(html) {
        // 给每个元素设置内容
        for (var i = 0; i < this.length; i++) {
            this[i].innerHTML = html;
        }
        // 链式调用
        return this;
    },
    // 设置样式
    css: function(key, value) {
        // 判断
        if (typeof key === 'string') {
            // 遍历元素,设置样式
            for (var i = 0; i < this.length; i++) {
                this[i].style[key] = value;
            }
            return this;
        }
        // 如果是对象
        for (var k in key) {
            // 递归调用
            this.css(k, key[k])
        }
        // 链式调用
        return this;
    }
})
// 使用简单
var $ = jQuery;

</script>
<script>
    // console.log($('div'));
    $('div')
        .html('爱创乐育')
        .css({ color: 'red', fontSize: '50px' })
        .css('background', 'green')
</script>
</body>
</html>

程序控制权

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<script>

// 异步:setTimeout
// 伪异步:Promise, async/await  微任务

// 2 6 7 b 3 8 9 4 a 5 1 
// 先找同步,再处理异步(微任务和宏任务)
// 定时器
// setTimeout(function() {
//     console.log(111);
// }, 0);
// // promise
// let p = new Promise((resolve, reject) => {
//     resolve('success')
//     console.log(222);
// })
// // 监听结果
// p
//     .then(data => console.log(333))
//     .then(data => console.log(444))
//     .then(data => console.log(555))
// // 异步函数
// async function demo() {
//     console.log(666);
//     await console.log(777);
//     console.log(888)
//     await console.log(999);
//     console.log('aaa');
// }
// demo();
// console.log('bbb');

// 2 4 5 9 3 6 1
// 面试题
// setTimeout(() => {
//     console.log(111);
// }, 0)
// async function demo() {
//     console.log(222);
//     await 100;
//     console.log(333);
// }
// demo();
// var p = new Promise(resolve => {
//     console.log(444);
//     resolve();
//     console.log(555);
// })
// p.then(
//     res => console.log(666)
// )
// function *test() {
//     console.log(777);
//     yield 100;
//     console.log(888);
// }
// let t = test()
// t.next()
// t.next()
// t.next()
// console.log(999);


// setTimeout(() => {
//     console.log(111);
// }, 0)
// async function demo() {
//     console.log(222);
//     await 100;
//     console.log(333);
//     await 200;
//     console.log('aaa');
//     await 300;
//     console.log('bbb');
//     await 400;
//     console.log('ccc');
// }
// demo();
// var p = new Promise(resolve => {
//     console.log(444);
//     resolve();
//     console.log(555);
// })
// p
//     .then(
//         res => console.log(666)
//     )
//     .then(
//         res => console.log('ddd')
//     )
//     .then(
//         res => console.log('eee')
//     )
// function *test() {
//     console.log(777);
//     yield 100;
//     console.log(888);
// }
// let t = test()
// t.next();
// t.next();
// console.log(999);
// 2 4 5 7 8 9 3 6 a d b e c 1


// 3 1 7 9 4 2 8 5 6 123  
// setTimeout(() => {
//     console.log(123123123);
// }, 0)
// function testSometing() {
//     console.log(111, "执行testSometing");
//     return "testSometing";
// }
// async function testAsync() {
//     console.log(222, "执行testAsync");
//     return Promise.resolve("hello async");
// }
// async function test() {
//     console.log(333, "test start...");
//     const v1 = await testSometing();
//     console.log(444, v1);
//     const v2 = await testAsync();
//     console.log(555, v2);
//     console.log(666, v1, v2);
// }
// test();
// var promise = new Promise((resolve)=> { 
//     console.log(777, "promise start.."); 
//     resolve("promise");
// });
// promise.then((val)=> console.log(888, val));
// console.log(999, "test end...")


// 3 1 7 9 4 2 8 100 200 300 400 5 6 500 123
setTimeout(() => {
    console.log(123123123);
}, 0)
async function testSometing() {
    console.log(111, "执行testSometing");
    return "testSometing";
}
async function testAsync() {
    console.log(222, "执行testAsync");
    await 100;
    console.log(100, 'bbb');
    return Promise.resolve("hello async");
    // return 200;
}
async function test() {
    console.log(333, "test start...");
    const v1 = await testSometing();
    console.log(444, v1);
    const v2 = await testAsync();
    console.log(555, v2);
    console.log(666, v1, v2);
}
test();
var promise = new Promise((resolve)=> { 
    console.log(777, "promise start.."); 
    resolve("promise");
});
promise
    .then((val)=> console.log(888, val))
    .then((val)=> console.log(200, 'aaa', val))
    .then((val)=> console.log(300, 'bbb', val))
    .then((val)=> console.log(400, 'bbb', val))
    .then((val)=> console.log(500, 'bbb', val));
console.log(999, "test end...")

</script>
</body>
</html>