Кажется, я использую переменную до того, как она полностью создана в Javascript, но это работает - почему? - PullRequest
5 голосов
/ 14 февраля 2012

Может ли кто-нибудь объяснить это мне?

var diagramImage = new Kinetic.Shape(function () {
    var context = this.getContext();
    context.beginPath();
    context.lineWidth = 1;
    //This is crazy tricks. It's part of the KineticJS demo website, but how am I able to assign diagramImage.color here?
    context.strokeStyle = diagramImage.color;

    var lastVertice = polygon.Vertices[polygon.Vertices.length - 1];

    context.moveTo(lastVertice.X, lastVertice.Y);

    for (var i = 0; i < polygon.Vertices.length; i++) {
        var vertice = polygon.Vertices[i];
        context.lineTo(vertice.X, vertice.Y);
    }

    context.stroke();
    context.closePath();
});

Мне кажется, что diagramImage не существует, пока не вернется конструктор Kinetic, но я могу (и, кажется, должен) назначить цвет strokeStyle контекста для цвета diagramImage - до того, как diagramImage был создан? Почему это работает?

РЕДАКТИРОВАТЬ: Полный код:

function DrawPolygon(diagramLayer, polygon) {
    var diagramImage = new Kinetic.Shape(function () {
        var context = this.getContext();
        context.beginPath();
        context.lineWidth = 2;
        //This is crazy tricks. It's part of the KineticJS demo website, but how am I able to assign diagramImage.color here?
        context.strokeStyle = diagramImage.color;

        var lastVertice = polygon.Vertices[polygon.Vertices.length - 1];

        context.moveTo(lastVertice.X, lastVertice.Y);

        for (var i = 0; i < polygon.Vertices.length; i++) {
            var vertice = polygon.Vertices[i];
            context.lineTo(vertice.X, vertice.Y);
        }

        context.stroke();
        context.closePath();
    });

    diagramImage.color = "red";

    diagramImage.on("mouseover", function () {
        this.color = "green";
        diagramLayer.draw();
    });

    diagramImage.on("mouseout", function () {
        this.color = "red";
        diagramLayer.draw();
    });

    diagramLayer.add(diagramImage);
    planViewStage.add(diagramLayer);
}

Ответы [ 3 ]

8 голосов
/ 14 февраля 2012

Потому что то, где вы вызываете diagramImage.color, находится в замыкании / функции, которая передается конструктору Kinetic.Shape. Эта функция не вызывается / не выполняется конструктором до тех пор, пока новый экземпляр, созданный конструктором, не будет присвоен diagramImage.

Вот минимальный пример, который может лучше объяснить, что происходит:

var MyObject = function(f){
  this.myFunc = f; // f is executed sometime later...
};
MyObject.prototype.execute = function(){
  this.myFunc();
};

var myObjInst = new MyObject(function(){
  console.log("myObjInst:", myObjInst);
});
myObjInst.execute();

Как заметил Twisol, это можно улучшить, используя вместо этого this. Например:

(function(){
  var MyObject = function(f){
    this.myFunc = f; // f is executed sometime later...
  };
  MyObject.prototype.execute = function(){
    this.myFunc();
  };

  var myObjInst = new MyObject(function(){
    console.log("myObjInst:", this);
  });
  myObjInst.execute();
})();

Однако, как отметил Крис, если это не задокументировано API - нет никакой гарантии, что this будет ссылаться на Kinetic.Shape во время обратного вызова - поэтому продолжение использования diagramImage здесь может быть лучшим из этих двух вариантов .

Короче говоря, я думаю, что это не лучший API / пример / использование JavaScript - и я бы не стал считать это нюансом JavaScript, с которым вам придется иметь дело. Конечно, эти нюансы есть, если они вам нужны, но не обязательно.

1 голос
/ 14 февраля 2012

Это интересная конструкция. Кажется, что происходит:

  • diagramImage является ссылкой до того, как ей присвоено что-либо только на основании объявления. Чтобы визуализировать это, представьте, что var diagramImage был на предыдущей строке сам по себе.
  • Kinetic.Shape принимает функцию обратного вызова, эту анонимную функцию, в качестве одного из аргументов конструктора для использования позже.
  • Обратный вызов хочет сослаться на объект Kinetic.Shape. Предположительно, существует некоторый контракт, описывающий то, к чему this относится позже (как доказано использованием this.getContext()), и это не объект Kinetic.Shape.
  • Поскольку diagramImage является ссылкой, и к тому времени, когда ссылка будет использоваться, ей будет присвоен новый Kinetic.Shape, поэтому использовать ее для этой цели будет кошерно.

В принципе, это ничем не отличается от обычного шаблона использования локальной переменной, чтобы сделать текущее значение доступным в обратном вызове, например,

var self = this;
$('myelement').click( function(){ self.hi = true; } );

Просто здесь переменная, доступная для дальнейшего использования, не является текущим объектом, она является членом указанного объекта.

0 голосов
/ 14 февраля 2012

Я думаю, что этот пост может помочь объяснить это - http://www.quirksmode.org/js/associative.html

В частности, раздел об ассоциативных массивах.Запись объясняет, что объекты в javascript также считаются ассоциативными массивами.

Таким образом, хотя событие diagramImage.strokeStyle не может быть явно определено, вы все равно можете ссылаться на diagramImage['strokeStyle'].

Помогает ли это?

...