Javascript 在基于Crockford';功能遗传

Javascript 在基于Crockford';功能遗传,javascript,inheritance,Javascript,Inheritance,我一直在读Crockford的《好的部分》中关于功能继承的章节。在他给出的哺乳动物示例中,我有点困惑,他为什么使用superior方法修改get\u name函数。下面是一个有问题的例子: Function.prototype.method = function (name, func) { this.prototype[name] = func; return this; }; var mammal = function (spec) { var that = {};

我一直在读Crockford的《好的部分》中关于功能继承的章节。在他给出的哺乳动物示例中,我有点困惑,他为什么使用
superior
方法修改
get\u 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'

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

这里的想法是:

var super_get_name = that.superior('get_name');
使
super\u get\u name
成为一个函数,该函数每次被调用时都会调用
的原始
get\u name
方法。这允许新的
get\u name
调用旧的(超类)
get\u name


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

Crockford书的这一章引起的混淆是因为Crockford描述的是“他的”在JavaScript中实现继承的首选模式,这依赖于他使用
Function.prototype.method
(第1.3章)扩展
Function
对象,他使用该方法向Function对象添加方法

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

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

Crockford现在需要做两件事:

  • 获取父对象(cat)的
    get\u name
    方法;以及
  • 以可以从重写的方法中使用的方式保存它
  • 在此代码中:

    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.通过调用上级方法获取一个函数,该函数获取cat的
    get\u name
    函数; 他这样做了2.将其保存到coolcat函数(/object)中的
    super\u get\u name
    变量中,允许在被coolcat的
    get\u name
    函数覆盖(更正确地覆盖)之前访问cat的
    get\u name
    函数

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

  • superior
    方法的命名很奇怪:“superior”方法只是一个函数名查找方法,可以更好地命名,例如
    getFunctionByName
    (您可以尝试通过替换字符串
    get\u name
    ,用
    purr
    ,coolcat的get\u name现在将调用purr,记住将其命名为
    super\u get\u name(10)
    ,否则您将得到一个空字符串)
  • 其次,代码和模式通过依赖某些特定的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的好部分”的演讲。这篇演讲绝对精彩,比这本书要好得多。

    哇,Crockford真的建议这样做吗?这是一种误导性的、不太易读的方法来获取超类方法调用。避免使用真正的语言功能原型继承很难,然后通过原型化到
    对象
    来破坏它?哎哟。@bobince:那代码不是原型化到
    对象
    ;而是原型化到
    函数
    ,这使得它在
    对象
    上可用(因为
    对象
    是一个构造函数)。(尽管我必须承认,我也对这个建议感到非常惊讶。似乎写
    var super\u get\u name=that.get\u name
    会更清晰。这将迫使新的
    get\u name
    super\u get\u name。应用(那)
    而不仅仅是
    super\u get\u name()
    ,但这对我来说似乎没什么大不了的。)@ruakh:对
    method()
    的调用是将成员
    superior
    添加到
    对象上。prototype
    @bobince:哦,是的,你说得对。好吧,是的,那太糟糕了。:-p感谢你的回答。我仍然有点困惑。如果我写
    var super\u get\u name=that.get\u name()
    我不会得到最新的返回值吗?那么,为什么新的
    get\u name
    可以调用旧的
    get\u name
    ?@StephenYoung:如果你写
    var super\u get\u name=that.get\u name()
    ,那么旧的
    get\u name()就重要了
    将在最初创建
    coolcat
    时被调用,而在以后调用
    mycolcat.get\u name()
    时不会被调用
    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;
    };