我想这是一个纯粹的香草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)
overriden函数在与原始函数运行的相同的上下文中运行,我不知道为什么全局变量未定义。
原始函数是一个免提的内置编辑器(datePicker)。最重要的想法是我从this thread和this
获得的答案 0 :(得分: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
函数,它将起作用。