Javascript 在基于Crockford';功能遗传
我一直在读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 = {};
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;
};