Reputation: 130075
How can the the top-most scope can be cached in order to be used deeper in the prototype later, like so:
var Game = function(id){
this.id = id;
};
Game.prototype = {
board : {
init: function(){
// obviously "this" isn't the instance itself, but will be "board"
console.log(this.id);
}
}
}
var game = new Game('123');
game.board.init(); // should output "123"
Well now that I think about it, I can use apply
/call
and pass the context...
game.board.init.apply(game);
Upvotes: 6
Views: 410
Reputation: 21380
It's a very bad idea to do so, as it leads to very strange behaviour in some cases, but it's possible:
var Model = function(x) { this.x = x };
Object.defineProperty(Model.prototype, 'a', (function() {
var lastSelf;
function get() { return lastSelf.x }
get.on = function () { return lastSelf.x * 2 };
return { get() { lastSelf=this; return get } };
})());
var m = new Model(17);
console.log(m.a(), m.a.on());
Why? I see your answer below, trying to realize what are bad cases.
You can't pass a
through the variable.
You must grant access to on
immediately after getting property a
of the same object:
var m1 = new Model(1), m2 = new Model(3);
console.log(m1.a(), m2.a(), m1.a.on(), m2.a.on()); // 1 3 2 6 - ok
var a1 = m1.a, a2 = m2.a;
console.log(m1.a(), m2.a(), a1.on(), a2.on()); // 1 3 6 6 - ooops!
console.log(m1.a(), m2.a(), m1.a(), a1.on(), a2.on()); // 1 3 1 2 2 - ooops!
Upvotes: 0
Reputation: 68393
Try this way
var Model = function() {this.b = 2;};
Model.prototype.a = function() {
console.log(this);
var that = this;
this.a.on = function(){
console.log(that); console.log(m.b);
};
}
var m = new Model();
m.a();
m.a.on();
You need to do two things
Create set the method inside the prototype method, means on
should be defined inside a
so that it has access to this
.
Create a copy of the parent reference in that
and use it inside on
Upvotes: 0
Reputation: 5460
There's no such thing as 'deeper in the prototype'. "this" will always be the object that you're calling it on, unless that's changed through being a callback or various ways to rebind. You'll have less sanity loss if you split up your concepts and link them together:
Board = function (game) {
this.game = game;
}
Board.prototype.init = function () {
console.log(this.game.id);
}
Game = function () {
this.id = 123;
this.board = new Board(game);
}
Game.prototype = {};
Alternatively, if you're hellbent on making it all use the same base, you can do some crazy hack like..
Game = function () {
this.id = 123;
var self = this;
for(var p in this.board) {
var property = this.board[p];
if(typeof property == 'function') {
this.board[p] = function (method) {
return function () {
method.apply(self, arguments);
}
}(property)
}
}
}
This is a total hack, and it'll make your coworkers hate you. (If you're using the underscore library, there's a bindAll function that helps with this stuff)
Upvotes: 1
Reputation: 17279
UPDATE
You must make a new instance of for each game if you don't want to provide scope for each function. If you want, you can make board private to Game
by making the board constructor a variable in the Game
constructor function
var Game = function(id){
//Keep board private to Game
var boardClass = function (scope) {
this.scope = scope;
this.init = function () {
if ( this.scope instanceof Game ) {
console.log(this.scope.id);
}
};
};
this.id = id;
//Instantiate a new board for each game
this.board = new boardClass(this);
};
var game = new Game(123);
//Logs 123
game.board.init();
var game2 = new Game(456);
//Logs 456
game2.board.init()
//Still Logs 123
game.board.init();
Don't use the code below because, as Guffy points out, one board object is shared between all instances of Game. So the solutions below do not work for multiple Games.
You could force game.board.init to make this
refer to an instance of game.
var Game = function(id){
this.id = id;
this.board.game = this;
};
Game.prototype = {
board : {
game: {},
init: function() {
//check if this is an instance of board by seeing if it has a game property
if(this.game) {
//make this refer to the game if it is referring to the board
this.init.apply(this.game);
}
console.log(this.id);
}
}
}
var game = new Game(123);
//Logs 123
game.board.init();
var game2 = new Game(456);
//Logs 456
game2.board.init();
//Logs 456...oops!
game.board.init();
Or you could simply make the instance of game a property on the board.
var Game = function(id){
this.id = id;
this.board.scope = this;
};
Game.prototype = {
board : {
init: function(){
// can check if the scope is what we want it to be
if( this.scope instanceof Game )
console.log(this.scope.id);
}
}
}
var game = new Game(123);
//Logs 123
game.board.init();
var game2 = new Game(456);
//Logs 456
game2.board.init();
//Logs 456...oops!
game.board.init();
Upvotes: 0
Reputation: 700222
As you only have one instance of the board
object, there is no way for it to know what you used to access it. Using game.board
or Game.prototype.board
to access the object gives exactly the same result.
If you don't want to create one board
object for each Game
instance, you have to tell the board
object which Game
object it should consider itself to belong to for each call:
game.board.doSomething(game);
or:
Game.prototype.board.doSomething(game);
To create one board for each Game
instance, make a constructor for Board
, and make the board object aware of the Game
instance that it belongs to:
function Game(id) {
this.id = id;
this.board = new Board(this);
}
Game.prototype = {
};
function Board(game) {
this.game = game;
}
Board.prototype = {
init: function(){
console.log(this.game.id);
}
};
var game = new Game('123');
game.board.init(); // outputs "123"
Upvotes: 2