1 Star 12 Fork 1

Ambit / object-defineproperty-ie

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
object-defineproperty-ie.js 16.59 KB
一键复制 编辑 原始数据 按行查看 历史
Ambit 提交于 2020-03-25 11:11 . refactor: delete isReactive
/**
* Object.defineProperty Sham For IE
* @version 3.1.0
* @author Ambit Tsai <ambit_tsai@qq.com>
* @license Apache-2.0
* @see {@link https://github.com/ambit-tsai/object-defineproperty-ie}
*/
(function (window, Object) {
// Constant variables
var UNDEFINED; // => undefined
var DEFINE_PROPERTY = 'defineProperty';
var DEFINE_PROPERTIES = 'defineProperties';
var GET_OWN_PROPERTY_DESCRIPTOR = 'getOwnPropertyDescriptor';
var GET_OWN_PROPERTY_DESCRIPTORS = GET_OWN_PROPERTY_DESCRIPTOR + 's'; // => "getOwnPropertyDescriptors"
var DESCRIPTOR_NOT_OBJECT = 'Property description must be an object: ';
var INTERNAL_NAME = '__INTERNAL__';
var ENUMERABLE = 'enumerable';
var CONFIGURABLE = 'configurable';
var VALUE = 'value';
var WRITABLE = 'writable';
var GET = 'get';
var SET = 'set';
/**
* Hack `Object.getOwnPropertyNames`
* @param {object} obj
* @returns {string[]}
*/
var getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) {
var names = [];
forEach(obj, function (key) {
names.push(key);
});
return names;
};
// Sham for `Object.defineProperty`
if (Object[DEFINE_PROPERTY]) {
try {
// In IE 8, `Object.defineProperty` is only effective on `Element` object,
// `document` and `window`. The program will throw an exception when
// `Object.defineProperty` works with other objects.
Object[DEFINE_PROPERTY]({}, '', {});
} catch(err) {
var _defineProperty = Object[DEFINE_PROPERTY];
Object[DEFINE_PROPERTIES] = function (obj, props) {
// Use the native method for `Element` object, `document` and `window`
if (obj instanceof Element || obj === document || obj === window) {
if (!isObject(props)) {
throwTypeError(DESCRIPTOR_NOT_OBJECT + props);
}
forEach(props, function (key, desc) {
_defineProperty(obj, key, desc);
});
return obj;
} else {
return implementDefineProperties(obj, props);
}
};
Object[DEFINE_PROPERTY] = function (obj, key, desc) {
var props = {};
props[key] = desc;
return Object[DEFINE_PROPERTIES](obj, props);
};
}
} else {
Object[DEFINE_PROPERTY] = function (obj, key, desc) {
var props = {};
props[key] = desc;
return implementDefineProperties(obj, props);
};
}
// Sham for `Object.defineProperties`
if (!Object[DEFINE_PROPERTIES]) {
if (/\[native code\]/.test(Object[DEFINE_PROPERTY].toString())) {
// Use the native method `Object.defineProperty`
Object[DEFINE_PROPERTIES] = function (obj, props) {
if (!isObject(props)) {
throwTypeError(DESCRIPTOR_NOT_OBJECT + props);
}
forEach(props, function (key, desc) {
Object[DEFINE_PROPERTY](obj, key, desc);
});
return obj;
};
} else {
Object[DEFINE_PROPERTIES] = implementDefineProperties;
}
}
// Sham for `Object.getOwnPropertyDescriptor`
if (!Object[GET_OWN_PROPERTY_DESCRIPTOR]) {
Object[GET_OWN_PROPERTY_DESCRIPTOR] = implementGetOwnPropertyDescriptor;
} else if (Object[GET_OWN_PROPERTY_DESCRIPTOR](window, CONFIGURABLE + CONFIGURABLE)) {
// In IE 8, `Object.getOwnPropertyDescriptor` will not return an `undefined` when
// using it to get the descriptor of a property that do not exist, and it's only
// effective on `Element` object, `document` and `window`.
var _getOwnPropertyDescriptor = Object[GET_OWN_PROPERTY_DESCRIPTOR];
Object[GET_OWN_PROPERTY_DESCRIPTOR] = function (obj, key) {
if (obj instanceof Element || obj === document || obj === window) {
return hasOwnProperty(obj, key) ? _getOwnPropertyDescriptor(obj, key) : UNDEFINED;
} else {
return implementGetOwnPropertyDescriptor(obj, key);
}
};
}
// Shim for `Object.getOwnPropertyDescriptors`
if (!Object[GET_OWN_PROPERTY_DESCRIPTORS]) {
Object[GET_OWN_PROPERTY_DESCRIPTORS] = function (obj) {
var names = getOwnPropertyNames(obj);
var descMap = {};
for (var i = names.length - 1; i >= 0; --i) {
var key = names[i];
var desc = Object[GET_OWN_PROPERTY_DESCRIPTOR](obj, key);
if (desc) descMap[key] = desc;
}
return descMap;
};
}
/**
* Check if value is the language type of Object
* @param {any} value
* @returns {boolean}
*/
function isObject(value) {
return value
? typeof value === 'object' || typeof value === 'function'
: false;
}
/**
* Throw a type error
* @param {string} message
*/
function throwTypeError(message) {
throw new TypeError(message);
}
/**
* Execute a provided function once for each property
* @param {object} obj
* @param {(key: string, value: any) => undefined} fn
*/
function forEach(obj, fn) {
for (var key in obj) {
if (hasOwnProperty(obj, key)) {
fn(key, obj[key]);
}
}
}
/**
* Check if key is an own property of object
* @param {object} obj
* @param {string} key
* @returns {boolean}
*/
function hasOwnProperty(obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
}
/**
* The internal implementation of `Object.defineProperties`
* @param {object} obj
* @param {object} props descriptors
* @returns {object}
*/
function implementDefineProperties(obj, props) {
if (!isObject(obj)) {
throwTypeError('Method called on non-object');
}
if (!isObject(props)) {
throwTypeError(DESCRIPTOR_NOT_OBJECT + props);
}
// Check descriptors
var names = getOwnPropertyNames(props);
var descMap = {}, hasNewProperty;
for (var i = names.length - 1; i >= 0; --i) {
var key = names[i];
var desc = toPropertyDescriptor(props[key]);
descMap[key] = desc;
if (!hasOwnProperty(obj, key)) {
hasNewProperty = true;
}
}
if (!names.length && !getOwnPropertyNames(obj).length) {
return obj;
} else if (isVbObject(obj) && !hasNewProperty) {
mergePropertyDescriptors(getVbInternalOf(obj).props, descMap);
return obj;
} else {
props = Object[GET_OWN_PROPERTY_DESCRIPTORS](obj);
mergePropertyDescriptors(props, descMap);
return createVbObject(props);
}
}
/**
* Convert to a standard descriptor
* @param {object} obj
* @returns {object} descriptor
*/
function toPropertyDescriptor(obj) {
if (!isObject(obj)) {
throwTypeError(DESCRIPTOR_NOT_OBJECT + obj);
}
var desc = {};
if (ENUMERABLE in obj) desc[ENUMERABLE] = !!obj[ENUMERABLE];
if (CONFIGURABLE in obj) desc[CONFIGURABLE] = !!obj[CONFIGURABLE];
if (VALUE in obj) desc[VALUE] = obj[VALUE];
if (WRITABLE in obj) desc[WRITABLE] = !!obj[WRITABLE];
if (GET in obj) {
if (typeof obj[GET] !== 'function' && obj[GET] !== UNDEFINED) {
throwTypeError('Getter must be a function: ' + obj[GET]);
} else {
desc[GET] = obj[GET];
}
}
if (SET in obj) {
if (typeof obj[SET] !== 'function' && obj[SET] !== UNDEFINED) {
throwTypeError('Setter must be a function: ' + obj[SET]);
} else {
desc[SET] = obj[SET];
}
}
if ((GET in desc || SET in desc) && (VALUE in desc || WRITABLE in desc)) {
throwTypeError('Cannot both specify accessors and a value or writable attribute');
}
return desc;
}
/**
* Check if object is the type of custom VB Object
* @param {object} obj
* @returns {boolean}
*/
function isVbObject(obj) {
if (!(INTERNAL_NAME in obj)) {
try {
obj[INTERNAL_NAME] = 0; // VB object can't add properties freely
delete obj[INTERNAL_NAME];
} catch(err) {
return true;
}
}
return false;
}
// Exposed to global
window._isVbObject = isVbObject;
/**
* Get the internal data of custom VB object
* @param {object} vbObj
* @returns {object}
*/
function getVbInternalOf(vbObj) {
for (var key in vbObj) {
vbObj[key] = Internal;
return vbObj[key];
}
}
// Exposed to global
window._getVbInternalOf = getVbInternalOf;
/**
* The object to store internal data
* @constructor
* @param {object} descriptors
*/
function Internal(descriptors) {
this.props = descriptors;
this.keyMap = {};
this.canGetData = UNDEFINED; // a flag used to judge whether return internal data
this.getterReturn = UNDEFINED; // a variable that cache the getter return value
}
/**
* Getter
* @param {number} index the index of key
* @param {object} ctx context
* @returns {boolean} is `getterReturn` an object
*/
Internal.prototype.getter = function (index, ctx) {
if (this.canGetData === index) {
this.getterReturn = this; // return internal data
this.canGetData = UNDEFINED; // reset flag
return true;
}
var key = this.keyMap[index];
var desc = this.props[key];
this.getterReturn = desc[GET]
? desc[GET].call(ctx)
: desc[VALUE];
return isObject(this.getterReturn);
};
/**
* Setter
* @param {number} index the index of key
* @param {object} ctx context
* @param {any} val value
*/
Internal.prototype.setter = function (index, ctx, val) {
// Constructor `Internal` is used as a key
if (val === Internal) {
this.canGetData = index;
return;
}
var key = this.keyMap[index];
var desc = this.props[key];
if (desc[WRITABLE]) {
desc[VALUE] = val;
} else if (desc[SET]) {
desc[SET].call(ctx, val);
}
};
/**
* Merge every descriptor of source into target
* @param {object} target descriptor map
* @param {object} source descriptor map
*/
function mergePropertyDescriptors(target, source) {
forEach(source, function (key, sDesc) {
var tDesc = target[key];
if (tDesc) {
// Validate
if (tDesc[CONFIGURABLE] === false && ((
VALUE in tDesc && (
sDesc[CONFIGURABLE] || GET in sDesc || SET in sDesc
|| (!tDesc[WRITABLE] && VALUE in sDesc && sDesc[VALUE] !== tDesc[VALUE])
|| (!tDesc[WRITABLE] && sDesc[WRITABLE])
|| (ENUMERABLE in sDesc && sDesc[ENUMERABLE] !== tDesc[ENUMERABLE])
)
) || (
GET in tDesc && (
sDesc[CONFIGURABLE] || VALUE in sDesc || WRITABLE in sDesc
|| (ENUMERABLE in sDesc && sDesc[ENUMERABLE] !== tDesc[ENUMERABLE])
)
))) {
throwTypeError('Cannot redefine property: ' + key);
}
} else {
// Set default value
tDesc = {};
tDesc[VALUE] = UNDEFINED;
tDesc[WRITABLE] = false;
tDesc[ENUMERABLE] = false;
tDesc[CONFIGURABLE] = false;
}
// Merge
target[key] = sDesc;
if (VALUE in sDesc || WRITABLE in sDesc) {
if (!(VALUE in sDesc)) sDesc[VALUE] = tDesc[VALUE];
if (!(WRITABLE in sDesc)) sDesc[WRITABLE] = tDesc[WRITABLE];
} else if (GET in sDesc || SET in sDesc) {
if (!(GET in sDesc)) sDesc[GET] = tDesc[GET];
if (!(SET in sDesc)) sDesc[SET] = tDesc[SET];
} else if (VALUE in tDesc) {
sDesc[VALUE] = tDesc[VALUE];
sDesc[WRITABLE] = tDesc[WRITABLE];
} else {
sDesc[GET] = tDesc[GET];
sDesc[SET] = tDesc[SET];
}
if (!(ENUMERABLE in sDesc)) sDesc[ENUMERABLE] = tDesc[ENUMERABLE];
if (!(CONFIGURABLE in sDesc)) sDesc[CONFIGURABLE] = tDesc[CONFIGURABLE];
});
}
/**
* Custom VB object factory
* @param {object} descriptors
* @returns {object} VB object
*/
function createVbObject(descriptors) {
// Generate VB script
var UID = window.setTimeout(Object); // generate an unique id
var INTERNAL = '[' + INTERNAL_NAME + ']'; // => "[__INTERNAL__]"
var buffer = [
'Class VbClass' + UID,
' Private ' + INTERNAL
];
var i = 0;
var internal = new Internal(descriptors);
forEach(descriptors, function (key) {
if (key === INTERNAL_NAME) {
throwTypeError('Property "' + key + '" is the reserved word of "object-defineproperty-ie"');
}
var prop = '[' + key + ']';
var arg = key === 'val' ? 'v' : 'val';
buffer.push(
' Public Property Get ' + prop,
' If ' + INTERNAL + '.getter(' + i + ', ME) Then',
' Set ' + prop + ' = ' + INTERNAL + '.getterReturn',
' Else',
' ' + prop + ' = ' + INTERNAL + '.getterReturn',
' End If',
' End Property',
' Public Property Let ' + prop + '(' + arg + ')',
' ' + INTERNAL + '.setter ' + i + ', ME, ' + arg,
' End Property',
' Public Property Set ' + prop + '(' + arg + ')'
);
if (i) {
buffer.push(
' ' + INTERNAL + '.setter ' + i + ', ME, ' + arg
);
} else {
// Initialize `__INTERNAL__` at index 0
buffer.push(
' If isEmpty(' + INTERNAL + ') Then',
' Set ' + INTERNAL + ' = ' + arg,
' Else',
' ' + INTERNAL + '.setter ' + i + ', ME, ' + arg,
' End If'
);
}
buffer.push(' End Property');
internal.keyMap[i++] = key;
});
buffer.push(
'End Class',
'Function VbFactory' + UID + '()',
' Set VbFactory' + UID + ' = New VbClass' + UID,
'End Function'
);
window.execScript(buffer.join('\r\n'), 'VBS'); // execute the VB script
var vbObj = window['VbFactory' + UID](); // use the factory to create an object
vbObj[ internal.keyMap[0] ] = internal; // initialize internal internal
return vbObj;
}
/**
* The internal implementation of `Object.getOwnPropertyDescriptor`
* @param {object} obj
* @param {string} key
* @returns {object}
*/
function implementGetOwnPropertyDescriptor(obj, key) {
if (!hasOwnProperty(obj, key)) {
return;
}
// Custom VB object
if (isVbObject(obj)) {
return objectAssign({}, getVbInternalOf(obj).props[key]);
}
// Others
var desc = {};
desc[VALUE] = obj[key];
desc[WRITABLE] = true;
desc[CONFIGURABLE] = true;
desc[ENUMERABLE] = false;
for (var prop in obj) {
if (prop === key) {
desc[ENUMERABLE] = true;
break;
}
}
return desc;
}
/**
* Hack `Object.assign`
* @param {object} target
* @param {object} source
* @returns {object}
*/
function objectAssign(target, source) {
forEach(source, function (key, value) {
target[key] = value;
});
return target;
}
}(window, Object));
JavaScript
1
https://gitee.com/ambit/object-defineproperty-ie.git
git@gitee.com:ambit/object-defineproperty-ie.git
ambit
object-defineproperty-ie
object-defineproperty-ie
master

搜索帮助