You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MVC+OOP重构JS项目报seatsIndex is not a function错误咨询

错误原因

  • 实例化顺序错误:const app = new Controller(new Model(), new View()); 这行代码会先执行new View()触发View的构造函数,构造函数里直接调用了this.updateSelectedSeats(),此时Controller还未初始化,也没有给View绑定handleSelectedSeats回调,updateSelectedSeats接收的seatsIndex参数是undefined,调用就直接抛出类型错误。
  • View内部多处调用updateSelectedSeats时未传参:点击座位的事件回调、电影选择切换的事件回调中,调用updateSelectedSeats都没有传入处理函数作为参数,就算第一次错误修复,后续触发这些事件也会报同样的错。

修复方案

调整逻辑给View增加存储回调的实例属性,等Controller绑定完回调后再执行第一次数据更新,修改后的代码如下:

修改View类

class View {
  screenContainer = document.querySelector('.screen__container');
  unoccupiedSeats = document.querySelectorAll(
    '.seat__row .seat__position:not(.seat__occupied)'
  );
  count = document.querySelector('#checkout__text--count');
  total = document.querySelector('#checkout__text--total');
  // 新增存储座位更新回调的属性
  #seatsIndexHandler = null;

  constructor() {
    this.movieSelect = document.querySelector('#movie');
    this.ticketPrice = +this.movieSelect.value;
    this.populateUI();
    this._seatHandler();
    // 移除构造函数内的updateSelectedSeats调用,等回调绑定完成后再执行
  }

  _seatHandler() {
    this.screenContainer.addEventListener('click', e => {
      const seat = e.target.classList.contains('seat__position');
      const occupied = e.target.classList.contains('seat__occupied');

      if (seat && !occupied) e.target.classList.toggle('seat__selected');
      this.updateSelectedSeats();
    });
  }

  bindSetMovieData(handler) {
    this.movieSelect.addEventListener('change', e => {
      this.ticketPrice = +e.target.value;
      handler(e.target.selectedIndex, e.target.value);
      this.updateSelectedSeats();
    });
  }

  // 新增绑定座位回调的方法
  bindSeatsIndex(handler) {
    this.#seatsIndexHandler = handler;
    // 绑定完成后执行第一次更新
    this.updateSelectedSeats();
  }

  updateSelectedSeats() {
    const selectedSeats = document.querySelectorAll(
      '.seat__row .seat__selected'
    );

    const index = [...selectedSeats].map(seat =>
      [...this.unoccupiedSeats].indexOf(seat)
    );
    // 回调存在时才执行
    if (typeof this.#seatsIndexHandler === 'function') {
      this.#seatsIndexHandler(index);
    }

    const numSeats = selectedSeats.length;
    this.count.innerText = numSeats;
    this.total.innerText = numSeats * this.ticketPrice;
  }

  populateUI() {
    const selectedSeats = JSON.parse(localStorage.getItem('selectedSeats'));

    if (selectedSeats !== null && selectedSeats.length > 1)
      this.unoccupiedSeats.forEach((seat, index) => {
        if (selectedSeats.indexOf(index) > -1)
          seat.classList.add('seat__selected');
      });

    const selectedMovieIndex = localStorage.getItem('selectedMovieIndex');

    if (selectedMovieIndex !== null) {
      this.movieSelect.selectedIndex = selectedMovieIndex;
    }

    this.ticketPrice = localStorage.getItem('selectedMoviePrice');
  }
}

修改Controller类

class Controller {
  constructor(model, view) {
    this.model = model;
    this.view = view;

    this.view.bindSetMovieData(this.handleSetMovieData);
    // 绑定座位处理回调
    this.view.bindSeatsIndex(this.handleSelectedSeats);
  }

  handleSetMovieData = (index, value) => {
    this.model.setMovieData(index, value);
  };

  handleSelectedSeats = index => {
    this.model.selectedSeats(index);
  };
}

Model类和实例化代码不需要修改,修改后即可正常运行。

内容的提问来源于stack exchange,提问作者jean_vw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 06:15:04