【问题标题】:JavaScript: multiple instances wrong reference to private propertyJavaScript:多个实例错误引用私有属性
【发布时间】:2017-09-04 16:37:57
【问题描述】:

在小提琴中是我编写的用于管理数据模型导航的“类”和一个测试,该测试表明该“类”的多个实例(从第二个开始)引用了一些错误。

https://jsfiddle.net/btvmnaxc/ (输出到控制台)

预期的输出是

[{"name":"xx"}]
[{"name":"yy"}]

但是在通过 setElements 设置 Elements 之后,在其他方法中 Elements 是空的,奇怪的是只有在创建第二个实例之后。我可以认为 setElements 会覆盖引用,但为什么其他方法保留这个旧引用而不是从 var 获取新引用。

有人能解释一下这种行为吗?

附:我可能可以考虑一个解决方案,将 vars 打包到作为对象的属性中。

function Pagination() {
  var props = {Elements:[], ...}
}

附言

function Pagination() {
  var that = this;
  var Elements = [0,1];
  var Frame = [];
  var FrameNumber = 0;
  var EntitiesPerFrame = 25;
  var FrameChangedCB = [];

  this.subscribeFrameChange = function(cb) {
    if (typeof cb === "function") {
      FrameChangedCB.push(cb);
    } else {
      throw new Error("Not a function");
    }
  }

  this.setEntitiesPerFrame = function(entities_per_frame) {
    entities_per_frame = parseInt(entities_per_frame);
    if (entities_per_frame > 0) {
      EntitiesPerFrame = entities_per_frame;
      while (!this.canDisplayFrame(FrameNumber) && FrameNumber > 0) {
        FrameNumber--;
      }
      calculateFrame();
    }
  }

  frameChanged = function() {
    FrameChangedCB.forEach(function(cb) {
      cb();
    });
  }

  this.setElements = function(elements) {
    if (Array.isArray(elements)) {
      Elements = elements;
      calculateFrame();
      console.log("qq");
    } else {
      throw new Error("Can only work with arrays");
    }
  }

  this.getStart = function() {
    return FrameNumber * EntitiesPerFrame;
  }

  this.getEnd = function() {
    var end = (FrameNumber + 1) * EntitiesPerFrame;
    return end > Elements.length ? Elements.length : end;
  }

  this.getEntitiesPerFrame = function() {
    return EntitiesPerFrame;
  }

  calculateFrame = function() {
    var start = that.getStart();
    var end = that.getEnd();
    if (that.canDisplayFrame(FrameNumber)) {
      Frame = Elements.slice(
        start,
        end
      );
      frameChanged();
    } else {
      throw new Error("Boundaries");
    }
  }

  this.canDisplayFrame = function(nr) {
    nr = parseInt(nr);
    var can = false;
    var start = nr * EntitiesPerFrame
    var end = (nr + 1) * EntitiesPerFrame;

    if (start <= Elements.length && nr >= 0) {
      can = true;
    }

    return can;
  }

  this.getFrame = function() {
    return Frame;
  }

  this.next = function() {
    return this.goto(FrameNumber + 1);
  }

  this.prev = function() {
    return this.goto(FrameNumber - 1);
  }

  this.goto = function(frame_nr) {
    var changed = false;
    if (that.canDisplayFrame(frame_nr)) {
      FrameNumber = parseInt(frame_nr);
      calculateFrame();
      changed = true;
    }
    return changed;
  }

  this.getLength = function() {
    return Elements.length;
  }


}


var b = new Pagination();
var a = new Pagination();
a.setElements([{name: 'xx'}]);
b.setElements([{name: 'yy'}]);
console.log(JSON.stringify(a.getFrame()));
console.log(JSON.stringify(b.getFrame()));

【问题讨论】:

  • 请在问题中发布完整的代码(您仍然可以edit它),而不是(仅)在外部小提琴中。
  • 你的frameChangedcalculateFrame 函数是accidentally global

标签: javascript object prototype private-members private-methods


【解决方案1】:

这是因为你在滥用implicit globals

您的Pagination 函数包含两个地方,其中一个函数被分配给一个标识符而不使用var

  calculateFrame = function() {
    var start = that.getStart();
    var end = that.getEnd();
    if (that.canDisplayFrame(FrameNumber)) {
      Frame = Elements.slice(
        start,
        end
      );
      frameChanged();
    } else {
      throw new Error("Boundaries");
    }
  }

这会将这个函数分配给一个名为 calculateFrame 的全局变量,并且对 calculateFrame() 的任何调用都将调用最后分配的任何一个(因此使用它有权访问的任何范围)。

要解决此问题,请使用var:

  var calculateFrame = function() {
    var start = that.getStart();
    var end = that.getEnd();
    if (that.canDisplayFrame(FrameNumber)) {
      Frame = Elements.slice(
        start,
        end
      );
      frameChanged();
    } else {
      throw new Error("Boundaries");
    }
  }

或者更好的是,使用命名函数声明:

  function calculateFrame() {
    var start = that.getStart();
    var end = that.getEnd();
    if (that.canDisplayFrame(FrameNumber)) {
      Frame = Elements.slice(
        start,
        end
      );
      frameChanged();
    } else {
      throw new Error("Boundaries");
    }
  }

在修复了你有这个问题的两个地方之后,sn -p 输出了预期的结果。

function Pagination() {
  var that = this;
  var Elements = [0, 1];
  var Frame = [];
  var FrameNumber = 0;
  var EntitiesPerFrame = 25;
  var FrameChangedCB = [];

  this.subscribeFrameChange = function(cb) {
    if (typeof cb === "function") {
      FrameChangedCB.push(cb);
    } else {
      throw new Error("Not a function");
    }
  }

  this.setEntitiesPerFrame = function(entities_per_frame) {
    entities_per_frame = parseInt(entities_per_frame);
    if (entities_per_frame > 0) {
      EntitiesPerFrame = entities_per_frame;
      while (!this.canDisplayFrame(FrameNumber) && FrameNumber > 0) {
        FrameNumber--;
      }
      calculateFrame();
    }
  }

  function frameChanged() {
    FrameChangedCB.forEach(function(cb) {
      cb();
    });
  }

  this.setElements = function(elements) {
    if (Array.isArray(elements)) {
      Elements = elements;
      calculateFrame();
      console.log("qq");
    } else {
      throw new Error("Can only work with arrays");
    }
  }

  this.getStart = function() {
    return FrameNumber * EntitiesPerFrame;
  }

  this.getEnd = function() {
    var end = (FrameNumber + 1) * EntitiesPerFrame;
    return end > Elements.length ? Elements.length : end;
  }

  this.getEntitiesPerFrame = function() {
    return EntitiesPerFrame;
  }

  function calculateFrame() {
    var start = that.getStart();
    var end = that.getEnd();
    if (that.canDisplayFrame(FrameNumber)) {
      Frame = Elements.slice(
        start,
        end
      );
      frameChanged();
    } else {
      throw new Error("Boundaries");
    }
  }

  this.canDisplayFrame = function(nr) {
    nr = parseInt(nr);
    var can = false;
    var start = nr * EntitiesPerFrame
    var end = (nr + 1) * EntitiesPerFrame;

    if (start <= Elements.length && nr >= 0) {
      can = true;
    }

    return can;
  }

  this.getFrame = function() {
    return Frame;
  }

  this.next = function() {
    return this.goto(FrameNumber + 1);
  }

  this.prev = function() {
    return this.goto(FrameNumber - 1);
  }

  this.goto = function(frame_nr) {
    var changed = false;
    if (that.canDisplayFrame(frame_nr)) {
      FrameNumber = parseInt(frame_nr);
      calculateFrame();
      changed = true;
    }
    return changed;
  }

  this.getLength = function() {
    return Elements.length;
  }


}


var b = new Pagination();
var a = new Pagination();
a.setElements([{
  name: 'xx'
}]);
b.setElements([{
  name: 'yy'
}]);
console.log(a.getFrame());
console.log(b.getFrame());

【讨论】:

  • 滥用?我不认为这是故意的:-)
  • @Bergi 无意滥用仍然是我书中的滥用。 ;)
  • 确实不是故意的。无论如何,我感谢您的帮助。
  • @IgorFialko 请记住在严格模式下运行您的代码。除其他外,它会阻止您使用隐式全局变量。
  • 我通常会这样做。但是sn-p 所来自的项目必须支持IE9 并且也没有转译器,所以有时会不幸出现这些错误。
猜你喜欢
  • 2012-02-08
  • 2020-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-03
  • 2013-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多