Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/460.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Duck类型的示例?_Javascript - Fatal编程技术网

Javascript Duck类型的示例?

Javascript Duck类型的示例?,javascript,Javascript,一些程序员建议不要在Javascript中使用伪经典继承,但建议使用duck类型并为每个对象提供一组功能 有没有一个很好的例子说明如何做到这一点?下面我有一个例子,但它一次只分配一个函数。我们是否可以为一个对象指定一整套方法,例如我们是否可以设置一个能“游泳”、“潜水”和“上升”的海洋动物原型,一个能“跑”、“走”和“跳”的陆地动物原型,并让一个对象继承其中一个或两个?(因此鱼对象可以继承或获得海洋动物的功能,海龟可以同时获得海洋动物和陆地动物?) 输出: My name is Yoyo , I

一些程序员建议不要在Javascript中使用伪经典继承,但建议使用duck类型并为每个对象提供一组功能

有没有一个很好的例子说明如何做到这一点?下面我有一个例子,但它一次只分配一个函数。我们是否可以为一个对象指定一整套方法,例如我们是否可以设置一个能“游泳”、“潜水”和“上升”的
海洋动物原型,一个能“跑”、“走”和“跳”的
陆地动物原型,并让一个对象继承其中一个或两个?(因此鱼对象可以继承或获得
海洋动物
的功能,海龟可以同时获得
海洋动物
陆地动物
?)

输出:

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 type
gun
,如果你有不同的战斗员类,每个类都有
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;
}