NodeJS系列(5)- ECMAScript 6 (ES6) 语法(三)

发布时间 2023-06-28 23:10:29作者: 垄山小站


在 “NodeJS系列(3)- ECMAScript 6 (ES6) 语法(一)” 和 “NodeJS系列(4)- ECMAScript 6 (ES6) 语法(二)” 里,我们介绍并演示 let、const、Symbol、函数扩展、类 等 ES6 语法和概念。

本文在 “NodeJS系列(2)- NPM 项目 Import/Export ES6 模块” 的 npmdemo 项目的基础上,继续介绍并演示 Reflect、Proxy 等 ES6 语法和概念。

NodeJS ES6:https://nodejs.org/en/docs/es6
ECMA:https://www.ecma-international.org/publications-and-standards/standards/ecma-262/

 

1. Reflect

    ES6 中将 Object 的一些内部方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。

    Reflect 对象对某些方法的返回结果进行了修改,使其更合理。Reflect 对象使用函数的方式实现了 Object 的命令式操作。

    Reflect 对象是一个全局的普通对象,它的原型是 Object。查看 Reflect 的原型,代码如下:

        console.log(Reflect.__proto__ === Object.prototype);    // 输出 true

    Reflect 对象的静态方法列表:

        名称                                             描述
        Reflect.get(target,name,receiver)               读取对象的属性。参数 target 是目标对象;name 是属性名;receiver 是可选项,可以理解为上下文 this 对象。
        Reflect.set(target,name,value,receiver)         设置对象的属性。参数 value 是要设置的属性值。
        Reflect.apply(target,thisArg,args)              通过指定的参数列表对目标函数的调用。参数 target 是目标函数;thisArg 是目标函数调用时绑定的 this 对象;args 是函数参数列表。
        Reflect.construct(target,args[, newTarget])     类似 new 的功能,创建一个实列方法。参数 target 是目标函数;args 是调用构造函数传递的参数数组或伪数组;newTarget 是可选项,可以把实例方法的构造函数指向 newTarget。
        Reflect.defineProperty(target,name,desc)        定义新的属性或修改原有的属性。参数 target 是目标对象;name 是属性名;desc 是属性的描述。      
        Reflect.deleteProperty(target,name)             删除对象的属性。参数 target 是目标对象;name 是属性名。
        Reflect.has(target,name)                        查看对象的属性是否存在。
        Reflect.ownKeys(target)                         获取目标对象的属性键组成的数组。
        Reflect.preventExtensions(target)               阻止对象扩展新属性和方法。         
        Reflect.isExtensible(target)                    检查对象是否可以扩展新属性和方法。
        Reflect.getOwnPropertyDescriptor(target, name)  获取对象的属性描述。
        Reflect.getPrototypeOf(target)                  获取对象的原型,即 prototype 属性。
        Reflect.setPrototypeOf(target, prototype)       设置对象的原型。

    示例,创建 D:\workshop\nodejs\npmdemo\es6_08.js 文件,内容如下

        let obj1 = {
            name: 'Test',
            age: 18,
            get info() {
                return this.name + ', ' + this.age
            },
            set info(value) {
                return this.age = value
            }
        }

        let obj2 = {
            name: 'Demo',
            age: 20
        }

        console.log(Reflect.__proto__ === Object.prototype);

        console.log(Reflect.get(obj1, 'name'))         // 输出:Test
        console.log(Reflect.get(obj1, 'info', obj2))   // 输出:Demo, 20  

        Reflect.set(obj1, 'age', '25')
        console.log(obj1.age)    // 输出:25

        Reflect.set(obj1, 'age')    // value 为空,将 age 属性清除
        console.log(obj1.age)  // 输出:undefined

        Reflect.set(obj1, 'info', 5, obj2)
        console.log(obj2.age)    // 输出:5

        console.log('--------------------------------------------------------------')

        console.log(Reflect.has(obj1, 'name')) // 输出:true

        Reflect.deleteProperty(obj1, 'name')
        console.log(obj1.name)  // 输出:undefined

        Reflect.defineProperty(obj2, 'now', {
            value: () => Date.now()
        })

        console.log(Reflect.getOwnPropertyDescriptor(obj2, 'now'))   // 输出: { value: [Function: value],writable: false,enumerable: false,configurable: false}
        console.log(obj2.now())   // 输出:1687943288174 (当前时间戳)

        console.log(Reflect.isExtensible(obj2)) // 输出:true
        Reflect.preventExtensions(obj2)
        console.log(Reflect.isExtensible(obj2)) // 输出:false

        console.log(Reflect.ownKeys(obj2)) // 输出:[ 'name', 'age', 'now' ]

        console.log('--------------------------------------------------------------')
        
        //
        function func(name){
            this.name = name;
        }
        func.prototype.age = 12

        let obj3 = Reflect.construct(func, ['ES 6'])
        console.log(obj3)   // 输出:func { name: 'ES 6' }

        let obj4 = new func('NodeJS')
        console.log(obj4)   //  输出:func { name: 'NodeJS' }

        console.log(Reflect.getPrototypeOf(obj4))   // 输出:{ age: 12 }
        Reflect.setPrototypeOf(obj4, Array.prototype)
        console.log(Reflect.getPrototypeOf(obj4))   // 输出:Object(0) []

        console.log(Reflect.apply(Math.max, Math, [1, 3, 5, 3, 1]))  // 输出: 5


    运行

        D:\workshop\nodejs\npmdemo> node es6_08

            true
            Test
            Demo, 20
            25
            undefined
            5
            --------------------------------------------------------------
            true
            undefined
            {
            value: [Function: value],
            writable: false,
            enumerable: false,
            configurable: false
            }
            1687949712324
            true
            false
            [ 'name', 'age', 'now' ]
            --------------------------------------------------------------
            func { name: 'ES 6' }
            func { name: 'NodeJS' }
            { age: 12 }
            Object(0) []
            5

 


2. Proxy

 

    Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

    一个 Proxy 对象由两个部分组成:target、handler 。在通过 Proxy 构造函数生成实例对象时,需要提供这两个参数。 target 即目标对象, handler 是一个对象,声明了代理 target 的指定行为。

    Proxy 的代理(或拦截)方法列表:

        名称                                             描述       
        get(target, prop, receiver)                     拦截读取属性操作。
        set(target, prop, value, receiver)              拦截新增或修改属性操作。如果目标对象自身的某个属性,不可写且不可配置,那么 set 方法将不起作用。参数 receiver 表示原始操作行为所在对象,一般是 Proxy 实例本身。
        apply(target, thisArg, args)                    拦截方法调用、call 和 reply 操作。参数 thisArg 是目标函数调用时绑定的 this 对象;args 是函数参数列表。
        has(target, prop)                               拦截判断属性是否存在操作,即在判断 target 对象是否存在 prop 属性时,会被这个方法拦截。此方法不判断一个属性是对象自身的属性,还是继承的属性。此方法不拦截 for ... in 循环。
        getOwnPropertyDescriptor(target, prop)          拦截获取属性描述操作。
        construct(target, args[, newTarget])            拦截 new 命令。
        defineProperty(target, prop, desc)              拦截定义新的属性或修改原有的属性操作。若目标对象不可扩展,增加目标对象上不存在的属性会报错;若属性不可写或不可配置,则不能改变这些属性。
        deleteProperty(target, prop)                    拦截删除属性操作。
        ownKeys(target)                                 拦截获取属性 prop 列表操作。
        isExtensible(target)                            拦截判断属性是否可扩展操作。
        preventExtensions(target)                       拦截阻止对象扩展新属性操作。
        getPrototypeOf(target)                          拦截获取原型对象操作。
        setPrototypeOf(target,prototype)               拦截设置原型对象操作。

    1) get/set/apply 方法

        示例,创建 D:\workshop\nodejs\npmdemo\es6_09.js 文件,内容如下

            let obj1 = {
                name: 'Default'
            }
            let handler1 = {
                get: function(target, prop) {
                    console.log("get('" + prop + "')")
                    return target[prop]      // 不是 target.prop
                },
                set: function(target, prop, value) {
                    console.log("set('" + prop + "', " + value + ")")
                    target[prop] = value
                    return true         // 严格模式下,set 代理如果没有返回 true,就会报错。
                },
                apply: function(target, thisArg, args) {
                    console.log("apply(" + args + ")")
                    return Reflect.apply(...arguments)
                }
            }

            let proxy1 = new Proxy(obj1, handler1)  
            console.log("proxy1.name: " + proxy1.name)   // 执行 handler1.get
            proxy1.name = 'Test1'                        // 执行 handler1.set
            console.log("proxy1.name: " + proxy1.name)


            // target 可以为空对象
            let obj2 = {}

            let proxy2 = new Proxy(obj2, handler1)
            console.log("proxy2.name: " + proxy2.name)     // 执行 handler1.get
            proxy2.name = 'Test2'                          // 执行 handler1.set
            console.log("proxy2.name: " + proxy2.name)


            // handler 对象也可以为空,相当于不设置拦截操作,直接访问目标对象
            let obj3 = {}
            let handler3 = {}

            let proxy3 = new Proxy(obj3, handler3)
            console.log("proxy3.name: " + proxy3.name)
            proxy3.name = 'Test3'
            console.log("proxy3.name: " + proxy3.name)


            //
            function sum(a, b) {
                return a+b
            }

            let proxy4 = new Proxy(sum, handler1)
            console.log("proxy4(2, 1): " + proxy4(2, 1))    // apply


        运行

            D:\workshop\nodejs\npmdemo> node es6_09

                get('name')
                proxy1.name: Default
                set('name', Test1)
                get('name')
                proxy1.name: Test1
                get('name')
                proxy2.name: undefined
                set('name', Test2)
                get('name')
                proxy2.name: Test2
                proxy3.name: undefined
                proxy3.name: Test3       
                apply(2,1)
                proxy4(2, 1): 3


    2) has/getOwnPropertyDescriptor/construct/defineProperty/deleteProperty 方法

        示例,创建 D:\workshop\nodejs\npmdemo\es6_10.js 文件,内容如下

            let obj1 = {
                name: 'Test'
            }
            let handler1 = {
                has: function(target, prop) {
                    console.log("has('" + prop + "')")
                    return Reflect.has(target, prop)
                },
                defineProperty: function(target, prop, desc) {
                    console.log("defineProperty('" + prop + "')")
                    return Reflect.defineProperty(target, prop, desc)
                },
                getOwnPropertyDescriptor: function(target, prop) {
                    console.log("getOwnPropertyDescriptor(" + prop + ")")
                    return Reflect.getOwnPropertyDescriptor(target, prop)
                },
                deleteProperty: function(target, prop) {
                    console.log("deleteProperty('" + prop + "')")
                    return Reflect.deleteProperty(target, prop)
                },
                construct: function(target, args, newTarget) {
                    console.log("construct('" + args + "')")
                    return Reflect.construct(target, args, newTarget)
                }
            }

            let proxy1 = new Proxy(obj1, handler1)
            console.log('name' in proxy1)       // has
            console.log('tt' in proxy1)         // has

            proxy1.age = 12     // defineProperty
            console.log(Object.getOwnPropertyDescriptor(proxy1, 'age'))

            delete proxy1.age   // deleteProperty

            class C1 {
                constructor(name) {
                    this.name = name
                }
            }
            let proxy2 = new Proxy(C1, handler1)
            let proxyObj = new proxy2('Test3')  // construct
            console.log(proxyObj)


        运行

            D:\workshop\nodejs\npmdemo> node es6_10

                has('name')
                true
                has('tt')
                false
                getOwnPropertyDescriptor(age)
                { value: 16, writable: true, enumerable: true, configurable: true }
                C1 { name: 'Test3' }        


    3) ownKeys/isExtensible/preventExtensions/getPrototypeOf/setPrototypeOf 方法

        示例,创建 D:\workshop\nodejs\npmdemo\es6_11.js 文件,内容如下

            let obj1 = {
                color: 'red'
            }
            let handler2 = {
                ownKeys: function(target) {
                    console.log("ownKeys()")
                    return Reflect.ownKeys(target)
                },
                isExtensible: function(target) {
                    console.log("isExtensible()")
                    return Reflect.isExtensible(target)
                },
                preventExtensions: function(target) {
                    console.log("preventExtensions()")
                    return Reflect.preventExtensions(target)
                },
                getPrototypeOf: function(target) {
                    console.log("getPrototypeOf()")
                    return Reflect.getPrototypeOf(target)
                },
                setPrototypeOf: function(target, prototype) {
                    console.log("setPrototypeOf('" + prototype + "')")
                    return Reflect.setPrototypeOf(target, prototype)
                }
            }

            let proxy1 = new Proxy(obj1, handler1)
            console.log(Object.keys(proxy1))       // ownKeys

            console.log(Object.isExtensible(proxy1))    // isExtensible
            console.log(Object.preventExtensions(proxy1))    // preventExtensions
            console.log(Object.isExtensible(proxy1))    // isExtensible

            class C1 {

            }
            let proxy2 = new Proxy(C1, handler1)
            Object.setPrototypeOf(proxy2, { name: 'object'})    // setPrototypeOf
            console.log(Object.getPrototypeOf(proxy2))    // getPrototypeOf


        运行

            D:\workshop\nodejs\npmdemo> node es6_11

                ownKeys()
                [ 'color' ]
                isExtensible()
                true
                preventExtensions()
                { color: 'red' }
                isExtensible()
                false
                setPrototypeOf('[object Object]')
                getPrototypeOf()
                { name: 'object' }


    4) 可取消的 Proxy 实例

        使用 Proxy.revocable() 方法创建可取消的 Proxy 实例, 格式如下:

            let {proxy, revoke} = Proxy.revocable({}, {});
            proxy.name = "Test";
            revoke();   // 取消 Proxy 实例
            proxy.name  // 报错: “TypeError: Cannot perform 'get' on a proxy that has been revoked”