簡體   English   中英

Google Scripts - 在另一個函數中調用函數時出錯

[英]Google Scripts - Error When Calling Function in Another Function

我發布了我在下面的全部代碼。

我正在使用 Google Scripts,從不斷變化的工作表中提取一些數據,執行計算,然后重新填充工作表。

數據正在被調用到應用程序中,因此我正在嘗試盡快進行計算(因此,如果有人能告訴我更有效的方法來執行此操作,那也會有所幫助)。

當我嘗試調用invoicePrice()函數或tiers()函數,我得到一個類型錯誤:功能不能被發現。

function invoicePrice() {
      // define row to do calculations on
      var AVals = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange("A1:A1000").getValues();
      var ALast = AVals.filter(String).length;

      // Set order No. here

      var orderNoPrev = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast - 1, 3).getValue();
      var orderNo = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 3).setValue(orderNoPrev + 1);
      var total = 0;

      // invoicePrice the total number of items
      var itemRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 4, 1, 14).getValues()[0];

      // set the values of the constants on the Products page

      var priceRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Products").getRange("F1:F1000").getValues();
      var pLast = priceRange.filter(String).length;
      var pGrab = priceRange.splice(0,pLast);

      var invoicePrice = 0;

      // set weight of case values

      var weightRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Products").getRange("V1:V1000").getValues();
      var wLast = weightRange.filter(String).length;
      var wGrab = weightRange.splice(0,wLast);


      for (var i = 0, len = 13; i <= len; i++) {
          // SUBTOTAL THE ORDER AMOUNT
          var orderAmount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 4).getValue();

          total += orderAmount;

          // SET/KG PRICES
          var perKGInvoice = pGrab[i + 1].map(Number);

          // SET WEIGHTS
          var weights = wGrab[i + 1].map(Number);

          // SET CASE PRICE
          var casePrice = perKGInvoice * weights;

          // SUM OF PRODUCT PRICE * ORDER AMOUNT * WEIGHT OF CASE
          invoicePrice += orderAmount * perKGInvoice * weights;


      }
    Logger.log(invoicePrice, ALast, pGrab, wGrab);
    return [invoicePrice, ALast, pGrab, wGrab];



  }

  function tiers() {
      var invoicePriceCall = invoicePrice();
      var invoicePrice = invoicePriceCall[0];
      var ALast = invoicePriceCall[1];

      var beefDiscount = 0;
      var sfDiscount = 0;
      var currentTier = ""

      // Set the invoice order value required ($ amount)

      var tier1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(3, 3).getValue();
      var tier2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(4, 3).getValue();
      var tier3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(5, 3).getValue();
      var tier4 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(6, 3).getValue();
      var tier5 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(7, 3).getValue();
      var minNotmet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(10, 3).getValue();

      // Set the discount tier levels for beef (% discount)
      var tier1BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(3, 4).getValue();
      var tier2BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(4, 4).getValue();
      var tier3BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(5, 4).getValue();
      var tier4BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(6, 4).getValue();
      var tier5BeefDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(7, 4).getValue();

      var tier1SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(3, 5).getValue();
      var tier2SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(4, 5).getValue();
      var tier3SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(5, 5).getValue();
      var tier4SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(6, 5).getValue();
      var tier5SfDiscount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DiscountGrid").getRange(7, 5).getValue();




      if (invoicePrice < tier1) {
          beefDiscount = tier1BeefDiscount;
          sfDiscount = tier1SfDiscount;
          currentTier = "Minimum Not Met"
      }
      else if (invoicePrice < tier2) {
          beefDiscount = tier1BeefDiscount;
          sfDiscount = tier1SfDiscount;
          currentTier = "Tier 1"
      }
      else if (invoicePrice < tier3) {
          beefDiscount = tier2BeefDiscount;
          sfDiscount = tier2SfDiscount;
          currentTier = "Tier 2"
      }
      else if (invoicePrice < tier4) {
          beefDiscount = tier3BeefDiscount;
          sfDiscount = tier3SfDiscount;
          currentTier = "Tier 3";
      }
      else if (invoicePrice < tier5) {
          beefDiscount = tier4BeefDiscount;
          sfDiscount = tier4SfDiscount;
          currentTier = "Tier 4";
      }
      else {
          beefDiscount = tier5BeefDiscount;
          sfDiscount = tier5SfDiscount;
          currentTier = "Tier 5";
      }


      SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 20).setValue(beefDiscount);
      SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 21).setValue(sfDiscount);
      SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 22).setValue(currentTier);

      return [beefDiscount, sfDiscount, tier1];

  }


  function calcsOnColumns2() {

      // call price range and weight from previous function 
      var invoicePriceCall = invoicePrice();
      var invoicePrice = invoicePriceCall[0];
      var ALast = invoicePriceCall[1];
      var priceRange = invoicePriceCall[2];
      var weightRange = invoicePriceCall[3];

      // call tiers function
      var tiers = tiers();
      var beefDiscount = tiers[0];
      var sfDiscount = tiers[1];
      var tier1 = tiers[2];

      // set sums to 0

      var beefSum = 0;
      var sfSum = 0

      for (var i = 0, len = 13; i <= len; i++) {
          // SUBTOTAL THE ORDER AMOUNT
          var orderAmount = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 4).getValue();

          // SET/KG PRICES
          var perKGInvoice = priceRange[i + 1].map(Number);

          // SET WEIGHTS
          var weights = weightRange[i + 1].map(Number);

          // SET CASE PRICE
          var casePrice = perKGInvoice * weights;

          // Split up seafood and beef with if statement:

          if (i < 9) {
              // set discount case price
              var discountCaseBeef = perKGInvoice - (perKGInvoice * beefDiscount);
              // set the total price of the discounted cases in the order (ie, order price * discount)
              var beefCaseOrder = discountCaseBeef * orderAmount * weights;
              beefSum += beefCaseOrder;
              // Nice and Formatted sheet
              var casePrice = perKGInvoice * weights;

              if (orderAmount) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 32).setValue(orderAmount + " cases @" + discountCaseBeef.toFixed(2) + "/kg = ~$" + beefCaseOrder.toFixed(2)); }

          }

          else {
              var discountCaseSF = perKGInvoice - (perKGInvoice * sfDiscount);
              // set the total price of the discounted cases in the order (ie, order price * discount)
              var sfCaseOrder = discountCaseSF * orderAmount * weights;
              sfSum += sfCaseOrder;
              // Nice and Formatted sheet
              var casePrice = perKGInvoice * weights;
              if (orderAmount) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, i + 32).setValue(orderAmount + " cases @" + discountCaseSF.toFixed(2) + "/kg = ~$" + sfCaseOrder.toFixed(2)); }

          }



          if (i == len) {
              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 24).setValue(beefSum);
              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 25).setValue(sfSum);
              // if statement to go here if min order size not met

              var sumOfSums = beefSum + sfSum;
              // swap out 75 with referenced value
              if (sumOfSums > tier1) { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 27).setValue(sumOfSums) }
              // swap out phrase with changable phrase
              else { SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 27).setValue("Minimum order size not met") }

              var totalDiscount = invoicePrice - sumOfSums

              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 29).setValue(totalDiscount);
              SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SubtotalDisplay").getRange(ALast, 30).setValue(invoicePrice);


          }



      }


  }

問題

甲正確聲明函數( invoicePrice ),從另一個函數(調用時calcsOnColumns2 )是undefined

重現步驟

假設您有三個名為ABC函數,然后調用B會導致您的錯誤:

function A() {
  var A = 0;
  return [A];
}

function C() {
  var a = A();
  var A = a[0];
}

function B() {
  A();
  C();
}

您可能已經注意到,這就是您的功能歸結為 - 行為是由於提升

  1. 函數B調用A ,它返回一個第一個元素為0Array
  2. [從未發生]功能C調用函數A和覆蓋A到的輸出A ;

相反,在第二步A被聲明為等於a第一個元素,然后才調用A 由於a從對A調用中獲取其值,因此A將是undefined ,因此: [undefined]() ->“不是函數”

優化

  1. 保持 DRY(不要重復自己)- 當您看到看起來相同或具有相同結構的代碼時,請考慮使其可重用(使其成為函數、類、對象、變量 - 視情況而定)。 它使您的代碼可讀且更易於調試,但很多時候它可以節省對 API 的額外調用、重新計算,從而節省時間(尤其是在重復 I/O 時)。
  2. 除非絕對必要,否則不要使用注釋(會大大降低可讀性 - 如果您需要其他人幫助調試您的代碼或自己進行調試,這一點至關重要[可能聽起來違反直覺,但您會感到驚訝])。
  3. 除非別無選擇,否則不要將 I/O( getRange()getValue() )放入循環中 - 輸入/輸出總是很慢,因此通常最好將所有內容加載到內存中並在那里處理數據。
  4. 保持一致的作用域:如果你聲明一個變量,每個訪問器都應該在同一個作用域內或嵌套(例如,不要在if...else聲明兩個同名的變量,將它移到外部作用域)。
  5. 使用 TypeScript 或至少JSDoc - 會讓你在試圖理解為什么你的函數需要一種類型而收到另一種類型時不會發瘋(就像你的情況一樣)[UPD:我還在解決方案中添加了 JSDOC 注釋]。

我已經對您的腳本進行了一些優化,請看一下(請在使用前檢查,因為我們沒有示例數據,我無法對其進行測試)。 你可以做的還有很多(特別是關於calcsOnColumns2的循環),但這應該是一個開始:

 /** * [Your decription here] * @returns {Number[]} */ function invoicePrice() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var subtotalDisplaySheet = ss.getSheetByName('SubtotalDisplay'); // define row to do calculations on var AVals = subtotalDisplaySheet.getRange("A1:A1000").getValues(); var ALast = AVals.filter(String).length; // Set order No. here var orderNoPrev = subtotalDisplaySheet.getRange(ALast - 1, 3).getValue(); var orderNo = subtotalDisplaySheet.getRange(ALast, 3).setValue(orderNoPrev + 1); // invoicePrice the total number of items var itemRange = subtotalDisplaySheet.getRange(ALast, 4, 1, 14).getValues()[0]; // set the values of the constants on the Products page var productsSheet = ss.getSheetByName("Products"); var priceRange = productsSheet.getRange("F1:F1000").getValues(); var pLast = priceRange.filter(String).length; var pGrab = priceRange.splice(0, pLast); var outputInvoicePrice = 0; var weightRange = productsSheet.getRange("V1:V1000").getValues(); var wLast = weightRange.filter(String).length; var wGrab = weightRange.splice(0, wLast); var orderAmounts = subtotalDisplaySheet.getRange(Alast, 17).getValues(); var total = 0; for (var i = 0, len = 13; i <= len; i++) { var nextI = i + 1; var orderAmount = orderAmounts[0][i + 4]; total += orderAmount; var perKGInvoice = pGrab[nextI].map(Number); var weights = wGrab[nextI].map(Number); var casePrice = perKGInvoice * weights; outputInvoicePrice += orderAmount * casePrice; } Logger.log(outputInvoicePrice, ALast, pGrab, wGrab); return [outputInvoicePrice, ALast, pGrab, wGrab]; } /** * Gets values from 5 rows in * a column starting from row 3 * @param {Sheet} sheet * @param {Number} column * @returns {*[]} */ function getGridValues(sheet, column) { var range = sheet.getRange(3, column, 5, 1); return range .getValues() .map(function (row) { return row[0]; }); } /** * [Your decription here] * @returns {Number[]} */ function tiers() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var discountGridSheet = ss.getSheetByName("DiscountGrid"); var invoicePriceCall = invoicePrice(); var outputInvoicePrice = invoicePriceCall[0]; var ALast = invoicePriceCall[1]; var beefDiscount = 0; var sfDiscount = 0; var currentTier = ""; var tierValues = getGridValues(discountGridSheet, 3); var tier1 = tierValues[0]; var tier2 = tierValues[1]; var tier3 = tierValues[2]; var tier4 = tierValues[3]; var tier5 = tierValues[4]; var minNotmet = discountGridSheet.getRange(10, 3).getValue(); var tierBeefDiscountValues = getGridValues(discountGridSheet, 4); var tier1BeefDiscount = tierBeefDiscountValues[0]; var tier2BeefDiscount = tierBeefDiscountValues[1]; var tier3BeefDiscount = tierBeefDiscountValues[2]; var tier4BeefDiscount = tierBeefDiscountValues[3]; var tier5BeefDiscount = tierBeefDiscountValues[4]; var tierSfDiscountValues = getGridValues(discountGridSheet, 5); var tier1SfDiscount = tierSfDiscountValues[0]; var tier2SfDiscount = tierSfDiscountValues[1]; var tier3SfDiscount = tierSfDiscountValues[2]; var tier4SfDiscount = tierSfDiscountValues[3]; var tier5SfDiscount = tierSfDiscountValues[4]; if (outputInvoicePrice < tier1) { beefDiscount = tier1BeefDiscount; sfDiscount = tier1SfDiscount; currentTier = "Minimum Not Met"; } else if (outputInvoicePrice < tier2) { beefDiscount = tier1BeefDiscount; sfDiscount = tier1SfDiscount; currentTier = "Tier 1"; } else if (outputInvoicePrice < tier3) { beefDiscount = tier2BeefDiscount; sfDiscount = tier2SfDiscount; currentTier = "Tier 2"; } else if (outputInvoicePrice < tier4) { beefDiscount = tier3BeefDiscount; sfDiscount = tier3SfDiscount; currentTier = "Tier 3"; } else if (outputInvoicePrice < tier5) { beefDiscount = tier4BeefDiscount; sfDiscount = tier4SfDiscount; currentTier = "Tier 4"; } else { beefDiscount = tier5BeefDiscount; sfDiscount = tier5SfDiscount; currentTier = "Tier 5"; } var subtotalDisplaySheet = ss.getSheetByName("SubtotalDisplay"); var subtotalDisplayRange = subtotalDisplaySheet.getRange(ALast, 20, 3, 1); subtotalDisplayRange.setValues([ [beefDiscount], [sfDiscount], [currentTier] ]); return [beefDiscount, sfDiscount, tier1]; } /** * Foramts amount string * @param {Number} amount * @returns {Function} */ function formatAmount(amount) { return function (discount, caseOrder) { return amount + " cases @" + discount.toFixed(2) + "/kg = ~$" + caseOrder.toFixed(2); }; } /** * Counts sum by discount, amount and weights * @param {Number} amount * @param {Number} weights * @returns {Number} */ function countSum(amount, weights) { return function (discount) { return discount * amount * weights; }; } /** * Counts discount case per Kg * @param {Number} perKG * @returns {Number} */ function countDiscountCase(perKG) { return function (discount) { return perKG - (perKG * discount); }; } /** * [Your decription here] */ function calcsOnColumns2() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var subtotalDisplaySheet = ss.getSheetByName("SubtotalDisplay"); var invoicePriceCall = invoicePrice(); var outputInvoicePrice = invoicePriceCall[0]; var ALast = invoicePriceCall[1]; var priceRange = invoicePriceCall[2]; var weightRange = invoicePriceCall[3]; var tiers = tiers(); var beefDiscount = tiers[0]; var sfDiscount = tiers[1]; var tier1 = tiers[2]; var beefSum = 0; var sfSum = 0; var orderAmounts = subtotalDisplaySheet.getRange(ALast, 17).getValues(); for (var i = 0, len = 13; i <= len; i++) { var orderAmount = orderAmounts[0][i + 4]; var nextI = i + 1; var perKGInvoice = priceRange[nextI].map(Number); var weights = weightRange[nextI].map(Number); var casePrice = perKGInvoice * weights; var subtotalRange = subtotalDisplaySheet.getRange(ALast, i + 32); var formatAmountOrder = formatAmount(orderAmount); var countDisountSum = countSum(orderAmount, weights); var countPerKGCase = countDiscountCase(perKGInvoice); // Split up seafood and beef with if statement: if (i < 9) { var discountCaseBeef = countPerKGCase(beefDiscount); var beefCaseOrder = countDisountSum(discountCaseBeef); beefSum += beefCaseOrder; } else { var discountCaseSF = countPerKGCase(sfDiscount); var sfCaseOrder = countDisountSum(discountCaseSF); sfSum += sfCaseOrder; } if (orderAmount) { subtotalRange.setValue( i < 9 ? formatAmountOrder(discountCaseBeef, beefCaseOrder) : formatAmountOrder(discountCaseSF, sfCaseOrder) ); } if (i == len) { subtotalDisplaySheet.getRange(ALast, 24).setValue(beefSum); subtotalDisplaySheet.getRange(ALast, 25).setValue(sfSum); // if statement to go here if min order size not met var subtotalRange = subtotalDisplaySheet.getRange(ALast, 27); var sumOfSums = beefSum + sfSum; subtotalRange.setValue(sumOfSums > tier1 ? "Minimum order size not met" : sumOfSums); var totalDiscount = outputInvoicePrice - sumOfSums; subtotalDisplaySheet.getRange(ALast, 29).setValue(totalDiscount); subtotalDisplaySheet.getRange(ALast, 30).setValue(outputInvoicePrice); } } }

筆記

  1. 盡管現代 JavaScript 會幫助你避免這個問題,但你永遠不應該覆蓋任何變量名,除非你確切地知道你為什么這樣做( i++是一個例子,但是函數式風格會說即使這是有問題的)。
  2. 為了可讀性,在 SO 上發布時永遠不要截斷錯誤消息/代碼 - 了解究竟發生了什么對我們幫助您非常寶貴( invoicePrice undefined說明了更多)。

參考

  1. 在 JavaScript 中提升

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM