【问题标题】:"Uncaught TypeError: Illegal invocation" in ChromeChrome 中的“未捕获的 TypeError:非法调用”
【发布时间】:2012-03-29 11:55:31
【问题描述】:

当我使用requestAnimationFrame 用下面的代码做一些原生支持的动画时:

var support = {
    animationFrame: window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame
};

support.animationFrame(function() {}); //error

support.animationFrame.call(window, function() {}); //right

直接调用support.animationFrame会给...

未捕获的类型错误:非法调用

在 Chrome 中。为什么?

【问题讨论】:

    标签: javascript html google-chrome typeerror


    【解决方案1】:

    在您的代码中,您将本机方法分配给自定义对象的属性。 当您调用 support.animationFrame(function () {}) 时,它会在当前对象的上下文中执行(即支持)。原生 requestAnimationFrame 函数要正常工作,必须在 window 的上下文中执行。

    所以这里正确的用法是support.animationFrame.call(window, function() {});

    警报也是如此:

    var myObj = {
      myAlert : alert //copying native alert to an object
    };
    
    myObj.myAlert('this is an alert'); //is illegal
    myObj.myAlert.call(window, 'this is an alert'); // executing in context of window 
    

    另一种选择是使用Function.prototype.bind(),它是 ES5 标准的一部分,适用于所有现代浏览器。

    var _raf = window.requestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            window.oRequestAnimationFrame;
    
    var support = {
       animationFrame: _raf ? _raf.bind(window) : null
    };
    

    【讨论】:

    • 从 Chrome 33 开始,第二次调用也因“非法调用”而失败。一旦答案是updated,很高兴删除反对票!
    • @DanDascalescu:我正在使用 chrome 33,它对我有用。
    • 我刚刚复制粘贴了您的代码并收到非法调用错误。 Here's the screencast.
    • 你肯定会得到非法调用错误,因为第一个标签myObj.myAlert('this is an alert');是非法的。正确用法是myObj.myAlert.call(window, 'this is an alert')。请正确阅读答案并尝试理解。
    • 如果我不是唯一一个被困在试图让 console.log.apply 以同样的方式工作的人,“这个”应该是控制台,而不是窗口:stackoverflow.com/questions/8159233/…
    【解决方案2】:

    你也可以使用:

    var obj = {
        alert: alert.bind(window)
    };
    obj.alert('I´m an alert!!');
    

    【讨论】:

    • 这并不能完全回答问题。我认为它应该是评论,而不是答案。
    • 此外,绑定到适当的对象也很重要,例如使用 history.replaceState 时,应使用:var realReplaceState = history.replaceState.bind(history);
    • @DeeY:感谢您回答我的问题!对于未来的人,localStorage.clear 要求您 .bind(localStorage),而不是 .bind(window)
    • 所以,过去是let log = console.loglet create = document.createElement,现在是let log = console.log.bind(console)let create = document.createElement.bind(document)。好的好的好的。
    【解决方案3】:

    当你执行一个方法(即分配给一个对象的函数)时,你可以在其中使用this变量来引用这个对象,例如:

    var obj = {
      someProperty: true,
      someMethod: function() {
        console.log(this.someProperty);
      }
    };
    obj.someMethod(); // logs true

    如果将一个对象的方法分配给另一个对象,则其this 变量指的是新对象,例如:

    var obj = {
      someProperty: true,
      someMethod: function() {
        console.log(this.someProperty);
      }
    };
    
    var anotherObj = {
      someProperty: false,
      someMethod: obj.someMethod
    };
    
    anotherObj.someMethod(); // logs false

    当您将windowrequestAnimationFrame 方法分配给另一个对象时,也会发生同样的事情。诸如 this 的原生函数具有内置保护,可防止在其他上下文中执行它。

    有一个Function.prototype.call() 函数,它允许您在另一个上下文中调用一个函数。您只需将它(将用作上下文的对象)作为第一个参数传递给此方法。例如alert.call({}) 给出TypeError: Illegal invocation。但是,alert.call(window) 工作正常,因为现在 alert 在其原始范围内执行。

    如果您将.call() 与您的对象一起使用:

    support.animationFrame.call(window, function() {});
    

    它工作正常,因为requestAnimationFrame 是在window 范围内执行的,而不是你的对象。

    但是,每次您想调用此方法时都使用.call(),这不是很优雅的解决方案。相反,您可以使用Function.prototype.bind()。它与.call() 具有类似的效果,但它不是调用函数,而是创建一个始终在指定上下文中调用的新函数。例如:

    window.someProperty = true;
    var obj = {
      someProperty: false,
      someMethod: function() {
        console.log(this.someProperty);
      }
    };
    
    var someMethodInWindowContext = obj.someMethod.bind(window);
    someMethodInWindowContext(); // logs true

    Function.prototype.bind() 的唯一缺点是它是 ECMAScript 5 的一部分,is not supported in IE <= 8。还好有a polyfill on MDN

    您可能已经知道,您可以使用.bind()window 的上下文中始终执行requestAnimationFrame。您的代码可能如下所示:

    var support = {
        animationFrame: (window.requestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            window.oRequestAnimationFrame).bind(window)
    };
    

    那么你可以简单地使用support.animationFrame(function() {});

    【讨论】:

      猜你喜欢
      • 2013-07-31
      • 1970-01-01
      • 2016-12-07
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-10
      • 1970-01-01
      相关资源
      最近更新 更多