这不仅仅是简单的Object.defineProperty。你需要先获取属性描述符(使用Object.getOwnPropertyDescriptor),然后检查属性是否可配置。如果它不可配置,你无法覆盖它。此外,你需要考虑一个属性可能具有getter/setter XOR值。因此,如果你有getter/setter,就不能设置desc.value和desc.writable,反之亦然。
let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
if (!desc.configurable)
{
console.log("AUTOTRACE-WARNING: Property \"" + key + "\" not configurable ! (" + self.constructor.name + ")");
continue;
}
let g = desc.get != null;
let s = desc.set != null;
if (g || s)
{
let newDescriptor: PropertyDescriptor = {};
newDescriptor.enumerable = desc.enumerable;
newDescriptor.configurable = desc.configurable;
if (g)
newDescriptor.get = getLoggableFunction(desc.get.bind(self), "Property", "get_" + key)
if (s)
newDescriptor.set = getLoggableFunction(desc.set.bind(self), "Property", "set_" + key)
Object.defineProperty(self, key, newDescriptor);
continue;
}
}
这是我如何做的(注意“TestClass”构造函数中的“autobind&autotrace-call”):
import { autoBind, autoTrace } from "./autobind_autotrace.js";
class TestClass
{
constructor()
{
autoBind(this);
autoTrace(this);
}
get bar(): boolean
{
return this._bar;
}
set bar(value: boolean)
{
this._bar = value;
}
public hello()
{
console.log("hello", "this", this);
}
public world(x, y)
{
console.log("world", "this", this);
}
}
AutoBind/AutoTrace (TypeScript):
export function autoBind(self: any): any
{
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
if (key !== 'constructor')
{
let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
if (!desc.configurable)
{
console.log("AUTOBIND-WARNING: Property \"" + key + "\" not configurable ! (" + self.constructor.name + ")");
continue;
}
let g = desc.get != null;
let s = desc.set != null;
if (g || s)
{
let newGetter = null;
let newSetter = null;
if (g)
newGetter = desc.get.bind(self);
if (s)
newSetter = desc.set.bind(self);
if (newGetter != null && newSetter == null)
{
Object.defineProperty(self, key, {
get: newGetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else if (newSetter != null && newGetter == null)
{
Object.defineProperty(self, key, {
set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else
{
Object.defineProperty(self, key, {
get: newGetter
, set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
continue;
}
}
if (typeof (self[key]) === 'function')
{
let val = self[key];
self[key] = val.bind(self);
}
}
}
return self;
}
export function autoTrace(self: any): any
{
function getLoggableFunction_old(func: any, type: any, name: any)
{
return function (...args:any[])
{
let logText = name + '(';
for (var i = 0; i < args.length; i++)
{
if (i > 0)
{
logText += ', ';
}
logText += args[i];
}
logText += ');';
console.log(type + " " + logText);
return func.apply(self, args);
};
}
function getLoggableFunction(func: any, type: any, name: any)
{
return function (...args: any[])
{
let logText = name + '(';
for (var i = 0; i < args.length; i++)
{
if (i > 0)
{
logText += ', ';
}
logText += args[i];
}
logText += ')';
console.log("Pre " + type + " " + logText + "; ");
let res = func.apply(self, args);
console.log("Post " + type + " " + logText + ":", res);
return res;
};
}
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
if (key !== 'constructor')
{
let desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
if (!desc.configurable)
{
console.log("AUTOTRACE-WARNING: Property \"" + key + "\" not configurable ! (" + self.constructor.name + ")");
continue;
}
let g = desc.get != null;
let s = desc.set != null;
if (g || s)
{
let newGetter = null;
let newSetter = null;
if (g)
newGetter = getLoggableFunction(desc.get.bind(self), "Property", "get_" + key)
if (s)
newSetter = getLoggableFunction(desc.set.bind(self), "Property", "set_" + key)
if (newGetter != null && newSetter == null)
{
Object.defineProperty(self, key, {
get: newGetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else if (newSetter != null && newGetter == null)
{
Object.defineProperty(self, key, {
set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else
{
Object.defineProperty(self, key, {
get: newGetter
, set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
continue;
}
}
if (typeof (self[key]) === 'function')
{
let val = self[key];
self[key] = getLoggableFunction(val.bind(self), "Function", key);
}
}
}
return self;
}
这将转译为普通的JavaScript:
"use strict";
function autoBind(self)
{
for (var _i = 0, _a = Object.getOwnPropertyNames(self.constructor.prototype); _i < _a.length; _i++)
{
var key = _a[_i];
if (key !== 'constructor')
{
var desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
if (!desc.configurable)
{
console.log("AUTOBIND-WARNING: Property \"" + key + "\" not configurable ! (" + self.constructor.name + ")");
continue;
}
var g = desc.get != null;
var s = desc.set != null;
if (g || s)
{
var newGetter = null;
var newSetter = null;
if (g)
newGetter = desc.get.bind(self);
if (s)
newSetter = desc.set.bind(self);
if (newGetter != null && newSetter == null)
{
Object.defineProperty(self, key, {
get: newGetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else if (newSetter != null && newGetter == null)
{
Object.defineProperty(self, key, {
set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else
{
Object.defineProperty(self, key, {
get: newGetter
, set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
continue;
}
}
if (typeof (self[key]) === 'function')
{
var val = self[key];
self[key] = val.bind(self);
}
}
}
return self;
}
function autoTrace(self)
{
function getLoggableFunction_old(func, type, name)
{
return function ()
{
var args = [];
for (var _i = 0; _i < arguments.length; _i++)
{
args[_i] = arguments[_i];
}
var logText = name + '(';
for (var i = 0; i < args.length; i++)
{
if (i > 0)
{
logText += ', ';
}
logText += args[i];
}
logText += ');';
console.log(type + " " + logText);
return func.apply(self, args);
};
}
function getLoggableFunction(func, type, name)
{
return function ()
{
var args = [];
for (var _i = 0; _i < arguments.length; _i++)
{
args[_i] = arguments[_i];
}
var logText = name + '(';
for (var i = 0; i < args.length; i++)
{
if (i > 0)
{
logText += ', ';
}
logText += args[i];
}
logText += ')';
console.log("Pre " + type + " " + logText + "; ");
var res = func.apply(self, args);
console.log("Post " + type + " " + logText + ":", res);
return res;
};
}
for (var _i = 0, _a = Object.getOwnPropertyNames(self.constructor.prototype); _i < _a.length; _i++)
{
var key = _a[_i];
if (key !== 'constructor')
{
var desc = Object.getOwnPropertyDescriptor(self.constructor.prototype, key);
if (desc != null)
{
if (!desc.configurable)
{
console.log("AUTOTRACE-WARNING: Property \"" + key + "\" not configurable ! (" + self.constructor.name + ")");
continue;
}
var g = desc.get != null;
var s = desc.set != null;
if (g || s)
{
var newGetter = null;
var newSetter = null;
if (g)
newGetter = getLoggableFunction(desc.get.bind(self), "Property", "get_" + key);
if (s)
newSetter = getLoggableFunction(desc.set.bind(self), "Property", "set_" + key);
if (newGetter != null && newSetter == null)
{
Object.defineProperty(self, key, {
get: newGetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else if (newSetter != null && newGetter == null)
{
Object.defineProperty(self, key, {
set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
else
{
Object.defineProperty(self, key, {
get: newGetter
, set: newSetter
, enumerable: desc.enumerable
, configurable: desc.configurable
});
}
continue;
}
}
if (typeof (self[key]) === 'function')
{
var val = self[key];
self[key] = getLoggableFunction(val.bind(self), "Function", key);
}
}
}
return self;
}
exports = exports || {};
exports.autoBind = autoBind;
exports.autoTrace = autoTrace;
undefined
(并使其不可枚举),这有点像它不存在一样:Object.defineProperty(x, "bar", {value: undefined});
- T.J. Crowder