How to “override” a defined (get-)property on a prototype?

前端 未结 3 556
北荒
北荒 2020-12-31 01:58

I have some code which defines a getter (but no setter, if such is relevant) on a prototype. The value returned is correct in 99.99% of the cases; however, the goal is to se

相关标签:
3条回答
  • 2020-12-31 02:22

    In TypeScript, I use Object.getOwnPropertyDescriptor and Object.defineProperty:

    class TestClass
    {
    
        constructor()
        {
            this.autoBind(this);
            this.autoTrace(this);
        }
    
    
    
        private autoBind(self: any): any
        {
            for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
            {
    
                if (key !== 'constructor')
                {
                    // console.log(key);
    
                    let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
    
                    if (desc != null)
                    {
                        let g = desc.get != null;
                        let s = desc.set != null;
    
                        if (g || s)
                        {
                            if (g)
                                desc.get = desc.get.bind(self);
    
                            if (s)
                                desc.set = desc.set.bind(self);
    
                            Object.defineProperty(self.constructor.prototype, key, desc);
                            continue; // if it's a property, it can't be a function 
                        } // End if (g || s) 
    
                    } // End if (desc != null) 
    
                    if (typeof (self[key]) === 'function')
                    {
                        let val = self[key];
                        self[key] = val.bind(self);
                    }
    
                } // End if (key !== 'constructor' && typeof val === 'function') 
    
            } // Next key 
    
            return self;
        } // End Function autoBind
    
    
    
        private autoTrace(self: any): any
        {
    
    
            function getLoggableFunction_old(func, type, name)
            {
                return function (...args)
                {
                    let logText = name + '(';
    
                    for (var i = 0; i < args.length; i++)
                    {
                        if (i > 0)
                        {
                            logText += ', ';
                        }
                        logText += args[i];
                    }
                    logText += ');';
    
                    console.log(type + " " + logText);
                    return func.apply(self, args);
                };
            }
    
    
            function getLoggableFunction(func, type, name)
            {
                return function (...args)
                {
                    let logText = name + '(';
    
                    for (var i = 0; i < args.length; i++)
                    {
                        if (i > 0)
                        {
                            logText += ', ';
                        }
                        logText += args[i];
                    }
                    logText += ')';
    
                    console.log("Pre " + type + " " + logText + "; ");
                    let res = func.apply(self, args);
                    console.log("Post " + type + " " + logText + ":", res);
                    return res;
                };
            }
    
    
            for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
            {
    
                if (key !== 'constructor')
                {
                    // console.log(key);
                    let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
    
                    if (desc != null)
                    {
                        let g = desc.get != null;
                        let s = desc.set != null;
    
                        if (g || s)
                        {
                            if (g)
                                desc.get = getLoggableFunction(desc.get.bind(self), "Property", "get_" + key)
    
                            if (s)
                                desc.set = getLoggableFunction(desc.set.bind(self), "Property", "set_" + key)
    
                            Object.defineProperty(self.constructor.prototype, key, desc);
                            continue; // if it's a property, it can't be a function 
                        } // End if (g || s) 
    
                    } // End if (desc != null) 
    
                    // if it's not a property, it can only be a function or not a function 
                    if (typeof (self[key]) === 'function')
                    {
                        let val = self[key];
                        self[key] = getLoggableFunction(val.bind(self), "Function", key);
                    } // End if (typeof (self[key]) === 'function') 
    
                } // End if (key !== 'constructor' && typeof val === 'function') 
    
            } // Next key 
    
            return self;
        } // End Function autoTrace
    
    
    
    get bar(): boolean
    {
        return this._bar;
    }
    set bar(value: boolean)
    {
        this._bar = value;
    }
    
    
    public hello()
    {
        console.log("hello", "this", this);
    }
    
    
    public world(x, y)
    {
        console.log("world", "this", this);
    
    }
    
    
    
    }
    
    0 讨论(0)
  • 2020-12-31 02:28

    As T.J. Crowder said, using defineProperty again does the trick. You may consider the following variation, where the setter itself overrides the property:

    Foo = function () {}
    Foo.prototype = {
      // computes, but only knows odd die sides
      get bar() { 
        console.log("getter invoked")
        return (Math.random() * 6) | 1 
      },
      
      // fix it
      set bar(value) {
        console.log("setter invoked")
        Object.defineProperty(
          this, 'bar', 
          {writable: true, enumerable: true, configurable: true}
        )
        this.bar = value 
      }
    }
    
    var x = new Foo
    console.log(x.bar)       // => eg. 5
    x.bar = 4   // by fair dice roll
    console.log(x.bar)       // => 4
    x.bar = 2   // no setter, no getter
    console.log(x.bar)

    I hope you'll pardon me to rewrite in a slightly different syntax. It does not change anything to the trick. I was actually just looking for a way to override an inherited getter when I came to this post.

    0 讨论(0)
  • 2020-12-31 02:37

    By using Object.defineProperty on x:

    var foo = {}
    Object.defineProperty(foo, "bar", {
        // only returns odd die sides
        get: function () { return (Math.random() * 6) | 1; }
    });
    
    var x = Object.create(foo);
    display(x.bar);      // E.g. 5
    
    (function() {
      var bar;
      var proto = Object.getPrototypeOf(x); // Or just use foo
    
      Object.defineProperty(x, "bar", {
        get: function () { return typeof bar !== "undefined" ? bar : proto.bar; },
        set: function(value) { bar = value; }
      });
    })();
    
    display(x.bar);  // Still odd
    x.bar = 4;       // By fair dice roll
    display(x.bar);  // Shows 4
    
    function display(msg) {
      document.body.insertAdjacentHTML("beforeend", "<p>" + msg + "</p>");
    }

    I am looking for if there is a way to stop the behavior of a property in the [prototype] and turn "bar" back into a normal/ad-hoc property.

    Okay, that's slightly different, but still uses Object.defineProperty:

    var foo = {}
    Object.defineProperty(foo, "bar", {
        // only returns odd die sides
        get: function () { return (Math.random() * 6) | 1; }
    });
    
    var x = Object.create(foo);
    display(x.bar);      // E.g. 5
    
    Object.defineProperty(x, "bar", {
      value: undefined,
      writable: true,
      enumerable: true // Or leave off if you want it non-enumerable
    });
    
    display(x.bar);  // undefined
    x.bar = 4;       // By fair dice roll
    display(x.bar);  // Shows 4
    
    function display(msg) {
      document.body.insertAdjacentHTML("beforeend", "<p>" + msg + "</p>");
    }

    0 讨论(0)
提交回复
热议问题