01-JS原生

6/28/2001

# 一. 手写call的实现原理

<script>
    // call的原理
    (function() {
        function mlcall(context) {
            context = context ? Object(context) : window;
            // this就是fn  
            // this()  fn()
            context.f = this;
            let args = []; // 收集函数的参数
            for (var i = 1; i < arguments.length; i++) {
                args.push(arguments[i]);
            }
            let res = context.f(...args)
            delete context.f;
            return res;
        }
        Function.prototype.mlcall = mlcall;
    }())

    function fn(num1, num2) {
        console.log(this);
        return num1 + num2
    }
    let obj = {
        name: "码路"
    }
    // 1)改变fn中this的指向
    // 2)call可以让函数调用
    // 3)返回函数调用的结果
    // let res = fn.call(obj, 6, 8)
    let res = fn.mlcall(obj, 6, 8)
    console.log(res)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 二. 手写apply的实现原理

<script>
    // apply的原理
    (function() {
        function mlapply(context, args) {
            context = context ? Object(context) : window;
            // this  表示fn
            // this() 不OK
            context.f = this;
            if (!args) {
                return context.f();
            }
            let res = context.f(...args)
            delete context.f;
            return res;
        }
        Function.prototype.mlapply = mlapply;
    }())

    function fn(num1, num2) {
        console.log(this);
        return num1 + num2
    }
    let obj = {
        name: "码路"
    }
    // 1)改变fn中this的指向
    // 2)apply可以让函数调用
    // 3)返回函数调用的结果
    // let res = fn.apply(obj, [6, 8])
    let res = fn.mlapply(obj, [6, 8])
    console.log(res)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 三. 手写bind的实现原理

<script>
    // bind实现原理
    (function() {
        function mlbind(context) {
            let bindArgs = Array.prototype.slice.call(arguments, 1);
            // this 表示fn  this()不OK
            let that = this;

            function gn() {
                let args = Array.prototype.slice.call(arguments);
                return that.apply(context, bindArgs.concat(args))
            }
            return gn;
        }
        Function.prototype.mlbind = mlbind;
    })()

    function fn(num1, num2) {
        console.log(this);
        return num1 + num2;
    }
    let obj = {
        name: "码路"
    }
    // 1)改变fn中this的指向
    // 2)返回一个绑定this后的函数
    // let res = fn.bind(obj, 1)
    let res = fn.mlbind(obj, 1)
    console.log(res(2));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 四. 手写new的实现原理

< script >
    function Dog(name) {
        this.name = name;
        // return {a:1}
        return function() {
            console.log("xx");
        }
    }
Dog.prototype.bark = function() {
    console.log('wangwang');
}
Dog.prototype.sayName = function() {
    console.log('my name is ' + this.name);
}
// (1)在构造器内部创建一个新的对象
// (2)这个对象内部的__proto__属性会被赋值为该构造函数的prototype属性;
// (3)让构造器中的this指向这个对象
// (4)执行构造器中的代码
// (5)如果构造器没有返回对象或函数,则返回上面的创建出来的对象
// let malu = new Dog('码路');
// console.log(malu);
// malu.sayName();
// malu.bark();
function _new(Ctor, ...args) {
    //=>完成你的代码
    if (!Ctor.hasOwnProperty("prototype")) {
        throw new TypeError("Ctor is not a constructor")
    }
    let obj = Object.create(Ctor.prototype)
    let result = Ctor.apply(obj, args)
    if (result !== null && (typeof result == "object" || typeof result == "function")) {
        return result;
    }
    return obj;
}
let malu = _new(Dog, '码路');
console.log(malu);
// malu.bark(); //=>"wangwang"
// malu.sayName(); //=>"my name is 码路"
// console.log(malu instanceof Dog); //=>true
<
/script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 五. 手写实现深copy

<script>
    // 实现深copy
    function deepClone(target, weakMap = new WeakMap()) {
        if (target == null) return target;
        if (target instanceof Date) return new Date(target);
        if (target instanceof RegExp) return new RegExp(target)
        // .....
        if (typeof target !== "object") return target;
        let cloneTarget = new target.constructor;
        if (weakMap.get(target)) {
            return weakMap.get(target)
        }
        weakMap.set(target, cloneTarget)
        for (let key in target) {
            if (target.hasOwnProperty(key)) {
                cloneTarget[key] = deepClone(target[key], weakMap)
            }
        }
        return cloneTarget;
    }
    let obj = {
        name: "码路",
        address: {
            city: "北京"
        }
    };
    obj.xxx = obj; // 循环引用
    let newObj = deepClone(obj);
    console.log(newObj); < /script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 六. 实现(a == 1 && a == 2 && a == 3)为true

<script>
    // a等于什么值,会使条件成⽴「两种⽅案」 
    // var a = "?";
    // valueOf方法
    // 解法一
    // let a = {
    //     i:0,
    //     valueOf(){
    //         return ++this.i
    //     }
    // }
    // 解法二
    // let a = {
    //     i:0,
    //     toString(){
    //         return ++this.i
    //     }
    // }
    // 解法三
    // let a = [1,2,3];
    // a.toString = a.shift;
    // 解法四
    let i = 0;
    Object.defineProperty(window, "a", {
        get() {
            // console.log("get...");
            return ++i;
        }
    })
    if (a == 1 && a == 2 && a == 3) {
        console.log('码路教育');
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 七. 如何给数组降维

<script>
    // 利用Array.some方法判断数组中是否还存在数组,es6展开运算符连接数组
    let arr = [1, 2, [3, 4, [5]]]
    while (arr.some(item => Array.isArray(item))) {
        arr = [].concat(...arr)
    }
    console.log(arr);
</script>
<script>
    // 使用数组的concat方法
    let arr = [1, 2, [3, 4]]
    let result = []
    // result = Array.prototype.concat.apply([], arr)
    result = [].concat(...arr)
    console.log(result);
</script>
<script>
    // es6中的flat函数也可以实现数组的扁平化
    let arr = [1, 2, ['a', 'b', ['中', '⽂', [1, 2, 3, [11, 21, 31]]]], 3];
    let result = arr.flat(Infinity)
    console.log(result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 八. 一个关于闭包的经典面试题

<script>
    // 下面代码是否可以,每隔1000MS依次输出 0 1 2 3 4 5 ? 
    // 如果不可以,说明为啥?以及如何解决?
    // for循环是同步代码   定时器是异步代码
    // 同步代码执行完毕后,i的值已经变了6
    // for (var i = 0; i < 6; i++) {
    //     setTimeout(function () {
    //         console.log(i);
    //     }, (i + 1) * 1000);
    // }
    // 利用闭包解决
    // for (var i = 0; i < 6; i++) {
    //     (function (i) {
    //         setTimeout(function () {
    //             console.log(i);
    //         }, (i + 1) * 1000);
    //     })(i)
    // }
    // 利用块级作用域
    // let + {} 会形成块级作用域
    // for (let i = 0; i < 6; i++) {
    //     setTimeout(function () {
    //         console.log(i);
    //     }, (i + 1) * 1000);
    // }
    // 利用闭包解决
    for (let i = 0; i < 6; i++) {
        setTimeout((function(i) {
            return function() {
                console.log(i);
            }
        })(i), i * 1000)
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 九. 检测一个对象是否是纯对象

<script>
    // 检测一个对象是否是纯对象
    const isPlainObject = function isPlainObject(obj) {
        let prototype;
        return Object.prototype.toString.call(obj) === '[object Object]' &&
            (prototype = Object.getPrototypeOf(obj), prototype === null ||
                prototype == Object.getPrototypeOf({})
            )
    };
    // {}  new Object()  Object.create(null)
    console.log(isPlainObject({}));
    console.log(isPlainObject(new Object()));
    console.log(isPlainObject(Object.create(null)));
    console.log("-------------");

    function Person() {}
    let p = new Person()
    console.log(isPlainObject(new Array()));
    console.log(isPlainObject(p));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 十. 手写JSONP实现原理

前端代码

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<button id="btn">发送ajax请求</button>
<script>
    let btn = document.getElementById("btn");

    function jsonp(options) {
        let callBackName = "wangcai";
        window[callBackName] = function(data) {
            if (data != null) {
                options.success(data)
            } else {
                options.fail()
            }
        }
        let url = options.url + "?callBack=" + callBackName
        let scriptEle = document.createElement("script");
        scriptEle.src = url;
        document.body.append(scriptEle)
    }
    btn.onclick = function() {
        jsonp({
            url: "http://localhost:3000/",
            success: function(data) {
                console.log("data:", data);
            },
            fail: function(err) {
                console.log("数据请求失败了");
            }
        })
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

后端代码

const Koa = require("koa");
const cors = require("koa2-cors");
const logger = require("koa-logger");
const Router = require("@koa/router");
const koaBody = require("koa-body");
const app = new Koa();
const router = new Router();
app.use(cors());
app.use(logger());
app.use(koaBody());
router.get("/", (ctx) => {
    let cb = ctx.query.callBack;
    // console.log(cb);
    // 后端返回函数调用字符串
    ctx.body = `${cb}(${JSON.stringify({ a: 1, b: 2 })})`
})
app.use(router.routes())
router.allowedMethods();
app.listen(3000, () => {
    console.log("running in http://127.0.0.1:3000");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 十一. Vue2数据响应式原理

<script>
    // vue2.x的响应式原理
    function updateView() {
        console.log("视图更新了~");
    }

    function definedReactive(target, key, value) {
        // console.log(target,key,value);
        observer(value); // 深度监听
        Object.defineProperty(target, key, {
            get() {
                // console.log("get...");
                return value
            },
            set(newValue) {
                observer(newValue); // 深度监听
                value = newValue
                // 数据变了,我监听到了,我要去更新视图
                updateView()
            }
        })
    }

    function observer(target) {
        if (typeof target !== "object" || target === null) {
            return target
        }
        for (let key in target) {
            definedReactive(target, key, target[key])
        }
    }
    // 准备数据
    // 目的:name,age,info,address
    let data = {
        name: "码路",
        age: 18,
        info: {
            address: "bj",
        }
    };
    // 监听数据,数据劫持
    observer(data)
    // 当数据变了,要更新视图
    // console.log(data.name);
    // data.name = "malu";
    // console.log(data.name);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 十二. Vue2数组响应式原理

<script>
    // vue2.x的响应式原理
    function updateView() {
        console.log("视图更新了~");
    }
    // 把Array的原型对象,赋值给oldArrayProperty
    let oldArrayProperty = Array.prototype;
    let arrProto = Object.create(oldArrayProperty);
    ["push", "pop", "shift", "unshift", "splice"].forEach(methodName => {
        // console.log(methodName);
        // 调用这7个方法时,调用的并不是原生的push是我们自己重写后的push
        arrProto[methodName] = function() {
            updateView(); // 更新视图
            oldArrayProperty[methodName].call(this, ...arguments)
        }
    })

    function definedReactive(target, key, value) {
        // console.log(target,key,value);
        observer(value); // 深度监听
        Object.defineProperty(target, key, {
            get() {
                // console.log("get...");
                return value
            },
            set(newValue) {
                observer(newValue); // 深度监听
                value = newValue
                // 数据变了,我监听到了,我要去更新视图
                updateView()
            }
        })
    }

    function observer(target) {
        if (typeof target !== "object" || target === null) {
            return target
        }
        if (Array.isArray(target)) {
            // console.log(target+"是数组");
            // 如果是数组,改变原型对象
            target.__proto__ = arrProto;
        }
        for (let key in target) {
            definedReactive(target, key, target[key])
        }
    }
    // 准备数据
    // 目的:name,age,info,address
    let data = {
        name: "码路",
        age: 18,
        info: {
            address: "bj",
        },
        arr: ["ml", "wc", "xq"]
    };
    // data.arr.push("z3")  当调用push时,模板也需要重新渲染
    // 在vue2.x中重写了数组的7个方法,在7个方法中更新了视图
    // 监听数据,数据劫持
    observer(data)
    // 当数据变了,要更新视图
    // console.log(data.name);
    // data.name = "malu";
    // console.log(data.name);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

# 十三. 防抖实现

<!-- 
说说你对防抖、 节流的理解, 以及它们的区别和应用场景?
防抖: 将多次执行函数变成最后⼀ 次执行 等待固定时间还没有事件触发时执行的函数
应用场景:
    按钮的点击
    屏幕滚动时的复杂计算
    输⼊ 框输⼊ 时进行搜索
    用户缩放浏览器的resize事件
简单的防抖函数实现:
-->
<script>
    function myDebounce(execFn, delay) {
        let timer = 0

        function _debounce(...args) {
            if (timer) clearTimeout(timer)
            timer = setTimeout(() => {
                execFn.apply(this, args)
                timer = null
            }, delay)
        }
        return _debounce
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 十四. 节流实现

<!-- 
    节流: 按照固定的时间频率(间隔)来执⾏对应的函数
    应用场景:
        监听页面的滚动事件 通过节流来降低事件调用的频率
        ⿏标移动
        用户频繁点击按钮的操作
    简单实现:
 -->
<script>
    function myThrottle(execFn, interval) {
        let initTime = 0

        function throttle(...args) {
            let nowTime = Date.now()
            const waitTime = interval - (nowTime - initTime)
            if (waitTime <= 0) {
                execFn.apply(this, args)
                initTime = nowTime
            }
        }
        return throttle
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 十五. 数组去重

<script>
    // 利用ES6 Set去重(ES6中最常用)
    function unique(arr) {
        return Array.from(new Set(arr))
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]
</script>
1
2
3
4
5
6
7
8
9
<script>
    // 利用for嵌套for,然后splice去重(ES5中最常用)
    function unique(arr) {
        for (var i = 0; i < arr.length; i++) {
            for (var j = i + 1; j < arr.length; j++) {
                if (arr[i] == arr[j]) { //第⼀个等同于第二个,splice方法删除第二个
                    arr.splice(j, 1);
                    j--;
                }
            }
        }
        return arr;
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    // [1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}]  //NaN和{}没有去重,两个null直接消失了
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
    // 利用indexOf去重
    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return
        }
        var array = [];
        for (var i = 0; i < arr.length; i++) {
            if (array.indexOf(arr[i]) === -1) {
                array.push(arr[i])
            }
        }
        return array;
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    // [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {… }] //NaN、{}没有去重
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    // 利用sort去重
    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return;
        }
        arr = arr.sort()
        var arrry = [arr[0]];
        for (var i = 1; i < arr.length; i++) {
            if (arr[i] !== arr[i - 1]) {
                arrry.push(arr[i]);
            }
        }
        return arrry;
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    // [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined] // NaN、{}没有去重
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
    // 利用includes
    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return
        }
        var array = [];
        for (var i = 0; i < arr.length; i++) {
            if (!array.includes(arr[i])) { //includes 检测数组是否有某个值
                array.push(arr[i]);
            }
        }
        return array
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    // [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
    // {} 没有去重
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
    // 利用filter
    function unique(arr) {
        return arr.filter(function(item, index, arr) {
            //当前元素,在原始数组中的第⼀个索引==当前索引值,否则返回当前元素
            return arr.indexOf(item, 0) === index;
        });
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr))
    //[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]
</script>
1
2
3
4
5
6
7
8
9
10
11
12
<script>
    // 利用递归去重
    function unique(arr) {
        return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
    }
    var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}];
    console.log(unique(arr));
    // [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]
</script>
1
2
3
4
5
6
7
8
9

# 十六. JS继承实现方案

  • 参考:http://47.94.210.129/malulesson/basic/jsplus/06.html

# 十七. 实现柯里化函数

<script>
    function mlcurrying(fn, ...args1) {
        let length = fn.length;
        let allArgs = [...args1];
        let res = (...args2) => {
            allArgs = [...allArgs, ...args2]
            if (allArgs.length == length) {
                return fn(...allArgs)
            } else {
                return res;
            }
        }
        return res;
    }

    // 测试:
    const add = (a, b, c) => a + b + c;
    const a = mlcurrying(add, 1);
    console.log(a(2, 3)) // 6

    const b = mlcurrying(add, 1, 2, 3);
    console.log(b()) // 6

    const c = mlcurrying(add);
    console.log(c(1, 2, 3)) // 6

    const d = mlcurrying(add, 1)
    console.log(d(2)(3)); // 6
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 十八. 使用setTimeout实现setInterval

<script>
    // 使用setTimeout实现setInterval
    function mlSetTimout(fn, delay) {
        let timer = null;
        let interval = () => {
            fn();
            timer = setTimeout(interval, delay)
        }
        setTimeout(interval, delay)

        return {
            cancel() {
                clearTimeout(timer)
            }
        }
    }

    // mlSetTimout(() => console.log(888), 1000);

    let {
        cancel
    } = mlSetTimout(() => console.log(888), 1000);
    setTimeout(() => {
        cancel();
    }, 5000);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 十九. 简单实现hash路由

<html>
<style>
    html,
    body {
        margin: 0;
        height: 100%;
    }

    ul {
        list-style: none;
        margin: 0;
        padding: 0;
        display: flex;
        justify-content: center;
    }

    .box {
        width: 100%;
        height: 100%;
        background-color: red;
    }
</style>

<body>
    <ul>
        <li>
            <a href="#red">红色</a>
        </li>
        &nbsp;&nbsp;&nbsp;
        <li>
            <a href="#green">绿色</a>
        </li>
        &nbsp;&nbsp;&nbsp;
        <li>
            <a href="#purple">紫色</a>
        </li>
    </ul>
    <div class="box"></div>

    <!-- <script>
        let box = document.getElementsByClassName("box")[0];

        window.onhashchange = function () {
            let color = location.hash.slice(1);
            box.style.background = color
        }
    </script> -->

    <script>
        // 封装成一个类
        let box = document.getElementsByClassName("box")[0];

        class HashRouter {
            constructor(hashStr, cb) {
                this.hashStr = hashStr;
                this.cb = cb;
                this.watchHash()
                window.addEventListener("hashchange", this.watchHash.bind(this))
            }
            watchHash() {
                let hash = location.hash.slice(1)
                this.hashStr = hash;
                this.cb(this.hashStr)
            }
        }

        new HashRouter("red", (color) => {
            box.style.background = color;
        });
    </script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

# 二十. 手写Promise.all的实现

<script>
    // 类方法(静态方法) all

    // 创建三个Promise
    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve("p1 resolve")
            reject("p1 reject error")
        }, 3000)
    })
    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve("p2 resolve")
            reject("p2 reject error")
        }, 2000)
    })
    const p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("p3 resolve")
        }, 5000)
    })

    // const p4 = 110;

    Promise.all = function(promises) {
        return new Promise((resolve, reject) => {
            let result = [];
            let index = 0;
            let len = promises.length;
            if (len === 0) {
                resolve(result)
                return;
            }
            for (let i = 0; i < len; i++) {
                // promises[i]
                Promise.resolve(promises[i]).then(data => {
                    result[i] = data;
                    index++;
                    if (index === len) {
                        resolve(result)
                    }
                }).catch(err => {
                    reject(err)
                })
            }
        })
    }

    // 类方法(静态方法) all
    // all的作用:所有promise都成功后,得到所有成功后的promise结果
    //    如果有一个先失败了,直接得到最先失败promise的结果
    Promise.all([p1, p2, p3]).then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err);
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

# 二十一. 手写Promise.allSettled的实现

<script>
    // 类方法(静态方法) allSettled

    // 创建三个Promise
    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("p1 resolve")
            // reject("p1 reject error")
        }, 3000)
    })
    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("p2 resolve")
            // reject("p2 reject error")
        }, 2000)
    })
    const p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("p3 resolve")
        }, 5000)
    })

    function isPromise(val) {
        return typeof val.then === "function"
    }
    Promise.allSettled = function(promises) {
        return new Promise((resolve, reject) => {
            let arr = [];
            let times = 0;
            let setData = (index, data) => {
                arr[index] = data;
                if (++times === promises.length) {
                    resolve(arr)
                }
            }

            for (let i = 0; i < promises.length; i++) {
                let current = promises[i];
                if (isPromise(current)) {
                    current.then(data => {
                        setData(i, {
                            status: 'fulfilled',
                            value: data
                        })
                    }, err => {
                        setData(i, {
                            status: 'rejected',
                            value: err
                        })
                    })
                } else {
                    setData(i, {
                        status: 'rejected',
                        value: current
                    })
                }
            }
        })
    }

    // allSettled  获取所有的promise的结果,不管成功还是失败
    Promise.allSettled([p1, p2, p3]).then(res => {
        console.log("all settled:", res)
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

# 二十二. 手写Promise.race的实现

<script>
    // 创建三个Promise
    const p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve("p1 resolve")
            reject("p1 reject error")
        }, 3000)
    })

    const p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            // resolve("p2 resolve")
            reject("p2 reject error")
        }, 2000)
    })

    const p3 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("p3 resolve")
        }, 5000)
    })

    Promise.race = function(promises) {
        return new Promise((resolve, reject) => {
            let len = promises.length;
            if (len === 0) return;
            for (let i = 0; i < len; i++) {
                Promise.resolve(promises[i]).then(data => {
                    resolve(data)
                    return;
                }).catch(err => {
                    reject(err);
                    return;
                })
            }
        })
    }

    // 类方法: race方法    race是比赛的意思
    // 特点: 会等到第一个Promise有结果(无论这个结果是fulfilled还是rejected)
    Promise.race([p1, p2, p3]).then(res => {
        console.log("race promise:", res)
    }).catch(err => {
        console.log("race promise err:", err)
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 二十三. 实现 (5).add(3).minus(2)

<script>
    // 实现 (5).add(3).minus(2) 功能

    Number.prototype.add = function(n) {
        return this.valueOf() + n;
    }
    Number.prototype.minus = function(n) {
        return this.valueOf() - n;
    }

    // console.log((5).add(3));
    console.log((5).add(3).minus(2));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 二十四. alert(add(1)(2)(3)(4)(5))

<script>
    // 实现 alert(add(1)(2)(3)(4)(5))

    function add(x) {
        let sum = x; // sum是存储和
        let tmp = function(y) {
            sum = sum + y;
            return tmp;
        }
        tmp.toString = function() {
            return sum;
        }
        return tmp;
    }

    // 当alert时,会调用toString方法
    alert(add(1)(2)(3)(4)(5))
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 二十五. 工具函数之封装判断数据类型的函数

<script>
    // 工具函数之封装判断数据类型的函数
    // Object.prototype.toString()
    function getType(data) {
        if (data === null) return String(data)
        return typeof data === "object" ? Object.prototype.toString.call(data).replace('[object ', '').replace(']', '').toLowerCase() : typeof data
    }

    // 调用
    console.log(getType(null)); // -> null
    console.log(getType(undefined)); // -> undefined
    console.log(getType({})); // -> object
    console.log(getType([])); // -> array
    console.log(getType(123)); // -> number
    console.log(getType(true)); // -> boolean
    console.log(getType('码路')); // -> string
    console.log(getType(/123/)); // -> regexp
    console.log(getType(new Date())); // -> date
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 二十六. 实现深度比较isEqual

<script>
    // 是不是对象
    function isObject(obj) {
        return typeof obj === "object" && obj !== null;
    }
    // 深度比较
    function isEqual(obj1, obj2) {
        if (!isObject(obj1) || !isObject(obj2)) {
            // 说明其中有一个数据是基本数据类型
            return obj1 === obj2
        }
        // 两个都是对象或数组,比较key的个数是否相同
        let obj1Keys = Object.keys(obj1)
        let obj2Keys = Object.keys(obj2)
        if (obj1Keys.length !== obj2Keys.length) {
            return false;
        }

        // 两个对象或数组中的key的个数是一样的
        // 以其中一个为基准,遍历,递归比较
        for (let key in obj1) {
            // console.log(obj1[key]);
            let res = isEqual(obj1[key], obj2[key])
            if (!res) {
                return false;
            }
        }

        // 全相等
        return true;
    }

    let obj1 = {
        a: 1,
        b: 2,
        c: {
            d: "d",
            e: 'e'
        }
    }
    let obj2 = {
        a: 1,
        b: 2,
        c: {
            d: "d",
            e: 'e'
        }
    }

    console.log(isEqual(obj1, obj2)); // true
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 二十七. 数组并集,交集,差集

<script>
    // 并集
    // [1,2,3] 并 [4,5,6] ==> [1,2,3,4,5,6]
    // [1,2,3,3] 并 [4,5,6,6] ==> [1,2,3,4,5,6]
    // [1,2,3,3] 并 [3,6,6] ==> [1,2,3,6]
    let arr1 = [1, 2, 3, 3];
    let arr2 = [4, 5, 6, 3, 2, 4, 9];

    function union(arr1, arr2) {
        let res = new Set([...arr1, ...arr2])
        return [...res]
    }
    let newArr = union(arr1, arr2);
    console.log(newArr)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    // 交集
    // [1,2,3] 交 [3,4,5,6] ==> [3]
    // [1,2,3] 交 [7,8,9] ==> []
    // [1,2,3] 交 [1,2,3] ==> [1,2,3]
    let arr1 = [1, 2, 3, 3];
    let arr2 = [3, 4, 5, 5];

    function intersection(arr1, arr2) {
        let s1 = new Set(arr1)
        let s2 = new Set(arr2)
        let newArr = [...s1].filter(item => {
            return s2.has(item)
        })
        return newArr
    }
    let newArr = intersection(arr1, arr2);
    console.log(newArr)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    // 差集
    // [1,2,3] 差 [3,4,5] ==> [1,2]
    // [3,4,5] 差 [1,2,3] ==> [4,5]

    let arr1 = [1, 2, 3];
    let arr2 = [3, 4, 5];

    function except(arr1, arr2) {
        let s1 = new Set(arr1)
        let s2 = new Set(arr2)
        let newArr = [...s1].filter(item => {
            return !s2.has(item)
        })
        return newArr;
    }

    console.log(except(arr1, arr2)) // [1,2]
    console.log(except(arr2, arr1)) // [4,5]
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 二十八. 实现map方法

<script>
    // 实现数组的map方法
    Array.prototype.mlmap = function(fn) {
        let newArr = [];

        // this   arr打点调用mlmap方法,mlmap方法中this表示arr
        for (let i = 0; i < this.length; i++) {
            let res = fn(this[i], i, this)
            newArr.push(res)
        }

        return newArr;
    };
    var arr = [1, 3, 6];
    // map可以对数组中每一个元素进行加工
    // map返回加工后的新数组
    var result = arr.mlmap(function(element, index, array) {
        console.log("index", index);
        console.log("array", array);
        return element * element;
    });
    console.log("result: ===", result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 二十九. 实现数组find方法

<script>
    // 实现数组find方法
    Array.prototype.mlfind = function(fn) {
        // this是arr
        for (let i = 0; i < this.length; i++) {
            let res = fn(this[i])
            if (res) {
                // 表示元素满足条件
                return this[i];
            }
        }
    };

    var arr = [1, 3, 6, 90, 23];
    // find:查找满足条件的第1个元素
    // item表示数组中每一个元素
    var result = arr.mlfind(function(item) {
        // 指定查找条件
        return item > 6;
    });
    console.log(result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 三十. 实现filter函数

<script>
    //模拟实现filter函数
    Array.prototype.mlfilter = function(fn) {
        let newArr = [];
        // this表示array
        for (let i = 0; i < this.length; i++) {
            let res = fn(this[i])
            if (res) {
                newArr.push(this[i])
            }
        }
        return newArr;
    };

    var array = [65, 56, 89, 53];
    // filter是用来过滤数组中符合某个条件的元素
    var arr = array.mlfilter(function(item) {
        return item >= 60;
    });
    console.log("arr=>", arr);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 三十一. 实现some方法

<script>
    //实现some方法
    Array.prototype.mlsome = function(fn) {
        // this 表示array
        for (let i = 0; i < this.length; i++) {
            let res = fn(this[i])
            if (res) {
                return res;
            }
        }
        return false
    };

    let array = [1, 3, 5, 7, 90];
    // 只要数组中一个元素满足条件,结果就true
    let result = array.mlsome(function(item) {
        return item > 10;
    });
    console.log("result=>", result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 三十二. 实现every方法

<script>
    //手动模拟实现every方法
    Array.prototype.mlevery = function(fn) {
        let flag = true;
        // this
        for (let i = 0; i < this.length; i++) {
            let res = fn(this[i])
            if (!res) {
                // 有一个元素不满足条件
                return false;
            }
        }

        return flag
    };

    let array = [11, 31, 5, 71, 90];

    // 如果数组中每一个元素都满足条件,结果就是true
    // 只要有一个不满足结果就是false
    let result = array.mlevery(function(item) {
        return item > 10;
    });
    console.log("result=", result);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 三十三. 实现findIndex方法

<script>
    // findIndex的实现
    Array.prototype.mlfindIndex = function(fn) {
        // this  表示arr  
        for (let i = 0; i < this.length; i++) {
            if (fn(this[i], i)) {
                return i
            }
        }
    }

    let arr = [1, 3, 4, 5, 7, 8];
    // 找到满足条件的第1个元素的索引
    let res = arr.mlfindIndex((item, index) => {
        return item % 2 === 1
    })
    console.log(res)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 三十四. 实现reduce方法

<script>
    // 实现reduce方法
    Array.prototype.mlreduce = function(fn, initVal) {
        // 给prev传递了初始化参数,也就是说有初始化参数
        let hasInitVal = initVal !== undefined
        let value = hasInitVal ? initVal : this[0]
        for (let i = hasInitVal ? 0 : 1, len = this.length; i < len; i++) {
            value = fn(value, this[i], i, this)
        }
        return value;
    };
    var arr = [1, 2, 3];
    // reduce是个累加器
    // 第1次:prev:1   next:2
    // 第2次:prev:3   next:3
    // 第3次:prev:6   next:无
    // prev表示之前累加和  next表示下一项
    // 在调用reduce时可以给prev传递初始化参数
    var sum = arr.mlreduce(function(prev, next, index, arr) {
        // console.log("prev", prev);
        // console.log("next", next);
        return prev + next;
    }, 10);
    console.log(sum);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 三十五. 实现concat方法

<script>
    // 实现concat方法
    Array.prototype.mlconcat = function myconcat(...values) {
        let newArr = [];
        newArr.push(...this)

        if (values.length === 0) {
            return newArr
        }
        values.forEach(value => {
            if (Array.isArray(value)) {
                newArr.push(...value)
            } else {
                newArr.push(value)
            }
        })

        return newArr;
    }
    let arr1 = [1, 2];
    let arr2 = [3, 4];
    let arr3 = [5, 6];
    let arr4 = [7, 8];
    // concat拼接多个数组形成一个数组
    // 拼接也可以是一个普通值  返回一个新的数组
    console.log(arr1.mlconcat(arr2, arr3, arr4, "hello"))
    console.log(arr1.mlconcat("malu"))
    console.log(arr1.mlconcat())
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 三十六. 实现slice方法

<script>
    // 实现slice方法
    Array.prototype.mlslice = function(start, end) {
        let newArr = [];

        // this表示arr1
        // 如果要截取的数组是一个空数组,什么也不做,直接返回空数组
        if (this.length === 0) {
            return newArr;
        }

        // 处理start特殊情况
        start = start || 0
        if (start < 0) {
            start = this.length + start
        } else if (start >= this.length) {
            return newArr;
        }

        // 处理end的特殊的情况
        end = end || this.length;
        if (end > this.length) {
            end = this.length;
        } else if (end <= start) {
            end = this.length + end;
        }

        for (let i = start; i < end; i++) {
            newArr.push(this[i])
        }

        return newArr;
    }

    // slice() 方法可从已有的数组中返回选定的元素。
    // 返回一个新的数组,包含从 start(包括该元素) 到 end (不包括该元素)的 arrayObject 中的元素。

    // start:规定从何处开始选取。如果该参数为负数,
    // 则表示从原数组中的倒数第几个元素开始提取,
    // slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)

    // end:规定从何处结束选取。该参数是数组片断结束处的数组下标。
    // 如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。
    // 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 
    // slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)
    let arr1 = [1, 3, 4, 5, 7, 9];
    let res1 = arr1.mlslice(2, 4); // 2 4 代表都是索引
    console.log(res1); //  [4, 5]

    let res2 = arr1.mlslice(-2, -1);
    console.log(res2); // [7]

    let res3 = arr1.mlslice();
    console.log(res3) // [1, 3, 4, 5, 7, 9]

    let res4 = arr1.mlslice(2);
    console.log(res4) // [1, 3, 4, 5, 7, 9]
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# 三十七. 实现发布订阅

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <button class="btn">点我</button>
    <script>
        // 实现发布订阅 事件总线
        // 订阅  发布
        class MyEventBus {
            constructor() {
                // {"malu":[f1,f2]}
                this.eventMap = {}
            }

            on(eventName, eventFn) {
                let eventFns = this.eventMap[eventName]
                if (!eventFns) {
                    eventFns = [];
                    this.eventMap[eventName] = eventFns
                }
                eventFns.push(eventFn)
            }

            off(eventName, eventFn) {
                let eventFns = this.eventMap[eventName];
                if (!eventFns) return;
                // {eat:[f1,f2,f3...]}
                for (let i = 0; i < eventFns.length; i++) {
                    let fn = eventFns[i];
                    if (fn === eventFn) {
                        eventFns.splice(i, 1)
                        break;
                    }

                }
                // 如果eventFns已经清空了
                if (eventFns.length === 0) {
                    delete this.eventMap[eventName]
                }
            }

            emit(eventName, ...args) {
                let eventFns = this.eventMap[eventName];
                if (!eventFns) return;
                eventFns.forEach(fn => {
                    fn(...args)
                })
            }
        }

        // 使用过程
        const eventBus = new MyEventBus()

        // 实现订阅
        eventBus.on("malu", (name, age, height) => {
            console.log("malu事件监听器01:", name, age, height);
        })

        let eat = (name, age, height) => {
            console.log("eat事件监听器02", name, age, height);
        };
        eventBus.on("eat", eat)

        setTimeout(() => {
            eventBus.off("eat", eat)
        }, 3000)

        const btn = document.querySelector(".btn")
        btn.onclick = function() {
            // 发布
            // eventBus.emit("malu", "wc", 18, 1.88)
            eventBus.emit("eat", "wc", 18, 1.88)
        }
    </script>

</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

# 三十八. 实现Promise的resolve

<script>
    // 实现 resolve 静态方法有三个要点:
    //      传参为一个 Promise, 则直接返回它。
    //      传参为一个 thenable 对象,返回的 Promise 会跟随这个对象,
    //          采用它的最终状态作为自己的状态。
    //      其他情况,直接返回以该值为成功状态的promise对象。

    // 实现Promise中resolve静态方法
    Promise.resolve = (param) => {
        if (param instanceof Promise) return param
        return new Promise((resolve, reject) => {
            if (param && param.then && typeof param.then === "function") {
                // console.log("传递的是一个thenable");
                param.then(resolve, reject)
            } else {
                resolve(param)
            }
        })
    }

    const p1 = new Promise((resolve, reject) => {
        resolve("p1 resolve")
        // reject("p1 reject error")
    })

    // Promise.resolve("码路").then(res => {
    // Promise.resolve(p1).then(res => {
    Promise.resolve({
        then(resolve, reject) {
            resolve("p1 resolve")
        }
    }).then(res => {
        console.log("then结果:", res)
    })

    // 相当于
    // new Promise((resolve) => {
    //   resolve("hello")
    // })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 三十九. 迭代器

<script>
    // 什么是迭代器
    const names = ["码路", "漫漫", "前端"];

    // 给数组names创建一个迭代器(迭代器: names的迭代器)
    let index = 0

    // 迭代器就是一个对象,要求这个对象必须有一个next方法
    // Iterator 是迭代器  就可以把一个容器中的元素迭代出来
    const namesIterator = {
        next() {
            if (index < names.length) {
                return {
                    done: false,
                    value: names[index++]
                }
            } else {
                return {
                    done: true,
                    value: undefined
                }
            }
        }
    };
    // 调用next返回一个对象,done表示是否迭代完毕,value表示迭代出来的数据
    console.log(namesIterator.next()); // { done:false,value:"码路" }
    console.log(namesIterator.next()); // { done:false,value:"漫漫" }
    console.log(namesIterator.next()); // { done:false,value:"前端" }
    console.log(namesIterator.next()); // { done:false,value:undefined }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<script>
    const nums = [100, 200, 300, 400, 500]

    let index = 0;
    let numsIterator = {
        next() {
            if (index < nums.length) {
                return {
                    done: false,
                    value: nums[index++]
                }
            } else {
                return {
                    done: true,
                    value: undefined
                }
            }
        }
    }

    console.log(numsIterator.next()); // { done: false, value: 100 }
    console.log(numsIterator.next()); // { done: false, value: 200 }
    console.log(numsIterator.next()); // { done: false, value: 300 }
    console.log(numsIterator.next()); // { done: false, value: 400 }
    console.log(numsIterator.next()); // { done: false, value: 500 }
    console.log(numsIterator.next()); // { done: false, value: undefined }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>
    // 封装一个函数,返回一个数组迭代器(Iterator)
    const nums = [100, 200, 300, 400, 500]
    const names = ["码路", "漫漫", "前端"];

    function createArrayIterator(arr) {
        let index = 0;
        return {
            next() {
                if (index < arr.length) {
                    return {
                        done: false,
                        value: arr[index++]
                    }
                } else {
                    return {
                        done: true,
                        value: undefined
                    }
                }
            }
        }
    }
    let namesIterator = createArrayIterator(names);
    console.log(namesIterator.next());
    console.log(namesIterator.next());
    console.log(namesIterator.next());

    let numsIterator = createArrayIterator(nums);
    console.log(numsIterator.next());
    console.log(numsIterator.next());
    console.log(numsIterator.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 四十. 可迭代对象

<meta charset="utf-8" />
<script>
    // 可迭代对象  
    // 满足条件1:必须实现一个特定的函数: [Symbol.iterator]
    // 满足条件2:这个函数需要返回一个迭代器(这个迭代器用于迭代当前的对象)

    // infos就可以叫做可迭代对象
    let infos = {
        friends: ["码路", "漫漫", "前端"],
        [Symbol.iterator]() {
            let index = 0;
            let infosIterator = {
                next() {
                    if (index < infos.friends.length) {
                        return {
                            done: false,
                            value: infos.friends[index++]
                        }
                    } else {
                        return {
                            done: true,
                            value: undefined
                        }
                    }
                }
            }
            return infosIterator;
        }
    };
    // 返回了迭代器
    // let iterator = infos[Symbol.iterator]();
    // console.log(iterator.next());
    // console.log(iterator.next());
    // console.log(iterator.next());
    // console.log(iterator.next());

    // 只要是一个可迭代对象,都可以使用for of进行遍历
    // infos is not iterable
    for (let item of infos) {
        console.log(item);
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
 <script>
     // 可迭代对象
     let infos2 = {
         name: "码路",
         age: 18,
         height: 1.88,
         [Symbol.iterator]() {
             let entries = Object.entries(this);
             // console.log("entries:", entries);
             // iterator迭代器
             // iterable可迭代的
             let index = 0;
             let infos2Iterator = {
                 next() {
                     if (index < entries.length) {
                         return {
                             done: false,
                             value: entries[index++]
                         }
                     } else {
                         return {
                             done: true,
                             value: undefined
                         }
                     }
                 }
             }
             return infos2Iterator;
         }
     };
     // 可迭代对象必然具备下面的特点
     // const iterator = infos2[Symbol.iterator]()
     // console.log(iterator.next());
     // console.log(iterator.next());
     // console.log(iterator.next());
     // console.log(iterator.next());

     // 只要是可迭代对象,都可以使用for of
     for (let item of infos2) {
         console.log(item);
     }
 </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script>
    // JS中哪些容器天生就是可迭代对象
    // 1)数组
    // const names = ["aaa", "bbb", "ccc"]
    // for (const name of names) {
    //     console.log(name)
    // }
    // let iterator = names[Symbol.iterator](); // 得到迭代器
    // console.log(iterator.next());
    // console.log(iterator.next());
    // console.log(iterator.next());
    // console.log(iterator.next());

    // console.log("---------------");

    // 2)Set
    // new Set时 ()中只要写可迭代对象都可以
    // ["aaa", "bbb", "ccc"] 就是一个迭代对象
    // const set = new Set(["aaa", "bbb", "ccc"])
    // for (const item of set) {
    //     console.log(item)
    // }
    // const setIterator = set[Symbol.iterator]()
    // console.log(setIterator.next())
    // console.log(setIterator.next())
    // console.log(setIterator.next())
    // console.log(setIterator.next())

    // console.log("---------------");

    // 3)arguments
    // function foo() {
    //     for (const arg of arguments) {
    //         console.log(arg)
    //     }
    // }
    // foo(111, 222, 333, 444)

    // console.log("---------------");

    // 4)字符串
    // let str = "hello";
    // for (let item of str) {
    //     console.log(item);
    // }

    // console.log("---------------");

    // 对象不是可迭代对象
    // let obj = {
    //     name: "wc",
    //     age: 18,
    //     sex: "man"
    // }
    // // TypeError: obj is not iterable
    // // iterable 可迭代的
    // for (let item of obj) {
    //     console.log(item);
    // }

    let obj = {
        a: 1,
        b: 2,
        c: 3
    }
    let res = {
        ...obj
    }
    console.log(res);

    function fn(x, y, z) {
        console.log(x, y, z);
    }
    fn(obj.a, obj.b, obj.c)
    // requires ...iterable[Symbol.iterator] to be a function
    fn(...obj)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# 四十一. 迭代对象的应用场景

<meta charset="utf-8">
<script>
    // 之前说过: 迭代器  可迭代对象
    // 可迭代对象的应用场景: 把一个非可迭代对象 转化成 可迭代对象
    const info = {
        name: "码路",
        age: 18,
        height: 1.88,
        [Symbol.iterator]() {
            let values = Object.values(this)
            // console.log(values);
            let index = 0;
            let iterator = {
                next() {
                    if (index < values.length) {
                        return {
                            done: false,
                            value: values[index++]
                        }
                    } else {
                        return {
                            done: true,
                            value: undefined
                        }
                    }
                }
            }
            return iterator;
        }
    };

    let iterator = info[Symbol.iterator]();
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<meta charset="utf-8">
<script>
    // 可迭代对象的应用场景: 一些类的构造方法中, 也是传入的可迭代对象
    // 语法:new Set([iterable])
    const set = new Set(["aaa", "bbb", "ccc"])
    console.log(set);
    const set2 = new Set("hello")
    console.log(set2);
    const set3 = new Set(set2)
    console.log(set3);
</script>
1
2
3
4
5
6
7
8
9
10
11
<meta charset="utf-8">
<script>
    // 可迭代对象的应用场景: 一些常用的方法
    // const p1 = Promise.resolve("aaaa")
    // const p2 = Promise.resolve("bbbb")
    // const p3 = Promise.resolve("cccc")

    // let set = new Set();
    // set.add(p1)
    // set.add(p2)
    // set.add(p3)

    // // all([iterable])
    // Promise.all(set).then(res => {
    //     console.log("res:", res);
    // })

    function bar() {
        // arguement不仅是一个伪数组,也是一个可迭代对象
        console.log(arguments);

        for (let item of arguments) {
            console.log(item);
        }
    }

    bar(111, 222, 333);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<meta charset="utf-8">
<script>
    // 可迭代对象的应用场景: 自定义类的迭代
    class Person {
        constructor(name, age, height, friends) {
            this.name = name
            this.age = age
            this.height = height
            this.friends = friends
        }

        // 实例方法
        running() {}
            [Symbol.iterator]() {
                let index = 0;
                let iterator = {
                    next: () => {
                        if (index < this.friends.length) {
                            return {
                                done: false,
                                value: this.friends[index++]
                            }
                        } else {
                            return {
                                done: true,
                                value: undefined
                            }
                        }
                    }
                }
                return iterator;
            }
    }

    // p1和p2不是可迭代对象
    const p1 = new Person("码路", 18, 1.88, ["aaa", "bbb", "ccc"])
    const p2 = new Person("漫漫", 30, 1.98, ["111", "222", "333"])

    for (const item of p2) {
        console.log(item)
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<meta charset="utf-8">
<script>
    // 可迭代对象的应用场景: 自定义类的迭代
    class Person {
        constructor(name, age, height, friends) {
            this.name = name
            this.age = age
            this.height = height
            this.friends = friends
        }

        // 实例方法
        running() {}
            *[Symbol.iterator]() {

                yield* this.friends;

                // let index = 0;
                // let iterator = {
                //     next: () => {
                //         if (index < this.friends.length) {
                //             return { done: false, value: this.friends[index++] }
                //         } else {
                //             return { done: true, value: undefined }
                //         }
                //     }
                // }
                // return iterator;
            }
    }

    // p1和p2不是可迭代对象
    const p1 = new Person("码路", 18, 1.88, ["aaa", "bbb", "ccc"])
    const p2 = new Person("漫漫", 30, 1.98, ["111", "222", "333"])

    for (const item of p2) {
        console.log(item)
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# 四十二. 生成器函数

<meta charset="utf-8">
<script>
    // 迭代器和可迭代对象学习

    // 生成器函数:
    //     1.function后面会跟上符号: *
    //         2.代码的执行可以被yield控制
    //     3.生成器函数默认在执行时, 返回一个生成器对象
    //         * 要想执行函数内部的代码, 需要生成器对象, 调用它的next操作
    //         * 当遇到yield时, 就会中断执行

    // 生成器函数
    function* fn() {
        console.log("aaa");
        console.log("bbb");
        yield
        console.log("ccc");
        console.log("ddd");
        yield
        console.log("eee");
        console.log("fff");
    }
    // 调用生成器函数,得到生成器对象
    // generator 是生成器的意思
    let generator = fn();
    // 生成器也有一个next方法
    generator.next()
    generator.next()
    generator.next()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<meta charset="utf-8">
<script>
    // 生成器函数
    // yield叫产出  产出数据
    function* fn() {
        console.log("aaa");
        console.log("bbb");
        yield "1"
        console.log("ccc");
        console.log("ddd");
        yield "2"
        console.log("eee");
        console.log("fff");
    }
    let generator = fn();
    // 第1次调用next就执行第1个yield之前的代码
    // 第2次调用next就执行第2个yield之前的代码
    // ... 
    console.log(generator.next()); // {value: '1', done: false}
    console.log(generator.next()); // {value: '2', done: false}
    console.log(generator.next()); // {value: undefined, done: false}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<meta charset="utf-8">
<script>
    // 生成器函数
    // yield叫产出  产出数据
    function* fn() {
        console.log("aaa");
        console.log("bbb");
        let num1 = yield "1"
        console.log("num1:", num1);
        console.log("ccc");
        console.log("ddd");
        let num2 = yield "2"
        console.log("num2:", num2);
        console.log("eee");
        console.log("fff");
    }
    let generator = fn();
    // 第1次调用next就执行第1个yield之前的代码
    // 第2次调用next就执行第2个yield之前的代码
    // ... 
    // 调用next时,可以给生成器函数传递参数   参数传递到yield前面
    console.log(generator.next('666')); // {value: '1', done: false}
    console.log(generator.next('888')); // {value: '2', done: false}
    console.log(generator.next('333')); // {value: undefined, done: false}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<meta charset="utf-8">
<script>
    function* fn() {
        let num1 = yield "1"
        console.log("num1:", num1);
        let num2 = yield "2"
        console.log("num2:", num2);
    }
    let generator = fn();
    // 第1次调用next方法,传递的参数,生成器函数是不能接收的
    // 第2次调用next方法,传递的参数,传递给了第1个yeild前面的变量
    // 第3次调用next方法,传递的参数,传递给了第2个yeild前面的变量
    console.log(generator.next('666')); // {value: '1', done: false}
    console.log(generator.next('888')); // {value: '2', done: false}
    console.log(generator.next('333')); // {value: undefined, done: false}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<meta charset="utf-8">
<script>
    function* foo(name1) {
        console.log("内部代码:111", name1);
        console.log("内部代码:222", name1);
        let name2 = yield "aaa"
        console.log("内部代码:333", name2);
        console.log("内部代码:444", name2);
        let name3 = yield "bbb"
        console.log("内部代码:555", name3);
        console.log("内部代码:666", name3);
        yield "ccc"
    }

    let generator = foo("malu")
    console.log(generator.next('666'));
    console.log(generator.next('888'));
    console.log(generator.next('333'));
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<meta charset="utf-8">
<script>
    // 使用生成器 替代 之前讲的 迭代器
    let names = ["码路", "漫漫", "前端"];

    // 生成器函数
    function* createArrayGenerator(arr) {

        for (let i = 0; i < arr.length; i++) {
            yield arr[i]
        }

        // yield arr[0]
        // yield arr[1]
        // yield arr[2]
    }
    // namesGen生成器对象
    let namesGen = createArrayGenerator(names)
    console.log(namesGen.next());
    console.log(namesGen.next());
    console.log(namesGen.next());
    console.log(namesGen.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>
    // 使用生成器 生成某个范围的值

    // 生成器函数
    function* createRangeGenerator(start, end) {

        for (let i = start; i <= end; i++) {
            yield i
        }

    }
    let rangeGen = createRangeGenerator(2, 5)
    console.log(rangeGen.next());
    console.log(rangeGen.next());
    console.log(rangeGen.next());
    console.log(rangeGen.next());
    console.log(rangeGen.next());
    console.log(rangeGen.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    let names = ["码路", "漫漫", "前端"];

    // 生成器函数
    function* createArrayGenerator(arr) {

        // yield语法糖
        yield* arr

        // for (let i = 0; i < arr.length; i++) {
        //     yield arr[i]
        // }

        // yield arr[0]
        // yield arr[1]
        // yield arr[2]
    }
    // namesGen生成器对象
    let namesGen = createArrayGenerator(names)
    console.log(namesGen.next());
    console.log(namesGen.next());
    console.log(namesGen.next());
    console.log(namesGen.next());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 四十三. 异步解决方案

<meta charset="utf-8">
<script>
    // 异步解决方案  如果优雅地解决异步问题
    //   前置内容:
    //       promise  迭代器  可迭代对象  生成器
    // 异步的解决方案
    //   1)回调函数  回调地狱

    // 需求: 
    //     1.发送一次网络请求, 等到这次网络请求的结果  传学生的编号  得到  学生的姓名
    //     2.发送第二次网络请求, 等待这次网络请求的结果  传学生的姓名  得到 学生的成绩
    //     3.发送第三次网络请求, 等待这次网络请求的结果  传学生的成绩  得到 成绩的等级

    function ajax(data) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(data)
            }, 2000)
        })
    }

    // 一层屋嵌套  不优雅
    function getData() {
        // 第一次发送ajax
        ajax("001").then(res1 => {
            console.log("res1是学生的姓名");
            // 第二次发送ajax
            ajax(res1).then(res2 => {
                console.log("res2是学生的成绩");
                // 第三次发送ajax
                ajax(res2).then(res3 => {
                    console.log("res3是学生的等级");
                })
            })
        })
    }

    getData()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<meta charset="utf-8">
<script>
    // 异步解决方案  如果优雅地解决异步问题
    //   前置内容:
    //       promise  迭代器  可迭代对象  生成器
    // 异步的解决方案
    //   1)回调函数  回调地狱
    //   2)then链

    // 需求: 
    //     1.发送一次网络请求, 等到这次网络请求的结果  传学生的编号  得到  学生的姓名
    //     2.发送第二次网络请求, 等待这次网络请求的结果  传学生的姓名  得到 学生的成绩
    //     3.发送第三次网络请求, 等待这次网络请求的结果  传学生的成绩  得到 成绩的等级

    function ajax(data) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(data)
            }, 2000)
        })
    }

    // 一层屋嵌套  不优雅
    function getData() {
        // 第一次发送ajax
        ajax("001").then(res1 => {
            console.log("res1是学生的姓名");
            return ajax(res1)
        }).then(res2 => {
            console.log("res2是学生的成绩");
            return ajax(res2)
        }).then(res3 => {
            console.log("res3是学生的等级");
        })
    }

    getData()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<meta charset="utf-8">
<script>
    // 异步解决方案  如果优雅地解决异步问题
    //   前置内容:
    //       promise  迭代器  可迭代对象  生成器
    // 异步的解决方案
    //   1)回调函数  回调地狱
    //   2)then链
    //   3)生成器函数(要学习生成器函数,必须学习迭代器)

    // 需求: 
    //     1.发送一次网络请求, 等到这次网络请求的结果  传学生的编号  得到  学生的姓名
    //     2.发送第二次网络请求, 等待这次网络请求的结果  传学生的姓名  得到 学生的成绩
    //     3.发送第三次网络请求, 等待这次网络请求的结果  传学生的成绩  得到 成绩的等级

    function ajax(data) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(data)
            }, 2000)
        })
    }

    // 生成器函数
    function* getData() {
        let res1 = yield ajax("001")
        let res2 = yield ajax(res1)
        let res3 = yield ajax(res2)
        console.log("res3:", "等级");
    }

    // 下面这一片代码能不能优化呢?
    //   TJ  co
    getData().next().value.then(res1 => {
        console.log("res1是学生的姓名");
        getData().next(res1).value.then(res2 => {
            console.log("res2是学生的成绩");
            getData().next(res2).value.then(res3 => {
                console.log("res3是学生的等级");
                // getData().next(res3)
            })
        })
    })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<meta charset="utf-8">
<script>
    // 异步解决方案  如果优雅地解决异步问题
    //   前置内容:
    //       promise  迭代器  可迭代对象  生成器
    // 异步的解决方案
    //   1)回调函数  回调地狱
    //   2)then链
    //   3)生成器函数(要学习生成器函数,必须学习迭代器)
    //   4)终级方案 async和await

    // 需求: 
    //     1.发送一次网络请求, 等到这次网络请求的结果  传学生的编号  得到  学生的姓名
    //     2.发送第二次网络请求, 等待这次网络请求的结果  传学生的姓名  得到 学生的成绩
    //     3.发送第三次网络请求, 等待这次网络请求的结果  传学生的成绩  得到 成绩的等级

    function ajax(data) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(data)
            }, 2000)
        })
    }

    // 生成器函数  
    // async和await并不能把同步代码变成异步代码
    // async和await 仅仅是生成器的语法糖
    // koa1.x中 生成函数有大量使用
    // react中 有一些中间件,也用到生成器
    // 生成器是必学  迭代器  可迭代对象都需要学
    async function getData() {
        let res1 = await ajax("001")
        console.log("res1是学生的姓名");
        let res2 = await ajax(res1)
        console.log("res2是学生的成绩");
        let res3 = await ajax(res2)
        console.log("res3是学生的等级");
    }

    getData()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Last Updated: 12/25/2022, 10:02:14 PM