全局变量只能由第一个函数访问

Global variable only accessible to first function?

本文关键字:函数 访问 第一个 全局变量      更新时间:2023-09-26

我对JS有一个不错的理解,但最终仍在学习。我正试图在IndexedDB上重新创建一个PHP/mySQL项目,但无法解释为什么我在这里看到错误。

IndexedDB连接正常工作。第一个函数(createItem)运行良好,但第二个函数(getItems)返回一个错误,声称"db"变量未定义。它是一个全局变量,因此函数的作用域应该可以访问它,createItem函数使用它没有问题。有人能帮我看看我在这里遗漏了什么吗。

// GLOBAL DB VAR
var db;
// WAIT FOR DOM
document.addEventListener("DOMContentLoaded", function(){
    // IF INDEXED DB CAPABLE
    if("indexedDB" in window) {
        // OPEN DB
        var openRequest = indexedDB.open("newTabTest",1);
        // CREATE NEW / UPDATE
        openRequest.onupgradeneeded = function(e) {
            // Notify user here: creating database (first time use)
            var thisDB = e.target.result;
            // Create "items" table if it doesn't already exist
            if(!thisDB.objectStoreNames.contains("items")) {
                var store = thisDB.createObjectStore("items", {keyPath: "id", autoIncrement: true});
                store.createIndex("name","name", {unique:true});
                store.createIndex("folder","folder", {unique:false});
                store.createIndex("dial","dial", {unique:false});
            }
        }
        openRequest.onsuccess = function(e) {
            // Success- set db to target result.
            db = e.target.result;
        }
        openRequest.onerror = function(e) {
            // DB ERROR :-(
        }
    }
},false);
// CREATE ITEM FUNCTION
function createItem(n,u,f,c) {
    var transaction = db.transaction(["items"],"readwrite");
    var store = transaction.objectStore("items");
    var item = {
        name: n,
        url: u,
        folder: f,
        colour: c,
        dial: 0,
        order: 100
    }
    var request = store.add(item);
    request.onerror = function(e) {
        console.log("An error occured.");
    }
    request.onsuccess = function(e) {
        console.log("Successfully added.")
    }
};
// GET ITEM(S) FUNCTION
// Specify index and key value OR omit for all
function getItems(callback, ind, key) {
    var transaction = db.transaction(["items"],"readonly");
    var store = transaction.objectStore("items");
    var response = [];
    // If args are omitted - grab all items
    if(!ind | !key) {
        var cursor = store.openCursor();
        cursor.onsuccess = function(e) {
            var res = e.target.result;
            if(res) {
                var r = {
                    "name": res.value['name'],
                    "url": res.value['url'],
                    "folder": res.value['folder'],
                    "colour": res.value['colour'],
                    "dial": res.value['dial'],
                    "order": res.value['order']
                };
                response.push(r);
                res.continue();
            }
        }
        cursor.oncomplete = function() {
            callback(response);
        }
    } else {
        // If both args are specified query specified index
        store = store.index(ind);
        var range = IDBKeyRange.bound(key, key);
        store.openCursor(range).onsuccess = function(e) {
            var res = e.target.result;
            if(res) {
                var r = {
                    "name": res.value['name'],
                    "url": res.value['url'],
                    "folder": res.value['folder'],
                    "colour": res.value['colour'],
                    "dial": res.value['dial'],
                    "order": res.value['order']
                };
                response.push(r);
                res.continue();
            }
        }
        cursor.oncomplete = function() {
            callback(response);
        }
    }
};

正如您在注释中所指出的,您必须将依赖于数据库的操作粘贴在从success处理程序调用的函数中。

这种基于回调的编程很快就会成为一种痛苦,而常见的解决方案是使用promise。

然而,使IndexedDB与promise一起工作仍在进行中(请参阅https://github.com/inexorabletash/indexeddb-promises如果你感兴趣的话)。

如果你的目标是完成一些事情(而不是学习裸露的IndexedDBAPI),也许你最好为IndexedDB找到一个包装库(但我不推荐,因为我还没有认真尝试过使用IDB)。

我不建议像您在示例中那样使用"db"这样的变量。如果你刚开始阅读和编写异步Javascript,你只会给自己带来很多痛苦。有更好的方法可以做到这一点。它需要几页的时间来解释,并且在StackOverflow上的许多其他问题中都有解释,所以,非常简短地考虑重写代码,以执行以下操作:

function createItem(db, ...) {
  var tx = db.transaction(...);
  // ...
}
function openIndexedDBThenCreateItem(...) {
  var openRequest = indexedDB.open(...);
  openRequest.onsuccess = function(event) {
    var db = event.target.result;
    createItem(db, ...);
  };
}
function getItems(db, callback, ...) {
  var tx = db.transaction(...);
  var items = [];
  tx.oncomplete = function(event) {
    callback(items);
  };
  // ...
  var request = store.openCursor(...);
  request.onsuccess = function(event) {
    var request = event.target;
    var cursor = event.target.result;
    if(cursor) {
      var item = cursor.value;
      items.push(item);
      cursor.continue();
    }
  };
}
function openIndexedDBThenGetItems(db, callback, ...) {
  var openRequest = indexedDB.open(...);
  openRequest.onsuccess = function(event) {
    var db = event.target.result;
    getItems(db, callback, ...);
  };
}

此外,您不需要等待DOMContentLoaded开始使用indexedDB。它立即可用。

如果你得到了上面的代码,那么你可以考虑进一步改进,添加一个简单的辅助函数:

function openIndexedDB(callback) {
  var openRequest = indexedDB.open(...);
  openRequest.onerror = callback;
  openRequest.onsuccess = callback;
}

然后重写这样的例子:

function openIndexedDBThenCreateItem(...) {
  openIndexedDB(function onOpen(event) {
     if(event.type !== 'success') {
       console.error(event);
     } else {
       var db = event.target.result;
       createItem(db, ...);
     }
  });
}
function openIndexedDBThenGetItems(...) {
  openIndexedDB(function onOpen(event) {
     if(event.type !== 'success') {
       console.error(event);
     } else {
       var db = event.target.result;
       getItems(db, ...);
     }
  });
}