Why doesn't the shim for watch() and unwatch()

2019-04-11 01:52发布

问题:

I'm doing some server-side javascript bits and pieces that essentially need both access to websites' full DOMs and the ability to open sites cross-domain, and at least so far, PhantomJS seems the best tool for the job.

However, it lacks an Object.watch() method, which I'd rather like to have. As such, I'm trying to use the shim posted as an answer elsewhere on Stackoverflow (available here: https://gist.github.com/384583) to give me access to just such methods.

It isn't working.

It's also well beyond my level of JavaScript understanding - so I'm wondering if anyone could help me understand both exactly what it's doing, and why it might not be working?

Thanks for the help,

Toby

(Code quoted below:

// Cross-browser object.watch and object.unwatch

// object.watch
if (!Object.prototype.watch) {
    Object.prototype.watch = function (prop, handler) {
        var oldval = this[prop], newval = oldval,
        getter = function () {
            return newval;
        },
        setter = function (val) {
            oldval = newval;
            return newval = handler.call(this, prop, oldval, val);
        };
        if (delete this[prop]) { // can't watch constants
            if (Object.defineProperty) { // ECMAScript 5
                Object.defineProperty(this, prop, {
                    get: getter,
                    set: setter,
                    enumerable: true,
                    configurable: true
                });
            } else if (Object.prototype.__defineGetter__ &&     Object.prototype.__defineSetter__) { // legacy
                Object.prototype.__defineGetter__.call(this, prop, getter);
                Object.prototype.__defineSetter__.call(this, prop, setter);
            }
        }
    };
}

// object.unwatch
if (!Object.prototype.unwatch) {
    Object.prototype.unwatch = function (prop) {
        var val = this[prop];
        delete this[prop]; // remove accessors
        this[prop] = val;
    };
}

And my test code:

tO = {
    "v":0,
    "vplus":function(){this.v ++}
};
tO.watch("v", function(prop, oldval, newval) {
    console.log("The property "+prop+" is now "+tO.v+". Newval is "+newval+" and oldval     "+oldval+".");
    if (newval == 5){phantom.exit()};
});
tO.v = 1;
var i = 0
for(i=0; i<10; i++) {
    tO.v = i; 
    console.log(tO.v);
    if(tO.v == 9){
        console.log("got to 9");
    };
};

回答1:

To answer my own question: the provided shim seems to put a broken setter function in place. In particular, unless the handler function returns something meaningful (which it may well not), the property gets set to undefined. Replacing

return newval = handler.call(this, prop, oldval, val);

with

var newval = handler.call(this, prop, oldval, val) || val;
return newval;

seemed to do the job.

As a side note, this particular shim is ingenious but includes a couple of limitations:

  • Code relying on a watched value may take a while to work, so it's good to change watched values after critical operations if using them as flags.

  • You can't add multiple handlers to a value.