5

For any unknown number of objects, there are certain properties I want to intercept and change if needed. I have tried getters and setters, but I was only able to achieve close to what I wanted and only for known objects.

The following are examples of what I am trying to achieve:

Objects being created outside of my scope/closure

As you can see these are objects that cannot be accessed from outside and what I want to do is check every year_of_birth property and modify it whenever it is created or altered for any unknown object. The closest I got was through getters/setters, but I had to pass the object and it would loop itself every time it change its value.

An example that tries to correct the year_of_birth by checking the age with the current year

Object.defineProperty(unknown_object, 'year_of_birth', {
    set: function(year) {
        if (this.age && 2017 - this.age > year) {
            this.year_of_birth = 2017 - this.age;
        }
    }
});

or

Object.defineProperty(unknown_object, 'year_of_birth', {
    get: function(year) {
        if (this.age && 2017 - this.age > year) {
            return 2017 - this.age;
        }
    }
});

but still did not work well and only worked with singular objects that I had direct access.

Is there any way to do this? Please no solutions with libraries/frameworks like jQuery.

EDIT: Snippet example because the above was not clear enough:

// Here I define some sort of solution to manipulate any object property of name "year_of_birth"

// unknown_object or a sort of prototype/constructor that intercepts any object, regardless of its scope

Object.defineProperty(unknown_object, 'year_of_birth', {
    set: function(year) {
        if (this.age && 2017 - this.age > year) {
            this.year_of_birth = 2017 - this.age;
        }
    }
});

// or

Object.defineProperty(unknown_object, 'year_of_birth', {
    get: function(year) {
        if (this.age && 2017 - this.age > year) {
            return 2017 - this.age;
        }
    }
});


// I want to modify "year_of_birth" or any other object property that is inside the below scope
// I do not have access to that scope, I cannot modify the function to allow access to its scope, I cannot do anything to it besides trying to modify the objects from the outside through some method that I am trying to find out in this question

(function() {
    var john = {
        age: 28,
        year_of_birth: 1985
    };
    var anne = {
        age: 31,
        year_of_birth: 1985
    };
}());

// Whenever a "year_of_birth" object property is read/set it should change to whichever value it has been established in the setters/getters at the top
Shadow
  • 4,168
  • 5
  • 41
  • 72
  • Do you have a handle for your object? Do you control who gets a reference to it in your code? – Madara's Ghost Mar 05 '17 at 17:44
  • @MadaraUchiha as I explained, I intend to do this for any number of objects that I have no direct access to. The example I provided shows how these objects might be generated, inside a self-closed anonymous function of which I have no way to manipulate its contents or expose them to the outside scope. – Shadow Mar 05 '17 at 17:46
  • What you want is not possible. – Felix Kling Mar 05 '17 at 17:58
  • This is not possible. If some library creates a private object, it should be ***very*** undesirable that the object gets a different behaviour or data because of something you have done. – trincot Mar 05 '17 at 18:00
  • _"Objects being created outside of my scope/closure"_ How do you become aware the objects are being created at all or access the objects? – guest271314 Mar 05 '17 at 18:01
  • @guest271314 I don't, I was clear about that, but maybe through `.prototype` or `.constructor` it might be possible to have access to any generated object. – Shadow Mar 05 '17 at 18:02
  • Not gathering context fully. What process creates the object and how are objects accessed by the `javascript` which you have control over? If you do not know if the objects are being created or not, how do you know the objects exist? – guest271314 Mar 05 '17 at 18:05
  • @guest271314 The same way this was achieved: http://stackoverflow.com/q/26447335/524695 We were able to find solutions based on the `prototype` property, which allowed the manipulation of any type of request. I am hoping something similar can be done for this case. – Shadow Mar 05 '17 at 18:06
  • Do you mean that you are trying to modify response from an ajax request? – guest271314 Mar 05 '17 at 18:08
  • @guest271314 No, I am not, otherwise I would have included that in the question just like I did in the one I linked to you. What I want is to manipulate any object property with the name `X` of any object whenever that property is read or assigned and I have no way to access it directly. I am repeating what I already explained in the question. – Shadow Mar 05 '17 at 18:11
  • _"whenever that property is read or assigned and I have no way to access it directly."_ The you do have access to the object. Why cannot you manipulate the property when you read or assign the property? – guest271314 Mar 05 '17 at 18:17
  • @guest271314 I don't know if I have access to the objects otherwise I wouldn't have even posted the question here. I clearly said that I do not have direct access to the objects, it does not mean in any way that I have access to them indirectly. I am hoping there is, either via constructor or prototype, but I have not found any solution which is why this question was posted in the first place. – Shadow Mar 05 '17 at 18:20
  • Still not entirely clear what the context is, though you could possibly utilize `Proxy` to achieve requirement [How to detect when a JavaScript object variable is accessed?](http://stackoverflow.com/questions/41405080/how-to-detect-when-a-javascript-object-variable-is-accessed). Can you create a stacksnippets or plnkr http://plnkr.co to demonstrate an example of the context? – guest271314 Mar 05 '17 at 18:22
  • @guest271314 The context is, for example, a userscript that needs to manipulate object properties of another 3rd party script that does not have an accessible scope, thus the option to intercept any object manipulation (regardless of the scope it is being created) is the only one left for the specific case I am trying to work. – Shadow Mar 05 '17 at 18:23
  • @Shadow How does your script access the object? Can you reproduce an example of the context at plnkr http://plnkr.co? – guest271314 Mar 05 '17 at 18:26
  • @guest271314 I already explained 3 times that I don't have any access to the objects, as of this moment I cannot access the objects, this is why the question was posted in the first place, there is no way that I know to access the objects that I want to manipulate. I want to find a generic way that intercepts any object that has been generated somewhere, I don't know where and I can't access, and modify specific properties of a certain name whenever they are either read or changed. I have added an example snippet, if you still don't understand this after 4 times then don't insist anymore. – Shadow Mar 05 '17 at 18:32
  • Have not "insist"ed anything, here. Will ask questions as much as need to in order to understand the phenomenon, in any circumstance. It is you who posted the Question. Do you not expect questions to be asked as to your question as well? All questions have answers. The universe is founded in logic; logic requires questions to be formulated, presented and vetted, else no answers can be derived. – guest271314 Mar 05 '17 at 18:34
  • If the script which contains the IIFE is loaded into the context which you have control of you can get the `.textContent` of the script, parse the IIFE using `RegExp` and get the objects, and all properties of the objects within the IIFE. – guest271314 Mar 05 '17 at 18:40
  • That is pointless since I cannot do anything with that information. Had the "beforescriptexecute" event been implemented in more browsers then it would be possible to manipulate inline scripts, but as it stands that is a no go because once the script is committed, there is nothing I can do to change it and if I cannot access its scope then it doesn't matter reading its textContent. – Shadow Mar 05 '17 at 18:53
  • @Shadow You can reassign the object to the manipulated value. Scope is irrelevant as to redefinition of the objects. Once you manipulate the objects, or any other content within the loaded script to meet requirement, you can remove the original ` – guest271314 Mar 05 '17 at 19:21

3 Answers3

9

After being told multiple times that what I was asking was not possible, I discovered a way to achieve this by targeting the Object.prototype.

The solution is as follows, with "my_property" being any object property name I wish to target.

Object.defineProperty(Object.prototype, "my_property", {
    set: function (value) {
        this._value = value;
    },
    get: function () {
        return "changed";
    }
});

var some_object = {};

some_object.my_property = "unchanged";


document.body.innerHTML += some_object.my_property;
My property value is: 
Shadow
  • 4,168
  • 5
  • 41
  • 72
0

You can use RegExp to parse original <script> .textContent to get variable identifiers and objects assigned to variable identifiers. Manipulate objects to meet requirement, assign identifier to global or other object. Remove original <script> from document

let scripts = document.scripts;

let iife = scripts.namedItem("iife");

let iifeText = iife.textContent;

let objs = iifeText.replace(/\(function\(\) \{|\}\(\)\)/g, "")
  .replace(/(var|let|const)|[a-z_]+?\s=(?=\s\{|\{)|\s+/ig, "")
  .replace(/(\w+)(?=:)/g, "\"$1\"")
  .match(/[^;]+/g);

let props = iifeText
  .match(/(var|let|const)\s[a-z_]+?\s=?\s(?=\{)/ig)
  .map(function(id) {
    return id.replace(/var|let|const|\s+|=/g, "");
  });

let setProp = (obj, value) => {
  // perform logic here
  if (obj.age && 2017 - obj.age > value) {
    obj.year_of_birth = 2017 - obj.age;
  }
  return obj
}

let getProp = (obj, value) => {
  if (obj.age && 2017 - obj.age > value) {
    return 2017 - obj.age;
  }
  return obj
}

for (let [key, prop] of props.entries()) {
  this[prop] = setProp(JSON.parse(objs[key]), 27);
}

// remove original `script`
iife.remove();

console.log(props, objs);

for (let prop of props) {
  // changed properties of objects
  console.log(this[prop]);
}
<script name="iife">
  (function() {
    var john = {
      age: 28,
      year_of_birth: 1985
    };
    var anne = {
      age: 31,
      year_of_birth: 1985
    };
  }());
</script>
guest271314
  • 1
  • 15
  • 104
  • 177
  • @Shadow Once the objects have been parsed you can use `Proxy` approach at linked answer to observe, intercept `get`, `set` on the objects. – guest271314 Mar 05 '17 at 20:26
0

At the end of the day, it boils down to the question of whether you have a handle for your object or not. If you do, great, you can alter its .prototype and __proto__, you can use Object.defineProperty to define getters and setters with your own logic.

If you have control over how the 3rd party object gets distributed throughout your code, you can even use a Proxy and replace the reference.

If you don't have a reference to your object, and it just kind of floats around in the back in closed scopes, you can do nothing.


It's worth noting that in these cases, using the Facade design pattern can be considered preferable, then you can have all the customization you want, with your own API to boot.

Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308