Javascript 原型:深度范围的;这";要访问实例';s范围

Javascript 原型:深度范围的;这";要访问实例';s范围,javascript,prototype,Javascript,Prototype,如何缓存最顶层的作用域,以便稍后在原型中更深入地使用,如下所示: 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); } } } va

如何缓存最顶层的作用域,以便稍后在原型中更深入地使用,如下所示:

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"
更新: 现在我想起来了,我可以使用/并传递上下文

game.board.init.apply(game);

没有“原型中的更深层次”这样的东西。“this”将始终是您调用它的对象,除非通过回调或各种重新绑定的方式改变了它。如果您将概念拆分并将其链接在一起,您的理智损失会减少:

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 = {};
或者,如果你下定决心要让它全部使用相同的基础,你可以做一些疯狂的黑客,比如

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)
        }
    }
}

这是一个彻头彻尾的黑客行为,它会让你的同事讨厌你。(如果您使用的是下划线库,则有一个bindAll函数可以帮助您处理此内容)

由于您只有一个
board
对象的实例,因此它无法知道您使用了什么来访问它。使用
game.board
game.prototype.board
访问对象会得到完全相同的结果

如果您不想为每个<代码>游戏实例创建一个“代码>板/<代码>对象,则必须告诉<代码>板<代码>对象,即<代码>游戏<代码>对象,它应该认为它属于每个调用:

game.board.doSomething(game);
或:

编辑: 要为每个
游戏
实例创建一个棋盘,请为
棋盘
创建一个构造函数,并使棋盘对象知道它所属的
游戏
实例:

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"
更新

如果不想为每个函数提供作用域,则必须为每个游戏创建一个新的实例。如果需要,可以通过在
Game
构造函数函数中将棋盘构造函数设置为变量,使棋盘私有化为
Game

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();
不要使用下面的代码,因为正如Guffy指出的,一个棋盘对象在所有游戏实例之间共享。因此,以下解决方案不适用于多个游戏。


您可以强制game.board.init使该引用游戏实例

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();
或者你可以简单地将游戏实例作为棋盘上的一个属性

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();
这样试试

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();
你需要做两件事

  • 在原型方法中创建并设置方法,意味着应在
    a
    中定义
    on
    ,以便它可以访问

  • 在中创建父引用的副本,并在上的中使用它


    • 这样做是一个非常糟糕的主意,因为在某些情况下会导致非常奇怪的行为,但也有可能:

      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());
      
      为什么??我在下面看到了你的答案,试图了解什么是坏案例

      不能通过变量传递
      a

      您必须在获取同一对象的属性
      a
      后立即授予对
      上的
      的访问权限:

      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!
      


      是的,很明显…但我不想改变我的代码,它被编写并拆分为“平面”函数,而不嵌套在名称空间中,这是有意义的,这是丑陋的编程…而且我有大量的内部函数,一直编写
      Game.prototype.something=function(){..}
      会很乱,我只是觉得这不值得我为核心作用域访问所获得的轻松修复。黑客不起作用,因为它错过了
      属性
      的闭包。它应该只使用ES5
      bind
      this.board[p]=this.board[p].bind(this)你说得对,它缺少一个闭包。然而,它不应该“仅仅使用”bind-IE8不支持它,而且它仍然是一个“流行”浏览器。它就像一个静态类,当你在prototype中创建对象时,它在所有实例中都是相同的对象。。它的意思是(newgame()).board==Game.board和(newgame()).board.init==Game.board.init。。这意味着另一个对象内部的“this”是父对象,它在内部init函数中的意思与board相同:在init函数->this==Game.board中你需要多个游戏实例吗?看起来它应该是一个名称空间(以对象文本的形式);然后你们会有板的构造器和原型,以及你们需要的任何东西。是的,我必须有实例。数量未知。我已决定在之后使用“应用”all@vsync:如果使用
      apply
      ,您将作为
      游戏
      实例的成员调用
      init
      方法,而不是作为
      棋盘
      实例的成员调用。然后最好将
      棋盘
      对象的成员放在
      游戏
      对象中开始,并完全抛弃
      棋盘
      对象。
      棋盘
      中有与每个特定实例交互的东西(因为每个游戏都有不同的棋盘),所以直接调用它不是一个好主意。@vsync您将
      board
      添加到
      Game.prototype
      ,所以所有游戏实例共享同一个board。考虑到上面的评论,这可能不是您真正想要的。@vsync:如果您希望每个
      游戏
      实例都有自己的棋盘,您必须更改实现,以便为每个
      游戏
      实例创建一个棋盘。然后董事会可以有一个所有者,并且可以知道它是谁。请看我上面添加的代码。我完全按照我想要的方式设计了它,所有实例都共享了原型。我编写JS已经8年了,我对此并不陌生。但问题是,我在想,也许有一种方法可以通过一些闭包技巧来保存作用域。有时候JS需要更多的创造性,mindsteaks的答案就是我想要的,一种JS技巧,让最里面的原型对象知道最上面的作用域。很好。你是一个很好的开发者,我的朋友,我喜欢这第二部分。我写它是为了更好地匹配,它只适用于
      游戏
      对象的一个实例。@Guffa你说得对。开发者不应该按原样使用这个答案。@Guffa我认为我修改后的解决方案应该适用于多个游戏实例。这两个都不是一个好的解决方案
      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!