210 lines
7.3 KiB
JavaScript
210 lines
7.3 KiB
JavaScript
/**
|
|
* @file Sham for ES6 Object.getOwnPropertyDescriptor
|
|
* @version 3.2.0
|
|
* @author Xotic750 <Xotic750@gmail.com>
|
|
* @copyright Xotic750
|
|
* @license {@link <https://opensource.org/licenses/MIT> MIT}
|
|
* @module object-get-own-property-descriptor-x
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var toObject = require('to-object-x');
|
|
var toPropertyKey = require('to-property-key-x');
|
|
var isFalsey = require('is-falsey-x');
|
|
var attempt = require('attempt-x');
|
|
|
|
var nativeGOPD = typeof Object.getOwnPropertyDescriptor === 'function' && Object.getOwnPropertyDescriptor;
|
|
var getOPDFallback1;
|
|
var getOPDFallback2;
|
|
|
|
// ES5 15.2.3.3
|
|
// http://es5.github.com/#x15.2.3.3
|
|
|
|
var doesGOPDWork = function (object, prop) {
|
|
object[toPropertyKey(prop)] = 0;
|
|
var testResult = attempt(nativeGOPD, object, prop);
|
|
return testResult.threw === false && testResult.value.value === 0;
|
|
};
|
|
|
|
// check whether getOwnPropertyDescriptor works if it's given. Otherwise, shim partially.
|
|
var $getOwnPropertyDescriptor;
|
|
if (nativeGOPD) {
|
|
var doc = typeof document !== 'undefined' && document;
|
|
var getOPDWorksOnDom = doc ? doesGOPDWork(doc.createElement('div'), 'sentinel') : true;
|
|
if (getOPDWorksOnDom) {
|
|
var res = attempt(nativeGOPD, Object('abc'), 1);
|
|
var worksWithStr = res.threw === false && res.value && res.value.value === 'b';
|
|
if (worksWithStr) {
|
|
var getOPDWorksOnObject = doesGOPDWork({}, 'sentinel');
|
|
if (getOPDWorksOnObject) {
|
|
var worksWithPrim = attempt(nativeGOPD, 42, 'name').threw === false;
|
|
var worksWithObjSym = require('has-symbol-support-x') && doesGOPDWork({}, Object(Symbol('')));
|
|
// eslint-disable-next-line max-depth
|
|
if (worksWithObjSym) {
|
|
// eslint-disable-next-line max-depth
|
|
if (worksWithPrim) {
|
|
$getOwnPropertyDescriptor = nativeGOPD;
|
|
} else {
|
|
$getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
|
|
return nativeGOPD(toObject(object), property);
|
|
};
|
|
}
|
|
} else if (worksWithPrim) {
|
|
$getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
|
|
return nativeGOPD(object, toPropertyKey(property));
|
|
};
|
|
} else {
|
|
$getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
|
|
return nativeGOPD(toObject(object), toPropertyKey(property));
|
|
};
|
|
}
|
|
} else {
|
|
getOPDFallback1 = nativeGOPD;
|
|
}
|
|
} else {
|
|
getOPDFallback2 = nativeGOPD;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isFalsey($getOwnPropertyDescriptor) || getOPDFallback1 || getOPDFallback2) {
|
|
var owns = require('has-own-property-x');
|
|
var isPrimitive = require('is-primitive');
|
|
var isString = require('is-string');
|
|
var isIndex = require('is-index-x');
|
|
var propertyIsEnumerable = require('property-is-enumerable-x');
|
|
var prototypeOfObject = Object.prototype;
|
|
|
|
// If JS engine supports accessors creating shortcuts.
|
|
var lookupGetter;
|
|
var lookupSetter;
|
|
var supportsAccessors = owns(prototypeOfObject, '__defineGetter__');
|
|
if (supportsAccessors) {
|
|
// eslint-disable-next-line no-underscore-dangle
|
|
var lg = prototypeOfObject.__lookupGetter__;
|
|
// eslint-disable-next-line no-underscore-dangle
|
|
var ls = prototypeOfObject.__lookupSetter__;
|
|
lookupGetter = function (object, property) {
|
|
return lg.call(object, property);
|
|
};
|
|
|
|
lookupSetter = function (object, property) {
|
|
return ls.call(object, property);
|
|
};
|
|
}
|
|
|
|
$getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
|
|
var obj = toObject(object);
|
|
var propKey = toPropertyKey(property);
|
|
|
|
var result;
|
|
// make a valiant attempt to use the real getOwnPropertyDescriptor for I8's DOM elements.
|
|
if (getOPDFallback1) {
|
|
result = attempt.call(Object, getOPDFallback1, obj, propKey);
|
|
if (result.threw === false) {
|
|
return result.value;
|
|
}
|
|
// try the shim if the real one doesn't work
|
|
}
|
|
|
|
var isStringIndex = isString(obj) && isIndex(propKey, obj.length);
|
|
if (getOPDFallback2 && isStringIndex === false) {
|
|
result = attempt.call(Object, getOPDFallback2, obj, propKey);
|
|
if (result.threw === false) {
|
|
return result.value;
|
|
}
|
|
// try the shim if the real one doesn't work
|
|
}
|
|
|
|
var descriptor;
|
|
// If object does not owns property return undefined immediately.
|
|
if (isStringIndex === false && owns(obj, propKey) === false) {
|
|
return descriptor;
|
|
}
|
|
|
|
// If object has a property then it's for sure `configurable`, and
|
|
// probably `enumerable`. Detect enumerability though.
|
|
descriptor = {
|
|
configurable: isPrimitive(object) === false && isStringIndex === false,
|
|
enumerable: propertyIsEnumerable(obj, propKey)
|
|
};
|
|
|
|
// If JS engine supports accessor properties then property may be a
|
|
// getter or setter.
|
|
if (supportsAccessors) {
|
|
// Unfortunately `__lookupGetter__` will return a getter even
|
|
// if object has own non getter property along with a same named
|
|
// inherited getter. To avoid misbehavior we temporary remove
|
|
// `__proto__` so that `__lookupGetter__` will return getter only
|
|
// if it's owned by an object.
|
|
// eslint-disable-next-line no-proto
|
|
var prototype = obj.__proto__;
|
|
var notPrototypeOfObject = obj !== prototypeOfObject;
|
|
// avoid recursion problem, breaking in Opera Mini when
|
|
// Object.getOwnPropertyDescriptor(Object.prototype, 'toString')
|
|
// or any other Object.prototype accessor
|
|
if (notPrototypeOfObject) {
|
|
// eslint-disable-next-line no-proto
|
|
obj.__proto__ = prototypeOfObject;
|
|
}
|
|
|
|
var getter = lookupGetter(obj, propKey);
|
|
var setter = lookupSetter(obj, propKey);
|
|
|
|
if (notPrototypeOfObject) {
|
|
// Once we have getter and setter we can put values back.
|
|
// eslint-disable-next-line no-proto
|
|
obj.__proto__ = prototype;
|
|
}
|
|
|
|
if (getter || setter) {
|
|
if (getter) {
|
|
descriptor.get = getter;
|
|
}
|
|
|
|
if (setter) {
|
|
descriptor.set = setter;
|
|
}
|
|
|
|
// If it was accessor property we're done and return here
|
|
// in order to avoid adding `value` to the descriptor.
|
|
return descriptor;
|
|
}
|
|
}
|
|
|
|
// If we got this far we know that object has an own property that is
|
|
// not an accessor so we set it as a value and return descriptor.
|
|
if (isStringIndex) {
|
|
descriptor.value = obj.charAt(propKey);
|
|
descriptor.writable = false;
|
|
} else {
|
|
descriptor.value = obj[propKey];
|
|
descriptor.writable = true;
|
|
}
|
|
|
|
return descriptor;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* This method returns a property descriptor for an own property (that is,
|
|
* one directly present on an object and not in the object's prototype chain)
|
|
* of a given object.
|
|
*
|
|
* @param {*} object - The object in which to look for the property.
|
|
* @param {*} property - The name of the property whose description is to be retrieved.
|
|
* @returns {Object} A property descriptor of the given property if it exists on the object, undefined otherwise.
|
|
* @example
|
|
* var getOwnPropertyDescriptor = require('object-get-own-property-descriptor-x');
|
|
* var obj = { bar: 42 };
|
|
* var d = getOwnPropertyDescriptor(o, 'bar');
|
|
* // d is {
|
|
* // configurable: true,
|
|
* // enumerable: true,
|
|
* // value: 42,
|
|
* // writable: true
|
|
* // }
|
|
*/
|
|
module.exports = $getOwnPropertyDescriptor;
|