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

从API JSON创建Movie对象后,数组索引访问返回undefined问题求助

问题描述

我尝试从API返回的JSON数据创建Movie对象,将每个创建好的对象存入电影数组,整个逻辑在函数内完成并返回该数组。调用函数后,我能通过console.log打印数组,但使用索引访问特定元素时返回undefined。相关代码如下:

function Movie(title, description, director, producer) { 
  this.title = title; 
  this.description = description; 
  this.director = director; 
  this.producer = producer; 
}
var connectedToAPI = false;
function retrieveMovies() { 
  var movies = []; 
  var request = new XMLHttpRequest(); 
  request.open('GET', 'https://ghibliapi.herokuapp.com/films', true); 
  request.onload = function() { 
    var data = JSON.parse(this.response); 
    if (request.status >= 200 && request.status < 400) { 
      var x = 0; 
      data.forEach(movie => { 
        var film = new Movie(movie.title, movie.description, movie.director, movie.producer); 
        movies[x] = film; 
        x++; 
      }); 
    } else { 
      console.log('error'); 
    } 
  } 
  request.send(); 
  connectedToAPI = true; 
  return movies; 
}
var films = retrieveMovies();
if (connectedToAPI == true) { 
  console.log(films); 
  console.log(films.length); 
  console.log("THIS IS MOVIE NUMBER 3: "); 
  console.log(films[1]); 
}

控制台输出:

[] //->展开后包含电影数据
0 //->长度为0
THIS IS MOVIE NUMBER 3: undefined //->索引访问返回undefined

请问我的代码存在什么问题?恳请帮助,谢谢!


问题原因与解决方案

核心问题:异步请求的执行时序

你的代码里的XMLHttpRequest是异步请求(request.open的第三个参数是true,表示异步),这意味着:

  • 当你调用retrieveMovies()时,浏览器会立刻发送请求,但不会等待请求完成,直接执行后面的connectedToAPI = true和return movies,此时movies还是空数组。
  • 后面的console.log(films)、films.length等代码,都是在请求还没返回、数据还没填充到数组的时候执行的,所以此时数组长度是0,索引访问自然是undefined。
  • 至于你看到控制台展开数组后有数据,是因为浏览器的控制台是惰性求值的:它只是保存了数组的引用,当你点击展开时,请求已经完成,数组已经被填充了数据,所以能看到,但此时之前的length和索引访问的代码早就执行完了。

解决方案:用回调/Promise处理异步逻辑

你需要把依赖异步请求结果的代码,放到请求完成的回调里,或者用Promise来处理。这里给你两种常见的解决方式:

方式1:使用回调函数

修改retrieveMovies,让它接收一个回调函数,在请求成功后执行回调并传递填充好的数组:

function Movie(title, description, director, producer) { 
  this.title = title; 
  this.description = description; 
  this.director = director; 
  this.producer = producer; 
}

function retrieveMovies(callback) { 
  var movies = []; 
  var request = new XMLHttpRequest(); 
  request.open('GET', 'https://ghibliapi.herokuapp.com/films', true); 
  request.onload = function() { 
    var data = JSON.parse(this.response); 
    if (request.status >= 200 && request.status < 400) { 
      data.forEach(movie => { 
        var film = new Movie(movie.title, movie.description, movie.director, movie.producer); 
        movies.push(film); // 用push更简洁,无需手动维护索引变量
      }); 
      // 请求成功后调用回调,传递结果
      callback(null, movies);
    } else { 
      console.log('error'); 
      callback(new Error('Request failed'), null);
    } 
  } 
  // 处理网络错误情况
  request.onerror = function() {
    callback(new Error('Network error'), null);
  }
  request.send(); 
}

// 调用时传入回调
retrieveMovies(function(err, films) {
  if (err) {
    console.error(err);
    return;
  }
  console.log(films); 
  console.log(films.length); 
  console.log("THIS IS MOVIE NUMBER 3: "); 
  console.log(films[1]); 
});

方式2:使用Promise(更现代的写法)

把异步请求包装成Promise,这样可以用async/await让代码看起来更像同步逻辑:

function Movie(title, description, director, producer) { 
  this.title = title; 
  this.description = description; 
  this.director = director; 
  this.producer = producer; 
}

function retrieveMovies() { 
  return new Promise((resolve, reject) => {
    var movies = []; 
    var request = new XMLHttpRequest(); 
    request.open('GET', 'https://ghibliapi.herokuapp.com/films', true); 
    request.onload = function() { 
      var data = JSON.parse(this.response); 
      if (request.status >= 200 && request.status < 400) { 
        data.forEach(movie => { 
          var film = new Movie(movie.title, movie.description, movie.director, movie.producer); 
          movies.push(film); 
        }); 
        resolve(movies);
      } else { 
        reject(new Error('Request failed with status ' + request.status));
      } 
    } 
    request.onerror = function() {
      reject(new Error('Network error'));
    }
    request.send(); 
  });
}

// 使用async/await调用
async function loadMovies() {
  try {
    var films = await retrieveMovies();
    console.log(films); 
    console.log(films.length); 
    console.log("THIS IS MOVIE NUMBER 3: "); 
    console.log(films[1]); 
  } catch (err) {
    console.error(err);
  }
}

loadMovies();

额外小优化

你之前在forEach里手动维护x变量来给数组赋值,其实用movies.push(film)就可以直接把元素添加到数组末尾,代码更简洁易读。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:44:03