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

时间:2015-01-08 10:59:40

标签: javascript oop prototype

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

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

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

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

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

// constructor
function SomeType() {
    SomeType.doSomething();
}

主要问题

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

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

This is my answer到另一个问题,你可以看到我的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);
}

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

2 个答案:

答案 0 :(得分:2)

  

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

实际上,这是不可能的,JS中的一个cannot know是否将用户函数作为构造函数调用。 this instanceof测试对于通常的情况是足够的,但只检查上下文是否继承了类的原型。

  

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

出于同样的原因,您不能在未将instanceof作为参数传递给this的情况下进行doSomething测试。

  

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

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

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();
}

DO

var SomeType = function() {

}.adoptArguments();

答案 1 :(得分:0)

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

  

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

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

// constructor
function SomeType() {
    SomeType.doSomethingWith(this);
}

警告:您的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)