Совсем немного отрефакторил код. Теперь итератором служит кастомный метод Object.each, который работает для всех браузеров, у которых есть defineProperty (то есть целевых браузеров).
В итоге у нас есть методы:
Object.defineProperties
Object.getOwnPropertyNames
Object.keys
Object.create
Object.getOwnPropertyDescriptor
Object.each — нестандартный
for..in всё еще не рекомендуется использовать.
( function() {
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
"use strict";
if (this == null) {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
if (len === 0) {
return -1;
}
var n = 0;
if (arguments.length > 1) {
n = Number(arguments[1]);
if (n != n) { // shortcut for verifying if it's NaN
n = 0;
} else if (n != 0 && n != Infinity && n != -Infinity) {
n = (n > 0 || -1) * Math.floor(Math.abs(n));
}
}
if (n >= len) {
return -1;
}
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
for (; k < len; k++) {
if (k in t && t[k] === searchElement) {
return k;
}
}
return -1;
}
}
if( !Object.create && Object.defineProperty ) {
initIeObject();
}
function initIeObject() {
var Constructor = XDomainRequest,
defineProperty = Object.defineProperty,
getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
Constructor.prototype.hasOwnProperty = function( key ) {
return !!~Object.getOwnPropertyNames( this ).indexOf( key );
};
Object.defineProperties = function( object, descriptors ) {
for( var key in descriptors ) {
Object.defineProperty( object, key, descriptors[ key ] );
}
};
JSON._stringify = JSON.stringify;
JSON.stringify = function( object ) {
var props = object._settings_ && object._settings_.enumerable,
o;
if( props ) {
o = {};
for( var i = 0; i < props.length; i++ ) {
o[ props[ i ] ] = object[ props[ i ] ];
}
} else {
o = object;
}
return JSON._stringify( o );
};
Object.defineProperty = function( object, key, descriptor ) {
if( !( object instanceof Constructor ) ) return defineProperty( object, key, descriptor ); // if another dom object
var isConfugurable = descriptor.configurable !== undefined ? descriptor.configurable : false,
_settings_ = object._settings_ = object._settings_ || {
enumerable: [],
notEnumerable: [],
notConfigurable: [],
descriptors: {}
};
delete descriptor.configurable;
_settings_.descriptors[ key ] = descriptor;
if( ~_settings_.notConfigurable.indexOf( key ) ) {
throw TypeError( 'Cannot redefine property: ' + key );
}
if( descriptor.enumerable === false ) {
if( 'value' in descriptor ) {
object[ '___' + key ] = descriptor.value;
defineProperty( object, key, {
get: function() {
return this[ '___' + key ];
},
set: function( value ) {
this[ '___' + key ] = value;
}
});
if( descriptor.writable === false ) {
defineProperty( object, key, {
get: function() {
return descriptor.value;
}
});
}
} else {
defineProperty( object, key, descriptor );
}
_settings_.notEnumerable.push( key );
} else {
if( 'get' in descriptor || 'set' in descriptor ) {
// что здесь можно сделать, чтоб свойство перечислялось с помощью for..in?
descriptor.enumerable === false;
}
defineProperty( object, key, descriptor );
_settings_.enumerable.push( key );
}
if( isConfugurable === false ) {
_settings_.notConfigurable.push( key );
}
_settings_.descriptors[ key ].configurable = isConfugurable;
};
Object.getOwnPropertyNames = function( object ) {
return object._settings_.enumerable.concat( object._settings_.notEnumerable );
};
Object.keys = function( object ) {
return object._settings_.enumerable;
};
Object.create = function( prototype, descriptors ) {
var object = new Constructor;
prototype && extend( object, prototype );
Object.defineProperties( object, descriptors );
return object;
};
Object.getOwnPropertyDescriptor = function( object, key ) {
if( !( object instanceof Constructor ) ) return getOwnPropertyDescriptor( object, key ); // if another dom object
return object._settings_ && object._settings_.descriptors[ key ];
};
};
function extend( o1, o2 ) {
for( var i in o2 ) {
o1[ i ] = o2[ i ];
}
}
if( Object.defineProperty ) {
Object.each = function( object, callback ) {
var props = object._settings_ && object._settings_.enumerable;
if( props ) {
for( var i = 0; i < props.length; i++ ) {
callback.call( object[ props[ i ] ], props[ i ], object[ props[ i ] ] );
}
} else {
for( var i in object ) if( object.hasOwnProperty( i ) ) {
callback.call( object[ i ], i, object[ i ] );
}
}
};
}
})();
x = Object.create( Object.prototype, {a: { value: 1, enumerable: true } });
console.log( x.a );
for( var i in x ) if( x.hasOwnProperty( i ) ){ console.log( i ) };
//Журнал: 1
//Журнал: a
y = Object.create( Object.prototype );
Object.defineProperties( y, {
b: {
get: function() {
return 2;
},
set: function( value ) {
alert( 'y.b setter, value = ' + value )
},
configurable: true
},
c: {
get: function() {
return 3;
},
set: function( value ) {
alert( 'y.c setter, value = ' + value )
}
},
d: {
value: 4,
configurable: false
},
e: {
value: 5,
enumerable: false,
writable: false
}
});
Object.each( y, function( key, value ) {
console.log( key, ' ', value )
});
//Журнал: b 2
//Журнал: c 3
//Журнал: d 4
console.log( JSON.stringify( y ) );
//Журнал: {"b":2,"c":3,"d":4}
Object.defineProperty( y, 'b', { get: function() { return 'changed B' }});
console.log( y.b );
//Журнал: changed B
console.log( 'e ', y.e );
//Журнал: e 5
y.e = 123;
console.log( 'e ', y.e );
//Журнал: e 5
Object.defineProperty( y, 'd', { get: function() { return 'changed D' }});
console.log( y.d );
//Error "Cannot redefine property: d"