问题
I thougt it would be fun to rewrite my zero padding extension to the Number.prototype into a genuine getter/setter pattern. Not very difficult. I ended up with this code (addSetter
and addGetter
are just wrapper functions using prototype.__defineGetter__ / __defineSetter__
.
Number.addSetter('leftPad0',
function(v){
var len = (String(v).length - String(this).length)+1;
this._leftPad0 = new Array(len).join('0')+this;
}
);
Number.addGetter('leftPad0',
function(){
return this._leftPad0;
}
);
Works fine for real Number Objects:
var a = new Number(977);
a.leftPad0 = 10000;
alert('a => '+a.leftPad0); // a => 00977
But not for Number literals:
var b = 977;
b.leftPad0 = 10000;
alert('b => '+b.leftPad0); // b => undefined
So, doesn't b reach the setter? Or if it reaches the setter, isn't it a Number?
I logged this in the console from within the setter function:
this.constructor === Number // true
this instanceof Number //true
Or is the getter not reached, or when it's reached would the literal not be an instance of Number? I logged the same within the getter. All fine and true too.
So, what may be the reason that we are not able to use this pattern using a Number literal? Or have I missed something?
Note: this doesn't occur if I use this prototype extension ('monkey patch'):
Number.prototype.leftPad = function(base){
var len = (String(base).length - String(this).length)+1;
return new Array(len).join('0')+this;
}
alert( (977).leftPad(10000) ); // 00977
[edit] I still wonder if we have to call this a bug, or if it's following the/a standard. Anyway, I deviced my own object for this now:
function NumPL(val,pval,chr){
if (!(this instanceof NumPL)){
return new NumPL(val,pval,chr);
}
this._value = new Number(val||0);
this._padValue = pval || 10;
this.chr = chr || '0';
}
NumPL.prototype = {
get value(){
return this._value;
},
set padValue(v) {
this._padValue = v%10 === 0 ? v : 10;
},
set value(v) {
this._value = v;
},
get padLeft(){
var len = (String(this._padValue).length -
String(this._value).length)+1;
return new Array(len).join(this.chr)+this._value;
}
}
// Usage
var a = NumPL(977,10000);
alert(a.padLeft); //=> 00977
// or a real world example
var dat = new Date,
datshort = [dat.getFullYear(),
NumPL(dat.getMonth()+1).padLeft,
NumPL(dat.getDate()).padLeft]
.join('/');
alert(datshort); //=> 2011/05/19
回答1:
A literal number is not a Number
:
typeof 3
// "number"
typeof new Number(3)
// "object"
So there is no prototype to modify for numeric literals.
However, when you try to set a property on a numeric literal, it looks like the literal is wrapped in a Number object first. Then, the setter is called on that object and this
will be an object inside the setter function. And then, the literal is unwrapped when the setter returns and this unwrapping leaves the property (which was set on the object wrapper) behind.
Number.prototype.__defineSetter__('leftPad0',
function(v) {
alert('In setter: ' + v + ' ' + (this instanceof Number));
var len = (String(v).length - String(this).length) + 1;
this._leftPad0 = new Array(len).join('0') + this;
}
);
var a = new Number(977);
a.leftPad0 = 10000;
alert('a => ' + a._leftPad0);
var b = 977;
b.leftPad0 = 10000;
alert('b => ' + b._leftPad0);
http://jsfiddle.net/ambiguous/H77qk/2/
I get four alerts out of the above in several browsers.
Sorry but I don't have an authoritative reference for this behavior, just empirical evidence.
回答2:
var b = 9;
means that b
is a reference to a primitive type, not an object of type Number
.
var b = new Number(9)
creates an object of type Number
, which will contain the function added to the Number
prototype, not a primitive number type.
来源:https://stackoverflow.com/questions/6050289/setter-getter-not-working-for-number-literal