简体   繁体   中英

Prototype of defineProperty in JavaScript

In JavaScript you can define getters and setters by using Object.defineProperty() . I am wondering if it is possible to extend or use the prototype of this to extend its functionality. Here is an example:

I start off a variable called color:

Object.defineProperty(window, 'color', {
    get: function() {
        return [_color.red,_color.green,_color.blue,_color.alpha];
    },
    set: function(val) {
        _color.red = val[0];
        _color.green = val[1];
        _color.blue = val[2];
        _color.alpha = val[3];
    }
});

This allows for me to both set and get the color by passing rgba arrays to and from the variables. Here is how I would use this:

color = [0,127,255,255];
alert(color);
//alerts [0,127,255,255]

Now I also want to be able to edit these variables through accessing each variable individually.

color.r = 255;
alert(color);
//alerts [255,127,255,255]

I am at an impasse at this moment because I don't know what I can do to create this. I would think either using color.prototype.r or something similar would work, but I can't get it to. Is it possible to do something like this?

You have to create an interim object extended from array to do it specifically.

Here you go:

var __color = {red:0,green:0,blue:0,alpha:1};
function createGetMethod(prop){
    return function(){return __color[prop]};
}
function createSetMethod(prop){
    return function(val){__color[prop]=val};
}
function createDescritorProperty(prop){
    return {
        get: createGetMethod(prop),
        set: createSetMethod(prop)
    }
}

var _color_descriptor = {
    red:createDescritorProperty('red'),
    green:createDescritorProperty('green'),
    blue:createDescritorProperty('blue'),
    alpha:createDescritorProperty('alpha'),
    length:{
        value:4,
        writable:false
    }
}
//setup numbered values
_color_descriptor[0]=createDescritorProperty('red');
_color_descriptor[1]=createDescritorProperty('green');
_color_descriptor[2]=createDescritorProperty('blue');
_color_descriptor[3]=createDescritorProperty('alpha');

var _color = Object.create(Array.prototype, _color_descriptor); //extends array


Object.defineProperty(window, 'color', {
    get: function() {
        return _color;
    },
    set: function(val) {
        _color.red = val[0];
        _color.green = val[1];
        _color.blue = val[2];
        _color.alpha = val[3];
    }
});

window.color = [255,255,255,0];
alert(window.color);
window.color.red=10;
alert(window.color);

Hope it works! :D

The prototype is only for object instances, which you don't have unfortunately. If you had a constructor like this:

function Color(r,g,b,a){
    for (var i=0; i<4; i++)
        this[i] = arguments[i];
}

it would work with

Object.defineProperty(Color.prototype, "r", {
    get: function() { return this[0]; } // setter analogous
};

However, these Color instances are no arrays. You could give them a length property and let them inherit from Array.prototype , but they won't really be arrays. @Trouts solution is a bit like that, and I'd say it is okay because colors really are no arrays (you can't push a fifth value etc).

The alternative were to extend the array which you return from the color getter with those properties. You could do that every time someone accesses the value (like you currently do, you create a new array in the getter) but I'd suggest that you should return the same instance, propagating any changes. Your current property definition is like "setColor" and "getCurrentColor".

So, actually you want two separate things: Color objects that have more than one property per value (ie 0 == r ); and a setter for your global color variable which accepts arrays and sets the single values on the respective object.

// version with private values
function Color(r, g, b, a) {
    // r, g, b and a are private-scoped variables
    var desc = {
        "0": {
             get:function(){return r;},
             set:function(val){ if(+val<256&&val>=0) r=+val;}
        },
        …
    }
    // use those property descriptors multiple times
    desc.r = desc[0];
    …
    Object.defineProperties(this, desc);
}
// or version with public and unlimited read/write access to the properties:
function Color(r,g,b,a){
    for (var i=0; i<4; i++)
        this[i] = arguments[i];
}
Object.defineProperties(Color.prototype, {
    r: { get:function(){return this[0];}, set:function(r){this[0]=r;} },
    …
}

// and for both versions we can add array-like methods on the prototype
var cprot = Color.prototype, aprot = Array.prototype;
Object.defineProperty(cprot, "length", {value:4});
// we only need accessor functions here, nothing which changes the array [length]
cprot.map = aprot.map;
cprot.reduce = aprot.reduce;
cprot.slice = aprot.slice;
cprot.join = aprot.join;

// you might want to add other utilities like
cprot.toString = function() {
    return "rgba("+this.join(",")+")"; // using array method from above
};
cprot.getHex = function() {
    function hex(n) { return (n<16?"0":"") + n.toString(16); }
    return "#"+this.slice(0, 3).map(hex).join("");
};

And then, your color value setter:

function defineColorProperty(obj, prop, color) {
    // again, color is private-scoped
    if (!color || !(color instanceof Color)) color = new Color(0, 0, 0, 0);
    Object.defineProperty(obj, prop, {
        get: function(){ return color; }, // a cool Color instance!
        set: function(val) {
            if (Object(val)!==val) return; // accept objects (including arrays)
            for (var i=0; i<4 && i<val.length; i++)
                color[i] = val[i];
        },
        enumberable: true
    });
    return color;
}

// usage:
> defineColorProperty(window, "color");
Object[Color]: 0, 0, 0, 0
> color = [255, 0, 120, 1];
> color.r = 42;
> color[0]
42
> color = [0, 0];
> ""+color
"rgba(0,0,120,1)"
> var x = color;
> x.b = 255;
> x.getHex()
"#0000FF"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM