Javascript Duck类型的示例?
一些程序员建议不要在Javascript中使用伪经典继承,但建议使用duck类型并为每个对象提供一组功能 有没有一个很好的例子说明如何做到这一点?下面我有一个例子,但它一次只分配一个函数。我们是否可以为一个对象指定一整套方法,例如我们是否可以设置一个能“游泳”、“潜水”和“上升”的Javascript Duck类型的示例?,javascript,Javascript,一些程序员建议不要在Javascript中使用伪经典继承,但建议使用duck类型并为每个对象提供一组功能 有没有一个很好的例子说明如何做到这一点?下面我有一个例子,但它一次只分配一个函数。我们是否可以为一个对象指定一整套方法,例如我们是否可以设置一个能“游泳”、“潜水”和“上升”的海洋动物原型,一个能“跑”、“走”和“跳”的陆地动物原型,并让一个对象继承其中一个或两个?(因此鱼对象可以继承或获得海洋动物的功能,海龟可以同时获得海洋动物和陆地动物?) 输出: My name is Yoyo , I
海洋动物原型,一个能“跑”、“走”和“跳”的陆地动物原型,并让一个对象继承其中一个或两个?(因此鱼对象可以继承或获得海洋动物
的功能,海龟可以同时获得海洋动物
和陆地动物
?)
输出:
My name is Yoyo , I am a turtle and I just swam 10 feet
My name is Dolphy , I am a dolphin and I just swam 80 feet
My name is Simba , I am a lion and I just ran 200 feet
My name is Yoyo , I am a turtle and I just ran 2 feet
My name is Yoyo , I am a turtle and I just ran 1 feet
false
true
false
true
true
false
My name is Dolphy , I am a dolphin and I just swam 10 feet
所以你有两个功能:
function make ( props ) {
var obj = Object.create( {} );
Object.keys( props ).forEach(function ( key ) {
obj[ key ] = props[ key ];
});
return obj;
};
function addMethods ( obj, methods ) {
var proto = Object.getPrototypeOf( obj );
Object.keys( methods ).forEach(function ( key ) {
proto[ key ] = methods[ key ];
});
}
用法:
var simba = make({
name: "Simba",
type: "lion"
});
addMethods( simba, {
swim: function () {},
run: function () {}
});
addMethods( simba, {
hunt: function () {},
kill: function () {}
});
实时演示:与其尝试子类化,为什么不使用duck类型计划来扩展组件和依赖项注入
var Duck = function (flyable, movable, speakable) {
this.speak = speakable.speak;
this.fly = flyable.fly;
this.position = movable.position;
flyable.subscribe("takeoff", movable.leaveGround);
flyable.subscribe("land", movable.hitGround);
}
var duck = new Duck(new BirdFlier(), new BirdWalker(), new Quacker());
var plane = new Duck(new VehicleFlier(), new Drivable(), new Radio());
duck.speak(); // "quack"
plane.speak(); // "Roger"
请注意,plane
正在愉快地使用与duck
相同的构造函数
它们甚至一开始就不需要构造函数。
一个工厂也能工作
DuckType的要点是,对象构造不是使用对象的程序所关心的
只要它具有相同的方法/属性名称,程序就会使用它们,而不管子/超级/静态继承如何
编辑:添加了示例组件
这里有几个不同的想法,我将它们结合在一起。
所以一次一个点:
首先,duck类型的基本前提是:
// the basic nature of duck-typing
var sheriff = {
gun : {
aim : function () { /* point gun somewhere */ },
bullets : 6,
fire : function () {
if (this.bullets === 0) { return; }
this.bullets -= 1;
/* ... et cetera */
}
},
draw : function () {
this.gun.aim();
this.gun.fire();
}
},
cartoonist = {
pen : { scribble : function () { /* ... */ } },
draw : function () { this.pen.scribble(); }
},
graphicsCard = {
pixels : [ /* ... */ ],
screen : { /* ... */ },
draw : function () {
pixels.forEach(function (pixel) { screen.draw(pixel); });
}
};
// duck-typing at its finest:
sheriff.draw();
cartoonist.draw();
graphicsCard.draw();
目标是编写不必检查对象类型的函数:
function duckDraw (array) { array.forEach(function (obj) { obj.draw(); }); }
duckDraw([ sheriff, cartoonist, graphicsCard ]);
因此,如果你有海龟、海豚、鲸鱼、潜艇和小鱼,你的程序就不必关心它们游泳方式的差异。它只关心他们都能.swime()代码>。每个项目都会担心自己,以及它需要做什么的特殊方式
鸭子打字
接下来是依赖注入。
在某种程度上,依赖项注入也使用duck类型,但是在类的内部,而不是外部(或者如果像我在上面做的那样,它从内部开始,然后也允许在外部使用duck类型)
你可以这样想:
与其让一个人继承某些东西,不如把它交给他们
如果你有一名士兵
、一名狙击手
一架飞机
和一辆坦克
,每个人都需要一支枪
。
而不是试图子类化,以便他们都可以开火。。。
…为什么不制造不同种类的枪来满足你的需要,并把他们需要的东西都交给他们
var Rifle = function () {
this.reload = function () {};
this.fire = function () { /* ... */ };
},
SniperRifle = function () {
this.reload = function () {};
this.fire = function () {};
},
MachineGun = function () {
this.reload = function () {};
this.fire = function () {};
},
Cannon = function () {
this.reload = function () {};
this.fire = function () {};
};
所以现在我们有不同种类的枪。。。
您可能会认为,因为它们有相同的函数名,并且它们都处理项目符号,所以您可以尝试子类化。。。但是你不需要——这些枪在开火或重新装弹时都不会做同样的事情。。。因此,您最终会用其他语言将重写
写入抽象虚拟
方法/属性,这将是无用的
现在我们有了它们,我们可以看到如何“注入”它们,以及这对我们有什么作用:
var Soldier = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var Sniper = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var Plane = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var Tank = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var soldier = new Soldier( new Rifle() ),
sniper = new Sniper( new SniperRifle() ),
plane = new Plane( new MachineGun() ),
tank = new Tank( new Cannon() );
现在我们有了这些课程,他们称自己的枪——他们不在乎什么样的枪,它只是起作用,因为枪知道枪的工作原理,战士知道如何开枪,程序知道如何告诉战士开枪
但是如果你再仔细看看,每个战士的内部代码现在是100%相同的
那么,为什么不让一名“战斗人员”提供专门的组件呢
var Combatant = function (gun) {
this.currentGun = gun;
this.inventory = {
guns : [ gun ]
};
this.attack = function () { this.currentGun.fire(); };
};
var soldier = new Combatant( new Rifle() );
因此,构造器的内部是duck typegun
,如果你有不同的战斗员类,每个类都有fire
方法,那么你也可以在游戏逻辑中duck键入你的单位
最终,构造器将只持有模块:一个模块处理射击,一个处理地面移动,一个用于绘图,一个用于玩家控制,等等。。。
建造师不需要做任何事情,只需要让这些部件彼此接触,你可以通过给他们不同种类的枪,不同种类的运动,不同种类的健康,使他们变得特别,这些在内部运作不同,但是具有相同的属性和方法名供公共访问。JavaScript中的函数用途广泛。它们可以用作、、等等
人们反对在JavaScript中使用伪经典继承的原因是因为它隐藏了JavaScript的真正功能。函数即使不比对象更具表现力,也同样具有表现力。世卫组织的工作证明了这一点
为了直接回答您的问题,我将使用函数创建海龟
、狮子
和海豚
。然后我将演示海龟如何是海洋动物
和陆地动物
,狮子如何只是陆地动物
,海豚如何只是海洋动物
。最后,我将解释什么是duck类型
首先,让我们为OceanAnimal
创建构造函数:
function OceanAnimal() {
this.swim = function (n) {
return "I am " + this.name + ", the " + this.type +
", and I just swam " + n + " meters.";
};
}
function LandAnimal() {
this.walk = function (n) {
return "I am " + this.name + ", the " + this.type +
", and I just walked " + n + " meters.";
};
}
function isOceanAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.swim !== "function") return false;
return true;
}
function isLandAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.walk !== "function") return false;
return true;
}
接下来,我们将为陆地动物创建构造函数:
function OceanAnimal() {
this.swim = function (n) {
return "I am " + this.name + ", the " + this.type +
", and I just swam " + n + " meters.";
};
}
function LandAnimal() {
this.walk = function (n) {
return "I am " + this.name + ", the " + this.type +
", and I just walked " + n + " meters.";
};
}
function isOceanAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.swim !== "function") return false;
return true;
}
function isLandAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.walk !== "function") return false;
return true;
}
好的。现在让我们为海龟
创建构造函数:
Turtle.prototype.type = "turtle";
function Turtle(name) {
this.name = name;
LandAnimal.call(this);
OceanAnimal.call(this);
}
这里发生了什么事?好的,我们希望海龟
继承海洋动物
和陆地动物
。所以我们正在调用陆地动物。调用(这个)
和海洋动物。调用(这个)
。通过这种方式,我们将海洋动物
和陆地动物
构造函数用作。因此,海龟
既继承了海洋动物
又继承了陆地动物
,而实际上没有成为海洋动物
或陆地动物
的类型
还有一件事是不可能的
function isLandAnimal(object) {
if (typeof object !== "object") return false;
if (typeof object.walk !== "function") return false;
return true;
}