Is it possible to modify defined getter function?

96 views Asked by At

Working on a performance reviewing tool on wechat mini apps platform (javascript + native hybrid based on wechat app), I am trying to inject codes into its prototypes, for example the wx.request function.

This is how you would use a wx.request function:

wx.request({
  url: 'test.php',
  data: {
     x: '' ,
     y: ''
  },
  header: {
      'content-type': 'application/json'
  },
  success: function(res) {
    console.log(res.data)
  }
})

So in order to know how long the request has taken without manually writing adding all the anchors, I tried to inject code by:

var owxrequest = wx.request
wx.request = function() {
  console.log('test', Date.now())
  return owxrequest.apply(owxrequest, arguments)
}

This failed and I got an Cannot set property "prop" of #<Object> which has only a getter error.

So I realized the the object must have been defined similar to:

wx = {
  request: get function(){
    ...
  }
  ...
}

So I tried:

var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
  get: function() {
    console.log('test', Date.now())
    return owxrequest.apply(owxrequest, arguments)
  }
})

This failed with an error (request: fail parameter error: parameter.url should be String instead of Undefined). Then I tried:

var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
  set: function() {
    console.log('test', Date.now())
    return owxrequest.apply(owxrequest, arguments)
  }
})

This wouldn't throw an error but it also has no effect when calling wx.request()...

2

There are 2 answers

2
shaochuancs On BEST ANSWER

You can implement this by re-define the getter. The point is: the re-defined getter should return a function object, as wx.request is a function:

Object.defineProperty(wx, 'request', {
  get: function() {
    return function() {
      //...
    };
  }
});

Why I get the error: request: fail parameter error: parameter.url should be String instead of Undefined?

You are trying to access the arguments of the getter itself (the arguments of function in get: function(){...}). This arguments is an empty object and it can be verified by console.log() statement. As it is empty, arguments.url is undefined, that's why wx complains about the parameter.

Here is an working example:

let wx = {
  get request(){
    return function() {
      console.log(10);
      return 88;
    };
  }
};

let oldF = wx.request;

Object.defineProperty(wx, 'request', {
  get: function() {
    return function() {
      console.log(new Date());
      return oldF.apply(wx, arguments);
    };
  }
});

console.log(wx.request());

The above code would print:

2017-08-28T06:14:15.583Z // timestamp
10
88
2
GibboK On

You could just shadowing the request function. Simple example:

Shadowing the getter:

// original getter latest
let base = {
  num: 1,
  get latest() {
    return this.num;
  }
}
console.log(base.latest);

// shadowing getter latest
Object.defineProperty(base, 'latest', {
  get: function() {
    return this.num + 1;
  }
});

console.log(base.latest);

Simple shadowing a object property

// your original object
let base = {
    request(){
        console.log('request original');
    }
};

base.request()


base.request = () => {
    console.log('request new implementation');
};
 
// now shadow the original request implementation
base.request()