Javascript继承与揭示原型设计模式

时间:2016-01-08 16:20:04

标签: javascript inheritance prototype

我试图创建一个简单的javascript继承,但是我错过了一些我需要你的帮助。

基本上,我有User可以拥有用户名。我还有一个继承自用户的玩家,可以得分,然后玩。

var Game = (function () {
  User = function () {
    var username;

    return {
        setUsername: function (newUsername) {
          username = newUserName;
        },
        getUsername: function () {
          return username;
       }
     }
  };

  Player = function () {
    var score = 0;

    return {
      getScore: function () {
        return score;
      },
      play: function () {
        score = Math.round(Math.random()*100);
      }
    };
  };

  Player.prototype = new User();

  return {
    player1: new Player(), 
    player2: new Player()
  };
});

var game = new Game();

game.player1.setUsername('alex');
game.player2.setUsername('tony');

game.player1.play();
game.player2.play();

console.log(game.player1.getUsername()+': '+game.player1.getScore());
console.log(game.player2.getUsername()+': '+game.player2.getScore());

我遇到的问题来自于我无法从我的播放器中的用户访问该方法。我不知道如何才能访问它们。

这是一个jsfiddle:https://jsfiddle.net/3xsu8pdy/

有什么想法吗? 感谢。

感谢。

3 个答案:

答案 0 :(得分:3)

首先,请注意您将newUserPerson一起使用,但是您的代码会通过从这些函数返回不同的对象来抛弃对象new创建的对象。因此new User()User() 完全同样的事情。

这主要是因为您无法访问User中的Person功能,因为返回的对象的原型不是 User.prototype ,它是Object.prototype

如果您不想要new ...

...您想要创建“人”对象,以便他们直接(或通过User)由Object.create(User())支持:

var Game = (function() {
    var User = function() { // <== Note the `var`
        var username;

        return {
            setUsername: function(newUsername) {
                username = newUserName;
            },
            getUsername: function() {
                return username;
            }
        }
    };

    var Player = function() {    // <== Note the `var`
        var score = 0;

        // Create the player, either using a User directly:
        var player = User();
        // ...or by using a User as a prototype:
        var player = Object.create(User());

        player.getScore = function() {
            return score;
        };
        player.play = function() {
            score = Math.round(Math.random() * 100);
        };

        return player;
    };

    return {
        player1: Player(),  // No `new`
        player2: Player()
    };
});

var game = new Game();

game.player1.setUsername('alex');
game.player2.setUsername('tony');

game.player1.play();
game.player2.play();

console.log(game.player1.getUsername() + ': ' + game.player1.getScore());
console.log(game.player2.getUsername() + ': ' + game.player2.getScore());

这使usernamescore属性保持私密,就像在原始代码中一样。

如果您想使用new

...那么你可能想要相当标准的模式I describe in this answer,它看起来像是应用于你的代码:

var Game = (function() {
    var User = function() {
    };
    User.prototype.setUsername = function(newUsername) {
        this.username = newUserName;
    };
    User.prototype.getUsername = function() {
        return this.username;
    };

    var Player = function() {
        this.score = 0;
    };
    Player.prototype = Object.create(User.prototype);
    Player.prototype.constructor = Player;
    Player.prototype.getScore = function() {
        return this.score;
    };
    Player.prototype.play = function() {
        this.score = Math.round(Math.random() * 100);
    };

    return {
        player1: new Player(),
        player2: new Player()
    };
});

var game = new Game();

game.player1.setUsername('alex');
game.player2.setUsername('tony');

game.player1.play();
game.player2.play();

console.log(game.player1.getUsername() + ': ' + game.player1.getScore());
console.log(game.player2.getUsername() + ': ' + game.player2.getScore());

或者在ES2015中:

var Game = (function() {

    class User {
        setUsername(newUsername) {
            this.username = newUserName;
        }
        getUsername() {
            return this.username;
        }
    }

    class Player extends User {
        constructor() {
            this.score = 0;
        }
        getScore() {
            return this.score;
        }
        play() {
            this.score = Math.round(Math.random() * 100);
        }
    }

    return {
        player1: new Player(),
        player2: new Player()
    };
});

var game = new Game();

game.player1.setUsername('alex');
game.player2.setUsername('tony');

game.player1.play();
game.player2.play();

console.log(game.player1.getUsername() + ': ' + game.player1.getScore());
console.log(game.player2.getUsername() + ': ' + game.player2.getScore());

请注意,在后两个示例中,usernamescore不再是私有的。也就是说,即使是私有范围之外的私有变量也可以通过这些语言的反射功能在私有范围之外使用,甚至私有变量也可以在私有范围之外使用。

在ES2015中,我们可以使用WeakMap来获得与原始代码一样好的隐私:

var Game = (function() {
    var UserNames = new WeakMap();

    class User {
        setUsername(newUsername) {
            UserNames.set(this, newUsername);
        }
        getUsername() {
            return UserNames.get(this);
        }
    }

    var PlayerScores = new WeakMap();
    class Player extends User {
        constructor() {
            PlayerScores.set(this, 0);
        }
        getScore() {
            return PlayerScores.get(this);
        }
        play() {
            PlayerScores.set(this, Math.round(Math.random() * 100));
        }
    }

    return {
        player1: new Player(),
        player2: new Player()
    };
});

var game = new Game();

game.player1.setUsername('alex');
game.player2.setUsername('tony');

game.player1.play();
game.player2.play();

console.log(game.player1.getUsername() + ': ' + game.player1.getScore());
console.log(game.player2.getUsername() + ': ' + game.player2.getScore());

这不会导致内存泄漏,因为当UserPerson对象不再被WeakMap引用时,WeakMap会让它可以被垃圾收集。

答案 1 :(得分:0)

试试这个:

User = function () {};

User.prototype.setUsername = function (newUsername) {
  this.username = newUserName;
};

User.prototype.getUsername = function () {
  return this.username;
};

同样适用于玩家:-)

以下代码是此模式的变体。 extend()函数负责将原型绑定在一起。 sup参数引用父原型。虽然,我担心这是老式的,但我不确定走这条路是不是一个好主意。无论如何,我相信它比以前的代码更容易理解。

var A = extend(Object, function (sup) {
  this.init = function (name) {
    this.name = name;
  };
  this.whoami = function () {
    return this.name;
  };
});

var B = extend(A, function (sup) {
  this.whoami = function () {
    return 'I\'m ' + sup.whoami.call(this) + '.';
  };
});

var C = extend(B, function (sup) {
  this.init = function (name) {
    sup.init.call(this, '&star; ' + name + ' &star;');
  };
});

var a = new A('an instance of A');
var b = new B('an instance of B');
var c = new C('an instance of C');

log(
  'a.whoami()',
  'b.whoami()',
  'c.whoami()',
  'b instanceof A',
  'b instanceof B',
  'b instanceof C'
);

function extend (tail, head) {
  function Class () {
    this.init.apply(this, arguments);
  }
  head.prototype = tail.prototype;
  Class.prototype = new head(tail.prototype);
  Class.prototype.constructor = Class;
  return Class;
}
<table><thead><tr><th>code</th><th>result</th></tr></thead><tbody></tbody></table><script>function log(){var el=document.getElementsByTagName('tbody')[0];Array.prototype.slice.call(arguments).forEach(function(x){el.innerHTML+='<tr><td>'+x+'</td><td>'+eval(x)+'</td></tr>';});}</script><style>table,td,th{text-align:left;border:1px solid #333;border-collapse:collapse;padding:.5em;}</style>

答案 2 :(得分:0)

我看到你试图使用揭示模块设计模式的地方但是如果你要继承,那么你应该选择构造函数。类似的东西:

var module = (function () {
    function User () {
        this.username = 'default';
    }
    User.prototype.setUsername = function (username) {
        this.username = username;
    };
    User.prototype.getUsername = function () {
        return this.username;
    };

    function Player () {
        User.call(this);
        this.score = 0;
    }
    Player.prototype = Object.create(User.prototype);
    Player.prototype.getScore = function () {
        return this.score;
    };
    Player.prototype.play = function () {
        this.score = Math.round(Math.random()*100);
    };

    return {
        player1 = new Player(),
        player2 = new Player()
    };
}());

因此我们仍在使用揭示模块模式,但我们也获得了原型继承以及随之而来的所有优化。这就是我的建议。