cortex.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. module.metadata = {
  6. "stability": "deprecated"
  7. };
  8. // `var` is being used in the module in order to make it reusable in
  9. // environments in which `let` and `const` is not yet supported.
  10. // Returns `object`'s property value, where `name` is a name of the property.
  11. function get(object, name) {
  12. return object[name];
  13. }
  14. // Assigns `value` to the `object`'s property, where `name` is the name of the
  15. // property.
  16. function set(object, name, value) {
  17. return object[name] = value;
  18. }
  19. /**
  20. * Given an `object` containing a property with the given `name`, create
  21. * a property descriptor that can be used to define alias/proxy properties
  22. * on other objects. A change in the value of an alias will propagate
  23. * to the aliased property and vice versa.
  24. */
  25. function createAliasProperty(object, name) {
  26. // Getting own property descriptor of an `object` for the given `name` as
  27. // we are going to create proxy analog.
  28. var property = Object.getOwnPropertyDescriptor(object, name);
  29. var descriptor = {
  30. configurable: property.configurable,
  31. enumerable: property.enumerable,
  32. alias: true
  33. };
  34. // If the original property has a getter and/or setter, bind a
  35. // corresponding getter/setter in the alias descriptor to the original
  36. // object, so the `this` object in the getter/setter is the original object
  37. // rather than the alias.
  38. if ("get" in property && property.get)
  39. descriptor.get = property.get.bind(object);
  40. if ("set" in property && property.set)
  41. descriptor.set = property.set.bind(object);
  42. // If original property was a value property.
  43. if ("value" in property) {
  44. // If original property is a method using it's `object` bounded copy.
  45. if (typeof property.value === "function") {
  46. descriptor.value = property.value.bind(object);
  47. // Also preserving writability of the original property.
  48. descriptor.writable = property.writable;
  49. }
  50. // If the original property was just a data property, we create proxy
  51. // accessors using our custom get/set functions to propagate changes to the
  52. // original `object` and vice versa.
  53. else {
  54. descriptor.get = get.bind(null, object, name);
  55. descriptor.set = set.bind(null, object, name);
  56. }
  57. }
  58. return descriptor;
  59. }
  60. // Defines property on `object` object with a name `alias` if given if not
  61. // defaults to `name` that represents an alias of `source[name]`. If aliased
  62. // property was an assessor or a method `this` pseudo-variable will be `source`
  63. // when invoked. If aliased property was a data property changes on any of the
  64. // aliases will propagate to the `source[name]` and also other way round.
  65. function defineAlias(source, target, name, alias) {
  66. return Object.defineProperty(target, alias || name,
  67. createAliasProperty(source, name));
  68. }
  69. /**
  70. * Function takes any `object` and returns a proxy for its own public
  71. * properties. By default properties are considered to be public if they don't
  72. * start with `"_"`, but default behavior can be overridden if needed, by
  73. * passing array of public property `names` as a second argument. By default
  74. * returned object will be direct decedent of the given `object`'s prototype,
  75. * but this can be overridden by passing third optional argument, that will be
  76. * used as `prototype` instead.
  77. * @param {Object} object
  78. * Object to create cortex for.
  79. * @param {String[]} [names]
  80. * Optional array of public property names.
  81. * @param {Object} [prototype]
  82. * Optional argument that will be used as `prototype` of the returned object,
  83. * if not provided `Object.getPrototypeOf(object)` is used instead.
  84. */
  85. exports.Cortex = function Cortex(object, names, prototype) {
  86. // Creating a cortex object from the given `prototype`, if one was not
  87. // provided then `prototype` of a given `object` is used. This allows
  88. // consumer to define expected behavior `instanceof`. In common case
  89. // `prototype` argument can be omitted to preserve same behavior of
  90. // `instanceof` as on original `object`.
  91. var cortex = Object.create(prototype || Object.getPrototypeOf(object));
  92. // Creating alias properties on the `cortex` object for all the own
  93. // properties of the original `object` that are contained in `names` array.
  94. // If `names` array is not provided then all the properties that don't
  95. // start with `"_"` are aliased.
  96. Object.getOwnPropertyNames(object).forEach(function (name) {
  97. if ((!names && "_" !== name.charAt(0)) || (names && ~names.indexOf(name)))
  98. defineAlias(object, cortex, name);
  99. });
  100. return cortex;
  101. }