【问题标题】:prototype: deep scope of "this" to access instance's scope原型:“this”的深层范围以访问实例的范围
【发布时间】:2013-05-11 22:54:01
【问题描述】:

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

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"

更新:

现在想想,我可以使用apply/call 并传递上下文...

game.board.init.apply(game);

【问题讨论】:

  • 它就像一个静态类,当你在原型中创建对象时,所有实例中的对象都是相同的。它的意思是 (new Game()).board == Game.board(new Game()).board.init == Game.board.init .. 表示对象内部另一个对象的“this”是父对象,它的意思是“this”在内部初始化函数与板子相同:在初始化函数中 -> this == Game.board
  • 您需要多个 Game 实例吗?看起来它应该是一个命名空间(以对象文字的形式);那么你就有了 Board 的构造函数和原型,以及任何你需要的东西。
  • 是的,我必须有实例。数量不详。
  • 毕竟我决定使用“应用”
  • @vsync:如果您使用apply,您将调用init 方法作为game 实例的成员,而不是作为board 实例的成员。那么最好将board对象的成员放在Game对象中,然后完全抛弃board对象。

标签: javascript prototype


【解决方案1】:

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

如果您不想为每个Game 实例创建一个board 对象,则必须告诉board 对象在每次调用时它应该认为自己属于哪个Game 对象:

game.board.doSomething(game);

或:

Game.prototype.board.doSomething(game);

编辑:

要为每个Game 实例创建一个板,为Board 创建一个构造函数,并使板对象知道它所属的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"

【讨论】:

  • board 内部有与每个特定实例交互的东西(因为每个游戏都有不同的棋盘),所以直接调用它不是一个好主意。
  • @vsync 您将board 添加到Game.prototype,因此所有游戏​​实例共享同一个板。考虑到您上面的评论,也许这不是您真正想要的。
  • @vsync:如果您希望每个Game 实例都有自己的板,则必须更改您的实现,以便为每个Game 实例创建一个板。然后董事会可以有一个所有者,并且可以知道它是哪个所有者。请参阅我上面添加的代码。
  • 我完全按照我想要的方式设计了它,所有实例都共享原型。我已经编写 JS 代码 8 年了,我对此并不陌生..但问题是,我在徘徊也许有一种方法可以通过一些闭包技巧来保存范围..有时 JS 需要更多的头脑创造力
  • steaks 的答案是我一直在寻找的东西,这是一种 JS 技巧,可以让最里面的原型对象知道最上面的范围。整洁。
【解决方案2】:

没有“深入原型”之类的东西。 "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 函数可以帮助您处理这些内容)

【讨论】:

  • 是的,显然......但我不想改变我的代码,它被编写并将所有内容拆分为“平面”函数,而不嵌套在命名空间中,这是有道理的,这是丑陋的编程...... .
  • 我还有很多内部函数,一直写Game.prototype.something= function(){ .. } 会很麻烦,我只是认为不值得我为核心范围访问而获得的简单修复
  • 该 hack 不起作用,因为它错过了 property 的关闭。它应该只使用 ES5 bind: this.board[p] = this.board[p].bind(this);
  • 你是对的,它缺少一个闭包。但是,它不应该“只使用”绑定——IE8 不支持它,它仍然是一个“流行”的浏览器。
【解决方案3】:

更新

如果您不想为每个函数提供作用域,则必须为每个游戏创建一个新实例。如果你愿意,你可以通过在 Game 构造函数中将 board 构造函数设为变量,将 board 设为 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 实例之间共享。因此,以下解决方案不适用于多个游戏。


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

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();

【讨论】:

  • 你是一位优秀的开发人员,我的朋友,我喜欢第二部分。我已经重写它以更好地匹配
  • 这只适用于Game 对象的一个​​实例。
  • @Guffa 你是对的。开发人员不应按原样使用此答案。
  • @Guffa 我认为我修改后的解决方案应该适用于多个 Game 实例。
  • 这些都不是一个好的解决方案。 1)因为不必要的“类”(带有额外的原型对象,而只需要一个“实例”) 2)因为它覆盖了那个 one 共享板对象的属性 3)因为它具有与 2 相同的问题
【解决方案4】:

试试这个方法

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();

你需要做两件事

  • 在原型方法内部创建方法,意味着on应该在a内部定义,这样它就可以访问this

  • that 中创建父引用的副本并在on 中使用它

【讨论】:

    【解决方案5】:

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

    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 后立即授予对 on 的访问权限:

    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!
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      • 2012-03-28
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多