ES6代理:捕获未定义的函数执行

时间:2018-05-02 01:24:43

标签: javascript ecmascript-6

假设我有以下两个函数作为属性:

const foo = {
  f1: () => {...},
  f2: () => {...},
}

当有人试图执行foo对象上不存在的函数时,我想执行特定操作(例如,抛出自定义错误)。

我尝试使用get proxy,但即使我尝试执行f3,也会引发错误,例如以下代码:

if (foo.f3) {...}

那么如何以foo.f3像往常一样返回undefined的方式编写代理,但foo.f3()会抛出错误?

3 个答案:

答案 0 :(得分:0)

这是一个部分解决方案,受Unmiss的启发。

const handler = {
  get: function(obj, prop) {
    if (prop in obj) {
      return obj[prop];
    } else {
      return () => {
        throw new Error(`Foo.${prop} is undefined`);
      }
    }
  }
};

这样做的问题是,虽然它实现了在实际尝试执行Foo.f3()时仅抛出错误的目标,但由于Foo.f3现在等于匿名函数不返回{ {1}},意味着(据我所知)undefined将始终返回if (Foo.f3) {...}

编辑:正如@paulpro所指出的那样:

  

你绝对不能那样做。 foo.f3要么是未定义的,要么是一些   可以使用自定义逻辑调用;它不可能都是。

我们可以做的最好的事情是使用true陷阱来捕获f3 in foo语句,但这意味着hasif (f3 in foo)现在会有不同的结果,这似乎是一个大红旗。

答案 1 :(得分:0)

这是你的要求吗? https://jsfiddle.net/MasterJames/bhesz1p7/23/

[显然你需要F12你的开发工具来查看控制台输出或根据需要改变]

唯一真正的区别是投掷后返回undefined。它就像没有做任何事情所执行的功能一样,因为它不存在。

我确定根据实际用例有不同的解决方案,但我喜欢这个想法/问题。保持事物更稳定等。

let foo = new Proxy(
    {
    f1: function (val) {
      console.log('  F1 value:' + val);
      return 'called OKAY with', val;
    }
  },
  {
    get: function(obj, prop) {
      console.log("obj:", obj, " prop:", prop);
      if (prop in obj) {
        console.log("Found:", prop);
        return obj[prop];
      }
      else {
        console.log("Did NOT find:", prop);
        throw new Error(`Foo.${prop} is undefined not called returning undefined`);
        return undefined;
      }
    }
  });

console.log("\nFoo Tester started");

console.log(' Does F1 exists', foo.f1 !== undefined);
console.log(' called F1 result:', foo.f1('passed') );

try {
    console.log(' Does F2 exists', foo.f2 !== undefined);
    console.log(' called F2 result:', foo.f2('passed') );
}
catch (err) {
    console.log(' Error calling F2:', err );
}

console.log("Foo Tester finished");

不确定你是否想要尝试捕捉或者不是也取决于你,所以最后检查它是否真实且功能是相同的,这取决于你将如何处理错误

if (foo.f2 && foo.f2.constructor === Function && foo.f2()) console.log("okay!");

您再次调用构建一个safeCall包装器更像是这样或介于两者之间的东西? 可能会打电话给foo' customThrow'如果它存在或者你有什么,那么JS有很多可能性。

答案 2 :(得分:0)

好的,所以我花了一些时间,但我现在有一个解决方案。

我没有完全理解你的问题,我在问题中重新提出了一个问题,让我自己更好地理解这个问题,因为它很复杂。

基本上你想知道它是否被调用,所以你需要在代理中获得的功能得到'是' isCalling'。

解决方案在JS Fiddle中并不干净,因为它至少在这类问题的解决方案中很麻烦。

基本上解决方案是一个句子是,"你必须使用一个错误来获得一个堆栈跟踪然后回溯调用的源代码并寻找一个右括号。",以确定它是如何被召唤并返回你想要的任何东西。)

[请注意,这取决于您的代码以及您如何调用它,以便根据需要进行调整。]

因为如果没有内联脚本标记,你必须在源代码中找到被调用的位置,就像这个JSFiddle示例中的情况一样。当arguments.callee.caller.toString()在实际的JS文件中更好时,我使用outerHTML来获取源代码。你也不会在这里通过奇怪的行为来偏离堆栈跟踪的位置,所以使用普通的JS文件时,建议使用其他解决方案正确对齐代码。如果有人知道每次使用脚本标记块等如何获得与错误跟踪一致的干净源。另请注意即将到来但不存在的内容是Error.lineNumber。

[请不要为版本历史感到烦恼,将这个版本排除在外是一场噩梦。再次,您最好使用其他npm包来从堆栈跟踪部件执行源代码。]

无论如何,我相信这个例子可以实现你想要的,但原则上可以证明你在给定的真实(无小提琴)情况下需要做得更好。我非常确定这样做不是生产中的一个很好的解决方案,我也没有测试时间(性能速度),但是它确实对你的事业非常重要(没有其他更好的解决方案,我怀疑)那么它会起作用。

最初我在做一些实验时发现了这种技术,而不仅仅是发送另一个参数,我正在检查实际调用它的是什么,并根据功能调整功能。

当你开始更多地思考它时,用法很广泛,正如我去年第一次做这样的事情时所做的那样。示例是作为额外的函数执行安全检查,实时神秘bug调试解决方案,一种以不同方式执行函数而不传递更多参数的方法,失控的递归循环(堆栈有多长),仅举几例。

https://jsfiddle.net/MasterJames/bhesz1p7/90/

let foo = new Proxy(
    {
    f1: function (val) {
      console.log('  F1 value:' + val);
      return 'called OKAY with', val;
    }
  },
  {
    isCalling: function() {
      let stk = new Error();
      let sFrms = this.stkFrms(stk.stack);
      console.log("stkFrms:", sFrms);

      //BETTER From real pure JS Source
      //let srcCod = arguments.callee.caller.toString()

      let srcCod = document.getElementsByTagName('html')[0].outerHTML.split("\n");
      let cItm = sFrms[(sFrms.length - 1)];
      if(cItm !== undefined) {
        let cRow = (parseInt(cItm[1]) - 3);
        let cCol = (parseInt(cItm[2]) + 1);
        let cLine = srcCod[cRow];
        let cCod = cLine.substr(cCol, 1);
        if(cCod === '(') return true;
      }
      return false;
    },
    stkFrms: function (stk) {
      let frmRegex1 = /^.*at.*\(.*\:([0-9]*)\:([0-9]*)\)$/;
      let frmRegex2 = new RegExp(frmRegex1.source, 'gm');
      let res = [], prc, mtch, frms = stk.match(frmRegex2);
      for(mtch of frms) {
        prc = frmRegex1.exec(mtch);
        res.push(prc);
      }
      return res;
    },
    get: function(obj, prop) {
      if (prop in obj) {
        console.log("Found:", prop);
        return obj[prop];
      }
      else {
        if(this.isCalling() === false) {
          console.log("Did NOT find:", prop);
          return undefined;
        }
        else {
          console.log("Did NOT find return custom throw function:", prop);
          return function() {throw new Error(`Foo.${prop} is undefined`);}
        }
      }
    }
  });

console.log("foo.f1:", foo.f1);
console.log("foo.f1('passed'):", foo.f1('passed'));

console.log("foo.f2:", foo.f2);
try {
    console.log("foo.f2('passed2'):", foo.f2('passed2'));
}
catch(err) {
    console.log("foo.f2('passed2') FAILED:", err);
}
console.log("'f2' in foo:", 'f2' in foo);

好的,所以口头上说:

你想检查foo.f2是未定义的,所以它会返回它,因为它没有被调用。

如果您确实调用它(f2)而不是简单地先检查并根据需要进行错误,并且您不想尝试捕获根据函数名称抛出自定义错误,您希望它返回实际值将抛出自定义错误的函数。

你也想在'中使用'看到它是未定义的,这与false相同(也可能通过类似isCallingFromIn的方式进一步发送false而不是undefined。

我错过了什么吗?这不是你们认为不可能的吗?