迭代器与生成器案例

<!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>

    <ul>

        <li>1</li>

        <li>2</li>

        <li>3</li>

        <li>4</li>

        <li>5</li>

        <li>6</li>

        <li>7</li>

        <li>8</li>

        <li>9</li>

    </ul>

</body>

<script>

    // 1.Proxy的实现

    // (1)、proxy的实现

    var person = { "name": "zhangsan" };  // 创建一个名为 'person' 的对象,该对象包含一个名为 'name' 的属性赋值为 'zhangsan'

    var pro = new Proxy(person, {          // 创建一个名为 'pro' 的代理对象,它会传递给 'person' 对象和一个拦截器对象

        get: function (target, propertr) { // 代理对象的拦截器钩子方法,用于拦截代理对象上的获取值操作

            return "lisi";                // 当访问代理对象中的任何属性时都返回字符串 'lisi'

        }

    });

    console.log(pro.name);                 // 在控制台打印出 "lisi"


 

    // (2)、set方法

    var bankAccount = { "RMB": 1000, "dollar": 0 };  // 创建一个名为 'bankAccount' 的对象,表示银行账户,由人民币和美元组成,并给出了初始值。

    var banker = new Proxy(bankAccount, {             // 创建一个名为 'banker' 的代理对象,并传递给它 'bankAccount' 对象和拦截器对象

        get: function (target, propertr) {            // 拦截获取值操作,以便在访问属性时检查余额是否充足

            if (target[propertr] > 0) {               // 如果需要的货币余额(即 RMB 或 dollar)大于零,则返回余额

                return target[propertr];

            }

            else {                                    // 如果所需余额为零或负数,则返回以下错误消息

                return "余额不足"

            }

        },

        set: function (target, propertr, value) {     // 拦截设置值操作,以便在设置属性时进行检查

            if (!Number.isInteger(value)) {           // 如果设置的值不是整数则返回以下错误消息。

                return "请设置正确的数值"

            }

            target[propertr] = value;                 // 否则更新相应的属性并返回。

        }

    });

    console.log(banker.RMB);                        // 在控制台打印出银行账户中 'RMB' 货币的当前余额。由于 'RMB' 初始值为 1000 ,因此输出 "1000"

    console.log(banker.dollar);                     // 非法获取,不符合条件 "if (target[propertr] > 0)",会在拦截器中返回 "余额不足"。

    banker.dollar = "五百";      // 在代理对象 'banker' 中的 'dollar' 属性上设置字符串值 "五百"。

    console.log(banker.dollar);  // 检索代理对象中的 'dollar' 属性,但由于 "五百" 不是整数,设置操作会失败,因此控制台输出了错误消息 "请设置正确的数值"。

    banker.dollar = "500";       // 在代理对象 'banker' 中的 'dollar' 属性上设置整数值 500。

    console.log(banker.dollar);  // 再次检索代理对象中的 'dollar' 属性来查看其是否成功设置,由于已经将 'dollar' 的值更改为 500,因此控制台打印出 "500"。


 

    // (3)、ownKeys方法

    // ownKeys拦截操作,拦截过滤Object.keys()对对象的属性遍历。

    let person1 = { "name": "老王", "age": 40, "height": 1.8 }; // 创建一个名为 `person1` 的普通对象,表示一个人的信息。该对象有三个属性,分别是 `name`、`age` 和 `height`。

    let proxy = new Proxy(person1, {                              // 创建一个名为 `proxy` 的代理对象,并传入 `person1` 对象和代理器函数。

        ownKeys: function (target) {                              // 这里的代理器函数主要用于拦截内部方法 `Object.keys()` 调用。

            return ["name", 'age'];                                // 当使用 `Object.keys()` 方法时,将返回一个代表新的可枚举属性名称的数组(只包括 name 和 age)。

        }

    });

    console.log(Object.keys(person1));  // 输出 ['name', 'age', 'height'],因为这是属性全部可枚举的 'person1' 对象本身的方法 Object.keys(),返回该对象所有可枚举自有属性的名称,会列出所有对象属性(包括 height)

    console.log(Object.keys(proxy));    // 输出 ['name', 'age'],因为这里使用的时代理对象 `proxy`,经过了代理函数过滤掉了除 name 和 age 以外其他自有属性


 

    // (4)、has方法

    // has()拦截操作:拦截key in object的操作,结果会返回一个布尔值。

    var person2 = {                          // 创建一个名为 `person2` 的普通对象,表示一个人的信息。该对象有两个属性,分别是 `name` 和 `age`。

        "name": "张三",

        "age": 20

    };

    var proxy2 = new Proxy(person2, {        // 创建一个名为 `proxy2` 的代理对象,并传入 `person2` 对象和拦截器函数。

        has: function (target, prop) {       // 这里的拦截器函数主要用于拦截内部方法 `in` 调用(例如 'prop in obj')。

            if (target[prop] === undefined) { // 判断被查询的 `prop` 属性是否在 target 对象中存在。

                return false;                // 如果该属性不存在,则返回 false。

            }

            else {

                return true                  // 如果该属性存在,则返回 true。

            }

        }

    });

    console.log("name" in proxy2);           // 输出 true,意味着 'proxy2' 代理对象中的名为 'name' 的属性已经存在。

    console.log("ahight" in proxy2);         // 输出 false,意味着在 'proxy2' 代理对象中没有名为 'ahight' 的属性。

    // (5)、apply方法

    // 除了对象类型的变量可以被代理,函数也可以被代理。如果被代理的变量是一个函数,那么还会支持一个拦截程序:apply调用。

    let fn = function () {                   // 定义一个函数 `fn`。

        console.log("wangzhihe");             // 函数主要功能是输出字符串 `"wangzhihe"`。

    };

    let proxy3 = new Proxy(fn, {             // 创建一个名为 `proxy3` 的代理对象,并传入 `fn` 函数和代理器函数。

        apply: function () {                 // 这里的代理器函数主要用于拦截使用了函数调用符号 ( ) 的调用。

            console.log("gebilaowang");       // 当执行函数时,将输出 "gebilaowang" 而不是原本的函数内容。

        }

    });

    proxy3();                                // 调用代理对象作为函数执行,将输出 "gebilaowang"。


 

    // (6)、proxy.revocable方法

    // 如果创建了代理之后又想取消代理的话,我们可以用Proxy.revocable()(可废止的,可撤回的;)函数来实现,它会返回一个对象,对象中含有一个proxy属性,它就是Proxy的代理实例对象;还有一个revoke属性,它是一个方法,用于取消代理。

    let person3 = { "name": "zhangsan" };   // 创建一个名为 `person3` 的普通对象,并拥有一个名为 `name` 的属性(值为 `"zhangsan"`)。

    let handle = {                           // 创建一个代理器对象 `handle`,用于重写代理对象的默认行为。

        get: function (target, prop) {       // 这里的拦截器函数主要用于捕获对代理对象的读取操作,例如 `proxy.name`。

            return "lisi";                   // 当读取代理对象中的属性时,始终返回字符串 `"lisi"` 而不是原本的属性值。

        }

    };

    let object = Proxy.revocable(person3, handle); // 使用 `Proxy.revocable()` 创建一个可撤销的代理对象 `object`,并传入普通对象和代理器对象。

    console.log(object.proxy.name);         // 输出 "lisi",因为已经被代理器所拦截,始终返回 `"lisi"` 而不是原本的属性值。

    object.revoke();                        // 撤销代理对象

    //报错    console.log(object.proxy.name);         // 抛出 TypeError 异常,因为代理对象已经被撤销,无法再访问其成员。

    // 2.for…of使用

    // (1)、for…of使用

    // for...of 一种用于遍历数据结构的方法。它可遍历的对象包括数组,字符串,set和map结构等具有iterator 接口的数据结构。

    // 方式一利用for循环来遍历数组的缺点就是:代码不够简洁。

    // var arr = [1, 2, 3, 4, 5,]

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

    // 方式二:利用forEach循环代码量少了很多,写法更加简洁,缺点就是:无法中断停止整个循环。

    // var arr = [1, 2, 3, 4, 5,]

    // arr.forEach(function (value, index) { })

    // 方式三: for...in循环更常用于对象的循环,如果用于数组的循环,那么就要注意了,上述代码中每次循环中得到的i是字符串类型,而不是预料中的数字类型,要想对它进行运算,那得先要进行类型转换,造成不方便。

    // var arr = [1, 2, 3, 4, 5]

    // for (let i in arr) { }

    // for...of的优势:

    // 写法比for循环简洁很多;

    // 可以用break来终止整个循环,或者continute来跳出当前循环,继续后面的循环;

    // 结合keys()获取到循环的索引,并且是数字类型,而不是字符串类型。

    // var arr = [1, 2, 3, 4, 5]

    // for (let value of arr) {

    //     console.log(value);

    // }

    // (2)、循环可以终止

    // var arr = [1, 2, 3, 4, 5]

    // for (let value of arr) {

    //     if (value == 3) {

    //         break

    //         //continue

    //     }

    //     console.log(value);

    // }

    // (3)、得到数字类型索引

    // var arr = [1, 2, 3, 4, 5]

    // for (let index of arr.keys()) {

    //     console.log(index);

    // }

    // (4)、遍历字符串    for...of 支持字符串的遍历。

    // let word = "王致和"

    // for (let w of words) {

    //     console.log(w);

    // }

    // (5)、遍历DOM list    for...of支持类数组的遍历,例如DOM List。

    let liList = document.querySelectorAll('li')

    for (let li of liList) {

        console.log(li);

    }

    // 3.Iterator遍历器

    // (1)、for…of为什么不遍历object对象

    console.log(Array.prototype[Symbol.iterator]);//ƒ values() { [native code] }

    console.log(String.prototype[Symbol.iterator]);//ƒ [Symbol.iterator]() { [native code] }

    console.log(Set.prototype[Symbol.iterator]);//ƒ values() { [native code] }

    console.log(Map.prototype[Symbol.iterator]);//ƒ entries() { [native code] }

    console.log(Object.prototype[Symbol.iterator]);//ndefined

    // (2)、Iterator原理

    // 当可遍历对象被for...of遍历的时候,[Symbol.iterator]()就会被调用,返回一个iterator对象。其中还有一个很重要的方法:next();

    let arr = ["a", "b", "c"]

    let iter = arr[Symbol.iterator]()

    console.log(iter.next());//{value: 'a', done: false}

    console.log(iter.next());//{value: 'b', done: false}

    console.log(iter.next());//{value: 'c', done: false}

    console.log(iter.next());//{value: undefined, done: true}

    // (3)、自定义Iterator遍历器

    let obj = {                           // 定义一个名为 `obj` 的普通对象,并拥有两个属性,分别是 `name` 和 `age`。

        name: "张三",

        age: 19

    };

    // 为 `obj` 对象定义迭代器

    obj[Symbol.iterator] = function () {

        let index = 0;                   // 初始化迭代器索引为 0

        let keys = Object.keys(this);    // 获取普通对象中所有可枚举的属性名称的数组

        // 返回一个包含 next() 方法的迭代器对象

        return {

            next: () => {               // next() 方法返回一个具有 value 和 done 两个属性的对象,表明目前迭代到的值和是否已经迭代完成

                let value = {

                    key: keys[index],   // 当前迭代器索引代表的属性名称

                    value: this[keys[index]] // 当前属性所对应的值

                }

                let done = index === keys.length; // 标志是否已经迭代完了所有属性(即当前索引是否超过了属性数量)

                index++;                // 更新迭代器索引

                return {

                    value,

                    done

                }

            }

        }

    }

    // 使用 for...of 循环遍历 `obj` 对象的每一个成员,并输出其 key 和 value

    for (let item of obj) {

        console.log(item);

    }


 

    // 4.Generator函数

    // (1)、声明Generator函数

    // Generator函数被调用后得到的生成器shi一个遍历器iterator,用于遍历函数内部的状态。

    // Generator函数,又称生成器函数,是ES6的一个重要的新特性

    function* Hello(name) {

        yield `hello ${name}`

        yield `how are you`

        yield `bye`

    }

    // (2)、调用Generator函数

    let ite = Hello('王老吉')

    console.log(ite.next());//{value: 'hello 王老吉', done: false}

    console.log(ite.next());//{value: 'how are you', done: false}

    console.log(ite.next());//{value: 'bye', done: false}

    console.log(ite.next());//{value: undefined, done: true}

    // (3)、Generator函数的行为

    // 通过上面的案例,我们知道了:Generator函数被调用后并不会一直执行到最后,它是先回返回一个生成器对象,然后hold住不动,等到生成器对象的next()方法被调用后,函数才会继续执行,直到遇到关键字yield后,又会停止执行,并返回一个Object对象,然后继续等待,直到next()再一次被调用的时候,才会继续接着往下执行,直到done的值为true

    // (4)、yield语句的使用

    // 而yield在这里起到了十分重要的作用,就相当于暂停执行并且返回信息。有点像传统函数的return的作用,但不同的是普通函数只能return一次,但是Generator函数可以有很多个yield。而return代表的是终止执行,yield代表的是暂停执行,后续通过调用生成器的next()方法,可以恢复执行。

    // (5)、next方法接收参数

    function* Helloo() {

        let res = yield `hello`;

        yield res;

    }

    let iterator = Helloo();

    console.log(iterator.next());

    console.log(iterator.next("沙琪玛"));

    // (6)、关键字‘yield *’

    // 在一个Generator函数里面,如果我们想调用另一个Generator函数,就需要用到的关键字是:yield *。

    function* gen1() {

        yield "gen1 start";

        yield "gen1 end";

    }

    //声明Generator函数:gen2

    function* gen2() {

        yield "gen2 start";

        yield "gen2 end";

    }

    //声明Generator函数:start

    function* start() {

        yield "start";

        yield* gen1();

        yield* gen2();

        yield "end";

    }

    //调用start函数

    var itee = start();

    //创建一个生成器

    console.log(itee.next());//{value: "start", done: false}

    console.log(itee.next());//{value: "gen1 start", done: false}

    console.log(itee.next());//{value: "gen1 end", done: false}

    console.log(itee.next()); //{value: "gen2 start", done: false}

    console.log(itee.next());//{value: "gen2 end", done: false}

    console.log(itee.next());//{value: "end", done: false}

    // (7)、Generator函数的用途

    // 以上就是对Generator函数的讲解介绍,它是ES6的一个很重要的新特性。它可以控制函数的内部状态,依次遍历每个状态;可以根据需要,轻松地让函数暂停执行或者继续执行。

    // 根据这个特点,我们可以利用Generator函数来实现异步操作的效果。

    // 原理是:利用Generator函数暂停执行的作用,可以将异步操作的语句写到yield后面,通过执行next方法进行回调。

    // 5.类基本用法

    // (1)、类的属性和方法

    // 声明一个类的写法

    // class Animal {

    //     constructor(color) {

    //         this.color = color;

    //     }

    // }

    // (2)、类的实例化对象

    // class Animal {

    //     constructor(name) {

    //         this.name = name;

    //     }

    //     getName() {

    //         return this.name

    //     }

    // }

    // (3)、类的自定义方法

    class Animal { // 定义 Animal 类

        constructor(name) { // 定义构造函数,接收一个参数 name 作为动物的名称

            this.name = name; // 将传入的 name 参数保存到该实例的属性中

        }

        getName() { // 定义方法,用于获取实例对象的名称

            return 'This is a' + this.name // 返回一个字符串文本,表示该实例的名称(在字符串前添加 "This is a")。

        }

    }

    let dog = new Animal('dog'); // 创建一个名为 dog 的 Animal 实例对象,并将其名称设定为 'dog'

    console.log(dog.name); // 打印 dog 实例对象的 name 属性值 'dog'

    console.log(dog.getName()); // 调用实例对象 dog 上定义的 getName() 方法,返回字符串 This is a dog,并将其打印到控制台上。


 

    // (4)、类的静态方法

    // 如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”

    class Father { // 定义 Father 类

        static testMethod() { // 在 Father 类上定义一个静态方法 testMethod()

            return 'hello'; // 该静态方法返回字符串 'hello'

        }

    }

    Father.testMethod(); // 调用Father类的静态方法testMethod()

    // 代码功能:定义 Father 类,并在 Father 类上定义静态方法 testMethod(),最后调用该静态方法返回字符串'hello'

    // (5)、类的继承  ES6使用extends关键字来实现子类继承父类

    // class Animali { }

    // class dogg extends Animali {

    //     constructor(name, color) {

    //         super(name)

    //         this.color = color

    //     }

    // }

    class Animali { // 定义 Animali 类

        constructor(name) { // 构造函数,接收一个参数 name 用于设定 Animali 实例的名称

            this.name = name // 将传入的 name 参数保存到该实例对象的属性中

        }

        say() { //定义方法 say() ,返回该实例对象的类型(字符串文本 This is a animal)

            return `This is a animal`

        }

    }

    class Dog extends Animali { //定义 Dog 狗类,并继承 Animali 父类

        constructor(name, color) { //构造函数,接收两个参数——name 用于设定 Dog 实例的名称,color 用于设定狗的颜色。

            super(name) //在子类的构造函数中调用父类的构造函数并传入相应的参数。

            this.color = color //将传入的 color 参数保存到该实例对象的属性中

        }

        getAttritube() { //定义方法 getAttritube() ,用于获取该实例对象的属性值和父类的类型信息

            return `${super.say()},

            name:${this.name},

            color:${this.color}` // 返回一个字符串文本,包含父类中该实例对象的类型信息(通过 super 调用父类方法 say()),以及该实例对象的名称(属性 name)和颜色(属性 color)

        }

    }

    let d = new Dog("dog", "black") // 创建一个名为“dog”、颜色为“black”的 Dog 实例对象 d

    console.log(d.getAttritube()); // 调用实例对象的 getAttritube() 方法,返回一个字符串文本,包含父类中该实例对象的类型信息(通过 super 调用父类方法 say()),以及该实例对象的名称(属性 name)和颜色(属性 color),并将其打印到控制台上。


 

</script>

</html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值