在基于Crockford's的功能继承

Using super methods in Javascript based on Crockford's functional inheritance

本文关键字:功能 继承 Crockford      更新时间:2023-09-26

我一直在阅读Crockford的《好的部分》中关于函数继承的章节。在他给出的哺乳动物例子中,我有点困惑他为什么使用superior方法来修改get_name函数。以下是有问题的例子:

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};
var mammal = function (spec) {
    var that = {};
    that.get_name = function () {
        return spec.name;
    };
    that.says = function () {
        return spec.saying || '';
    };
    return that;
};
var myMammal = mammal({
    name: 'Herb'
});
var cat = function (spec) {
    spec.saying = spec.saying || 'meow';
    var that = mammal(spec);
    that.purr = function (n) {
        var i, s = '';
        for (i = 0; i < n; i += 1) {
            if (s) {
                s += '-';
            }
            s += 'r';
        }
        return s;
    };
    that.get_name = function () {
        return that.says() + ' ' + spec.name + ' ' + that.says();
    };
    return that;
};
Object.method('superior', function (name) {
    var that = this,
        method = that[name];
    return function () {
        return method.apply(that, arguments);
    };
});
var coolcat = function (spec) {
    var that = cat(spec);
    var super_get_name = that.superior('get_name');
    that.get_name = function (n) {
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};
var myCoolCat = coolcat({
    name: 'Bix'
});
var name = myCoolCat.get_name(); // 'like meow Bix meow baby'

我对此感到困惑,因为我可以通过删除superior方法并按如下方式更改coolcat来复制相同的东西:

var coolcat = function(spec) {
    var that = cat(spec);
    var super_get_name = that.get_name();
    that.get_name = function(n) {
        return 'like ' + super_get_name + ' baby';
    };
    return that;
};

所以,我不明白Crockford为什么选择使用superior方法。有人能解释吗?

这里的想法是:

var super_get_name = that.superior('get_name');

使CCD_ 6成为一个函数;每次调用时;调用that原始get_name方法。这允许get_name调用(超类)get_name

现在,如果最初的get_name方法除了返回一个永远不变的值之外,永远不会有任何效果,那么是的,这有点毫无意义;您可以只保存一个从未更改的值,然后在新的get_name中使用它。但是,如果原始的get_name实际上可以做一些事情(例如,运行AJAX请求,或更改HTML元素的样式),或者如果它的返回值可以更改(例如,如果有一些相应的set_name方法),那么代码所做的(保存原始返回值并使用它)和Crockford的代码所做(保存原始方法并调用它)之间会有一个重要的区别。

Crockford书的这一章引起的困惑是,Crockford所描述的是"他"在JavaScript中实现继承的首选模式,该模式依赖于他用Function.prototype.method扩展Function对象(第1.3章),他使用CCD_16向Function对象添加方法。

coolcat示例中解决的问题是需要访问父类型的方法。在Java这样的"经典"OO语言中,这是很自然的,因为类本身就存在。在JavaScript继承是原型的情况下,您创建一个类型为mammal的对象,然后修改该对象以创建类型为CCD19或coolcat

根据您的设计,您可以添加属性和函数或覆盖"继承"的函数。当您重写"继承的"函数时,问题就会出现,在JavaScript中,您基本上用新函数替换旧函数,从而丢失旧函数。

克罗克福德现在需要做两件事:

  1. 得到父母(猫)的get_name方法;以及
  2. 以可在重写方法中使用的方式保存它

在此代码中:

var coolcat = function(spec) {
    var that = cat(spec), 
        super_get_name = that.superior('get_name');
    that.get_name = function(n) {
        return 'like ' + super_get_name() + ' baby';
    };
    return that;
};

他做了1。通过调用superior方法得到一个得到cat的CCD_ 22函数的函数;他做了2。通过将其保存到coolcat函数(/object)内的CCD_ 23变量,允许在被coolcat的get_name函数覆盖(更正确地覆盖)之前访问cat的get_name函数。

在我看来,出现这种混乱是因为:

  1. superior方法的名称很奇怪:"superior"方法只是一个按名称查找的函数方法,可以更好地命名为getFunctionByName(您可以通过将字符串get_name替换为purr来尝试此操作,coolcat的get_name现在将调用purr,只需记住将其调用为super_get_name(10),否则您将得到一个空字符串)
  2. 其次,代码和模式通过依赖一些特定的Crockford模式来混淆代码,如果你试图在没有阅读整本书的情况下深入本章,可能会给你带来压力

我认为有一种更简单的方法可以实现这一点,我认为因为它是完全本地化的,所以更容易理解等等,如下面的代码所示:

var coolcat = function(spec) {
    var that = cat(spec);
    that.parent_get_name = that.get_name;
    that.get_name = function() {
       return 'like ' + this.parent_get_name() + ' baby';
    };
    return that;
};

还有一些其他奇怪的地方,例如coolcat get_name函数定义中的参数n,我只能假设它来自于复制purr函数,这将暗示一个幽灵写入器!

最后,我建议大家在读这本书之前,应该听听他关于"JavaScript的好部分"的演讲。这篇演讲非常精彩,比这本书好多了。