【问题标题】:can't get function's global execution context variable in runtime在运行时无法获取函数的全局执行上下文变量
【发布时间】:2018-08-29 06:47:36
【问题描述】:

我猜这是纯香草的 javascript 问题。

我需要重写一个函数 showDatepicker,我是这样做的:

            const Base = (Handsontable.editors.DateEditor as any).prototype as any;
            const DateEditorHelper = Base.extend();

            DateEditorHelper.prototype.showDatepicker = function (event: any[]) {                    
                Base.showDatepicker.apply(this, event)

                this.$datePicker.config(this.getDatePickerConfig());

                var offset = this.TD.getBoundingClientRect();
                var dateFormat = this.cellProperties.dateFormat || this.defaultDateFormat;
                var datePickerConfig = this.$datePicker.config();
                var dateStr = void 0;
                var isMouseDown = this.instance.view.isMouseDown();
                var isMeta = event ? (0, _unicode.isMetaKey)(event.keyCode) : false;

                this.datePickerStyle.top = offset.top >= $(window).height() - 224 ? window.pageYOffset + offset.top - 224 + (0, _element.outerHeight)(this.TD) + 'px' : window.pageYOffset + offset.top + (0, _element.outerHeight)(this.TD) + 'px';
                this.datePickerStyle.left = window.pageXOffset + offset.left + 'px';

                this.$datePicker._onInputFocus = function () { };
                datePickerConfig.format = dateFormat;

                if (this.originalValue) {
                    dateStr = this.originalValue;

                    if ((0, _moment2.default)(dateStr, dateFormat, true).isValid()) {
                        this.$datePicker.setMoment((0, _moment2.default)(dateStr, dateFormat), true);
                    }

                    // workaround for date/time cells - pikaday resets the cell value to 12:00 AM by default, this will overwrite the value.
                    if (this.getValue() !== this.originalValue) {
                        this.setValue(this.originalValue);
                    }

                    if (!isMeta && !isMouseDown) {
                        this.setValue('');
                    }
                } else if (this.cellProperties.defaultDate) {
                    dateStr = this.cellProperties.defaultDate;

                    datePickerConfig.defaultDate = dateStr;

                    if ((0, _moment2.default)(dateStr, dateFormat, true).isValid()) {
                        this.$datePicker.setMoment((0, _moment2.default)(dateStr, dateFormat), true);
                    }

                    if (!isMeta && !isMouseDown) {
                        this.setValue('');
                    }
                } else {
                    // if a default date is not defined, set a soft-default-date: display the current day and month in the
                    // datepicker, but don't fill the editor input
                    this.$datePicker.gotoToday();
                }

                this.datePickerStyle.display = 'block';
                this.$datePicker.show();
            }

这实际上是原始函数的代码,我在其中做了一些小的修改。 我认为函数的内部代码变量将在运行时进行评估(我不是在谈论内部函数范围变量作为其参数或在其中声明的变量,而是关于执行上下文的全局变量)。 在函数执行之前完全没有错误(当浏览器读取函数时,它不会抱怨某些变量未定义)但是当它运行时,我得到了这个错误:

Uncaught ReferenceError: _unicode is not defined
    at eval (eval at DateEditorHelper.showDatepicker (module.js?v=64fd5db96274:40281), <anonymous>:1:1)
    at e.DateEditorHelper.showDatepicker (module.js?v=64fd5db96274:40281)

被覆盖的函数在与原始函数应该运行的相同上下文中运行,我不知道为什么全局变量未定义。

原始功能是一个可操作的内置编辑器(datePicker)。我从this threadthis 得到的最重要的想法

【问题讨论】:

    标签: javascript handsontable


    【解决方案1】:

    您说您从原始函数中复制了代码并进行了细微更改。问题似乎是您使用了一个名为_unicode 的变量。你在定义那个变量吗?

    当用修改后的“原始”代码替换方法的代码时,您必须确保旧代码引用的任何其他变量/函数也被复制。

    您必须考虑到原始方法是在另一个上下文中定义的。可能作者的意思不是要派生这个类,或者重写它的方法。

    模块(或 IIFE)的优势在于您可以定义公共 API,同时封装实现的私有部分。当然,这意味着重写方法或函数要困难得多。

    在这种情况下,变量_unicode 显然是私有实现的一部分。它的名称遵循以下划线开头的约定这一事实几乎证明了这一点。它可能与DatePickerHelper 定义在同一个模块中,或者从另一个模块中导入,但无论如何我几乎可以肯定它没有导出,因此您无法访问它并且您的代码会失败。

    为了使您的覆盖生效,您必须更改代码以避免使用该_unicode 变量,或者按照与原始代码中相同的方式自行定义它。当然,取决于它的定义方式,您最终可能不得不“复制”大量代码,但这是任何尝试从外部库覆盖方法的 JavaScript 程序员都会遇到的问题。

    让我们看一个“私有实现”的示例:

    (function() {
      // this is a function I want to keep private.
      function giveMeHello() {
        return 'hello, ';
      }
      
      // this is the class I want to share
      class Greeting {
        greet(name) {
          console.log(giveMeHello() + name);
        }
      }
      
      window.Greeting = Greeting;
    })();
    
    const greet1 = new Greeting();
    greet1.greet('Oscar');  // outputs 'hello, Oscar'.
    
    // As a consumer of Greeting, I don't like the way if greets people. I want the name to be in uppercase
    // implementation is practically identical to original code
    Greeting.prototype.greet = function() {
      console.log(giveMeHello() + name.toUpperCase());
    };
    
    // Let's use it!
    greet1.greet('John'); // Oh! Error! giveMeHello is not defined!

    如你所见,在这个例子中我做的和你一样。但是我的实现和原来的一样依赖于giveMeHello 函数。但giveMeHello 不是全局或公共函数。我无法从我覆盖该功能的位置访问它。因此,当我执行该方法时,它会失败。

    如果现在我只是在覆盖该方法之前复制giveMeHello 函数,它将起作用。

    你现在明白了吗?如果你不这样做,我建议你阅读更多关于 JavaScript 范围的内容,这超出了 StackOverflow 的目的。

    【讨论】:

    • 唯一修改的行是第 16 行 (this.datePickerStyle.top = offset.top >= $(window).height() - 224 ? window.pageYOffset + offset.top - 224 + (0 , _element.outerHeight)(this.TD) + 'px' : window.pageYOffset + offset.top + (0, _element.outerHeight)(this.TD) + 'px';) 其他都和原代码一样. _unicode 我没有定义它 myslef
    • 我添加了有关该问题的更多信息。您没有定义 _unicode 的事实正是问题所在
    • 是的 _unicode 应该已经在全局执行上下文/范围中定义为原始代码使用它
    • 显然不是。我假设您没有在重新定义原始方法的同一位置重新定义该方法。我会举一个例子让你理解这一点
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多