高级Javascript:检测当前函数是否由构造函数调用

Advanced Javascript: Detecting whether current function is being called by a constructor

本文关键字:是否 函数调用 函数 Javascript 检测 高级      更新时间:2023-09-26

在Javascript函数中,这是一个相当简单的检测,该函数是简单地执行还是作为对象实例构造函数执行(使用new关键字)。

// constructor
function SomeType() {
    if (this instanceof SomeType)
        // called as an object instance constructor
    else
        // the usual function call
}

没关系,这在SO之前至少已经回答过几次了。

因此,

现在让我们假设我们的构造函数调用我直接在函数原型上定义的另一个函数,因此所有函数都可以访问 - 这是我这样做的主要目的。

Function.prototype.doSomething = function doSomething() {
    // what code here?
};
// constructor
function SomeType() {
    SomeType.doSomething();
}

主要问题

我们现在如何在doSomething内检测SomeType函数的相同情况?

我想检测它的原因是我正在编写一个函数,该函数采用/注入构造函数参数作为具有相同名称的构造对象实例成员。当然,此函数应仅在由构造函数调用时才执行,而不是由定期调用的函数调用。

这是我对另一个问题的回答,您可以在其中看到我的adoptArguments函数,该函数将对象构造函数参数作为成员放入构造的对象实例中。

强制使用特定用法 = 错误的解决方法

有一个我不想使用的可能的解决方法,因为它强制执行正确的用法 - 执行上下文注入。以下是可以检测对象实例构造函数执行的代码:

Function.prototype.doSomething = function doSomething() {
    if (this instanceof doSomething.caller)
    {
        // object instance construction
    }
    else return; // nope, just normal function call
};
// constructor
function SomeType() {
    // required use of ".call" or ".apply"
    SomeType.doSomething.call(this);
}

这个想法可能会激发你自己的一些想法来解决最初的问题

在 Javascript 函数中,检测函数是简单地执行还是作为对象实例构造函数(使用 new 关键字)执行,这是一个相当简单的检测。

实际上,这是不可能的,在JS中无法知道用户函数是否作为构造函数调用。对于通常的情况,this instanceof测试就足够了,但只检查上下文是否继承自类的原型。

我们现在如何在doSomething内检测到SomeType函数的相同内容?

出于同样的原因,您不能,并且如果不将this作为参数传递给doSomething,则无法进行instanceof测试。

主要问题:我正在编写一个函数,该函数采用/注入构造函数参数作为具有相同名称的构造对象实例成员。

我建议不要通过构造函数内部的函数调用来执行此操作。相反,请尝试修饰构造函数,以便您可以立即访问所需的所有值:

Function.prototype.adoptArguments = function() {
    var init = this;
    var args = arguments.length ? arguments : init.toString().replace(comments, "").match(argumentsparser);
    if (!args || !args.length) return init;
    var constructor = function() {
        if (this instanceof constructor) {
            for (var i=0; i<args.length; i++)
                this[args[i]] = arguments[i];
            init.apply(this, arguments);
        } else {
            // throw new Error("must be invoked with new");
        }
    };
    return constructor;
};

然后代替

function SomeType() {
    SomeType.adoptArguments();
}

var SomeType = function() {
}.adoptArguments();

一种可能的解决方案是将构造函数上下文作为参数传递。没有必要传入参数对象,因为它可以通过this.arguments访问,就像您在链接答案adoptArguments所做的那样。

这个解决方案对我来说很有意义,因为我希望Function.prototype.someMethodFunction实例的上下文中调用,而不是在其他上下文中调用 (即新创建的实例)。

Function.prototype.doSomethingWith = function doSomethingWith(instance) {
    if( instance instanceof this ) // proceed
};
// constructor
function SomeType() {
    SomeType.doSomethingWith(this);
}

WARN:您的adoptArguments函数存在严重错误,请参见下文

var comments = /(('/'/.*$)|('/'*['s'S]*?'*'/))/mg;
var parser = /^function[^'(]*'(([^)]*)')/mi;
var splitter = /'s*,'s*/mi;
Function.prototype.adoptArguments = function() {
    var args;
    // remove code comments
    args = this.toString().replace(comments, "");
    // parse function string for arguments
    args = parser.exec(args)[1];
    if (!args) return; // empty => no arguments
    // get individual argument names
    args = args.split(splitter);
    // adopt all as own prototype members
    for(var i = 0, len = args.length; i < len; ++i)
    {
        this.prototype[args[i]] = this.arguments[i];
    }
};
console.log('the problem with your implementation:');
console.log('> adopting arguments as prototype members');
console.log('> implies you override values for every instance of YourType');
function YourType(a, b, c) {
  YourType.adoptArguments();
}
var foo = new YourType( 1, 2, 3 );
console.log( 'foo', foo.a, foo.b, foo.c ); // foo 1 2 3
var bar = new YourType( 4, 5, 6 );
console.log( 'foo', foo.a, foo.b, foo.c ); // foo 4 5 6
console.log( 'bar', bar.a, bar.b, bar.c ); // bar 4 5 6
console.log();
console.log('also, a trim is need because:');
function OtherType( a, b, c ) { // see where whitespaces are
  OtherType.adoptArguments();
}
var baz = new OtherType( 1, 2, 3 );
console.log( 'baz', baz.a, baz.b, baz.c );
// baz undefined 2 undefined
//
// My solution
//
console.log();
console.log('results');
// slighly modified from your adoptArguments function
Function.prototype.injectParamsOn = function injectParamsOn( instance ) {
  // you may check `instance` to be instanceof this
  if( ! (instance instanceof this) ) return;
  // proceed with injection
  var args;
  // remove code comments
  args = this.toString().replace(comments, "");
  // parse function string for arguments
  args = parser.exec(args)[1];
  if (!args) return; // empty => no arguments
  // get individual argument names (note the trim)
  args = args.trim().split(splitter);
  // adopt all as instance members
  var n = 0;
  while( args.length ) instance[ args.shift() ] = this.arguments[ n++ ];
};
function MyType( a, b, c ){
  MyType.injectParamsOn( this );
}
var one = new MyType( 1, 2, 3 );
console.log( 'one', one.a, one.b, one.c ); // one 1 2 3
var two = new MyType( 4, 5, 6 );
console.log( 'one', one.a, one.b, one.c ); // one 1 2 3
console.log( 'two', two.a, two.b, two.c ); // two 4 5 6
var bad = MyType( 7, 8, 8 );
// this will throw as `bad` is undefined
// console.log( 'bad', bad.a, bad.b, bad.c );
console.log( global.a, global.b, global.c );
// all undefined, as expected (the reason for instanceof check)
相关文章: