Komut dosyalarını V8 çalışma zamanına taşıma

Rhino çalışma zamanını kullanan mevcut bir komut dosyanız varsa ve V8 söz dizimini ve özelliklerini kullanmak istiyorsanız komut dosyasını V8'e taşımanız gerekir.

Rhino çalışma zamanı kullanılarak yazılan çoğu komut dosyası, herhangi bir ayarlama yapmadan V8 çalışma zamanı kullanılarak çalışabilir. Bir komut dosyasına V8 söz dizimini ve özelliklerini eklemenin tek ön koşulu genellikle V8 çalışma zamanını etkinleştirmektir.

Ancak V8 çalışma zamanını etkinleştirdikten sonra komut dosyasının başarısız olmasına veya beklenmedik şekilde davranmasına neden olabilecek küçük bir uyumsuzluk ve diğer farklılıklar vardır. Bir komut dosyasını V8'i kullanacak şekilde taşırken komut dosyası projesinde bu sorunları aramanız ve bulduğunuz sorunları düzeltmeniz gerekir.

V8 taşıma prosedürü

Bir komut dosyasını V8'e taşımak için aşağıdaki prosedürü uygulayın:

  1. Komut dosyası için V8 çalışma zamanını etkinleştirin.
  2. Aşağıda listelenen uyumsuzlukları dikkatlice inceleyin. Uyumluluk sorunlarından herhangi birinin olup olmadığını belirlemek için komut dosyanızı inceleyin. Bir veya daha fazla uyumluluk sorunu varsa sorunu gidermek veya önlemek için komut dosyası kodunuzu düzenleyin.
  3. Aşağıda listelenen diğer farklılıkları dikkatlice inceleyin. Listelenen farklılıklardan herhangi birinin kodunuzun davranışını etkileyip etkilemediğini belirlemek için komut dosyanızı inceleyin. Davranışı düzeltmek için komut dosyanızı ayarlayın.
  4. Tespit edilen uyumsuzlukları veya diğer farklılıkları düzelttikten sonra, kodunuzu V8 söz dizimini ve diğer özellikleri istediğiniz gibi kullanacak şekilde güncellemeye başlayabilirsiniz.
  5. Kod ayarlamalarınızı tamamladıktan sonra, beklendiği gibi davrandığından emin olmak için komut dosyanızı ayrıntılı bir şekilde test edin.
  6. Komut dosyanız bir web uygulaması veya yayınlanmış eklenti ise V8 ayarlarını içeren yeni bir sürüm oluşturmanız gerekir. V8 sürümünün kullanıcılara sunulabilmesi için komut dosyasını bu sürümle yeniden yayınlamanız gerekir.

Uyumsuzluk durumları

Orijinal Rhino tabanlı Apps Komut Dosyası çalışma zamanı maalesef birkaç standart dışı ECMAScript davranışına izin veriyordu. V8 standartlara uygun olduğundan bu davranışlar taşıma işleminden sonra desteklenmez. Bu sorunların düzeltilmemesi, V8 çalışma zamanı etkinleştirildikten sonra hatalara veya bozuk komut dosyası davranışına neden olur.

Aşağıdaki bölümlerde bu davranışların her biri ve V8'e geçiş sırasında komut dosyanızı düzeltmek için uygulamanız gereken adımlar açıklanmaktadır.

for each(variable in object) kullanmaktan kaçının

for each (variable in object) ifadesi JavaScript 1.6'ya eklendi ve for...of için kaldırıldı.

Komut dosyanızı V8'e taşırken for each (variable in object) statements kullanmaktan kaçının.

Bunun yerine for (variable in object) kullanın:

// Rhino runtime
var obj = {a: 1, b: 2, c: 3};

// Don't use 'for each' in V8
for each (var value in obj) {
  Logger.log("value = %s", value);
}
      
// V8 runtime
var obj = {a: 1, b: 2, c: 3};

for (var key in obj) {  // OK in V8
  var value = obj[key];
  Logger.log("value = %s", value);
}
      

Date.prototype.getYear() kullanmaktan kaçının

Orijinal Rhino çalışma zamanında, Date.prototype.getYear(), 1900-1999 arasındaki yıllar için iki haneli yıllar, diğer tarihler için ise dört haneli yıllar döndürür. Bu, JavaScript 1.2 ve önceki sürümlerdeki davranıştır.

V8 çalışma zamanında Date.prototype.getYear(), ECMAScript standartlarının gerektirdiği şekilde yılın 1900'den çıkarılmış halini döndürür.

Komut dosyanızı V8'e taşırken her zaman Date.prototype.getFullYear() işlevini kullanın. Bu işlev, tarihten bağımsız olarak dört haneli bir yıl döndürür.

Ayrılmış anahtar kelimeleri ad olarak kullanmaktan kaçının

ECMAScript, işlev ve değişken adlarında belirli ayrılmış anahtar kelimelerin kullanılmasını yasaklar. Rhino çalışma zamanı bu kelimelerin çoğuna izin veriyordu. Dolayısıyla, kodunuzda bu kelimeler kullanılıyorsa işlevlerinizi veya değişkenlerinizi yeniden adlandırmanız gerekir.

Komut dosyanızı V8'e taşırken ayrılmış anahtar kelimelerden birini kullanarak değişken veya işlev adlandırmaktan kaçının. Anahtar kelime adını kullanmaktan kaçınmak için tüm değişkenleri veya işlevleri yeniden adlandırın. Anahtar kelimelerin ad olarak yaygın kullanımları class, import ve export'dir.

const değişkenlerini yeniden atamaktan kaçının

Orijinal Rhino çalışma zamanında, const kullanarak bir değişken tanımlayabilirsiniz. Bu, simgenin değerinin hiçbir zaman değişmediği ve simge için gelecekte yapılacak atamaların yoksayıldığı anlamına gelir.

Yeni V8 çalışma zamanında const anahtar kelimesi standart uyumludur ve const olarak tanımlanan bir değişkene atama yapmak TypeError: Assignment to constant variable çalışma zamanı hatasına neden olur.

Skriptinizi V8'e taşırken const değişkeninin değerini yeniden atamaya çalışmayın:

// Rhino runtime
const x = 1;
x = 2;          // No error
console.log(x); // Outputs 1
      
// V8 runtime
const x = 1;
x = 2;          // Throws TypeError
console.log(x); // Never executed
      

XML değişmez değerleri ve XML nesnesinden kaçının

ECMAScript'e yönelik bu standart dışı uzantı, Apps Script projelerinin doğrudan XML söz dizimini kullanmasına olanak tanır.

Komut dosyanızı V8'e taşırken doğrudan XML değişmez değerleri veya XML nesnesi kullanmaktan kaçının.

Bunun yerine, XML'i ayrıştırmak için XmlService'i kullanın:

// V8 runtime
var incompatibleXml1 = <container><item/></container>;             // Don't use
var incompatibleXml2 = new XML('<container><item/></container>');  // Don't use

var xml3 = XmlService.parse('<container><item/></container>');     // OK
      

__iterator__ kullanarak özel iteratör işlevleri oluşturmayın

JavaScript 1.7, sınıfın prototipinde bir __iterator__ işlevi tanımlayarak herhangi bir sınıfa özel bir iteratör ekleme olanağı sağlayan bir özellik ekledi. Bu özellik, geliştiricilere kolaylık sağlamak amacıyla Apps Script'in Rhino çalışma zamanında da eklendi. Ancak bu özellik hiçbir zaman ECMA-262 standardının bir parçası olmamıştır ve ECMAScript uyumlu JavaScript motorlarından kaldırılmıştır. V8 kullanan komut dosyaları bu iteratör yapısını kullanamaz.

Komut dosyanızı V8'e taşırken özel iteratör oluşturmak için __iterator__ işlevini kullanmaktan kaçının. Bunun yerine ECMAScript 6 iteratörlerini kullanın.

Aşağıdaki dizi oluşumunu ele alalım:

// Create a sample array
var myArray = ['a', 'b', 'c'];
// Add a property to the array
myArray.foo = 'bar';

// The default behavior for an array is to return keys of all properties,
//  including 'foo'.
Logger.log("Normal for...in loop:");
for (var item in myArray) {
  Logger.log(item);            // Logs 0, 1, 2, foo
}

// To only log the array values with `for..in`, a custom iterator can be used.
      

Aşağıdaki kod örnekleri, bir iteratörün Rhino çalışma zamanında nasıl oluşturulabileceğini ve V8 çalışma zamanında nasıl değiştirilen bir iteratör oluşturulabileceğini gösterir:

// Rhino runtime custom iterator
function ArrayIterator(array) {
  this.array = array;
  this.currentIndex = 0;
}

ArrayIterator.prototype.next = function() {
  if (this.currentIndex
      >= this.array.length) {
    throw StopIteration;
  }
  return "[" + this.currentIndex
    + "]=" + this.array[this.currentIndex++];
};

// Direct myArray to use the custom iterator
myArray.__iterator__ = function() {
  return new ArrayIterator(this);
}


Logger.log("With custom Rhino iterator:");
for (var item in myArray) {
  // Logs [0]=a, [1]=b, [2]=c
  Logger.log(item);
}
      
// V8 runtime (ECMAScript 6) custom iterator
myArray[Symbol.iterator] = function() {
  var currentIndex = 0;
  var array = this;

  return {
    next: function() {
      if (currentIndex < array.length) {
        return {
          value: "[${currentIndex}]="
            + array[currentIndex++],
          done: false};
      } else {
        return {done: true};
      }
    }
  };
}

Logger.log("With V8 custom iterator:");
// Must use for...of since
//   for...in doesn't expect an iterable.
for (var item of myArray) {
  // Logs [0]=a, [1]=b, [2]=c
  Logger.log(item);
}
      

Koşullu yakalama yan tümcelerinden kaçının

V8 çalışma zamanı, standartlara uygun olmadığı için catch..if koşullu yakalama yan tümcelerini desteklemez.

Komut dosyanızı V8'e taşırken, catch koşullularını catch gövdesinin içine taşıyın:

// Rhino runtime

try {
  doSomething();
} catch (e if e instanceof TypeError) {  // Don't use
  // Handle exception
}
      
// V8 runtime
try {
  doSomething();
} catch (e) {
  if (e instanceof TypeError) {
    // Handle exception
  }
}

Object.prototype.toSource() kullanmaktan kaçının

JavaScript 1.3, hiçbir zaman ECMAScript standardının parçası olmayan bir Object.prototype.toSource() yöntemi içeriyordu. V8 çalışma zamanında desteklenmez.

Komut dosyanızı V8'e taşırken Object.prototype.toSource() işlevinin tüm kullanımlarını kodunuzdan kaldırın.

Diğer farklılıklar

Komut dosyası hatalarına neden olabilecek yukarıdaki uyumsuzluklara ek olarak, düzeltilmezse beklenmedik V8 çalışma zamanı komut dosyası davranışına neden olabilecek birkaç fark daha vardır.

Aşağıdaki bölümlerde, bu beklenmedik sürprizleri önlemek için komut dosyası kodunuzu nasıl güncelleyeceğiniz açıklanmaktadır.

Yerel ayara özgü tarih ve saat biçimlendirmesini ayarlama

Date toLocaleString(), toLocaleDateString() ve toLocaleTimeString() yöntemleri, V8 çalışma zamanında Rhino'ya kıyasla farklı davranır.

Rhino'da varsayılan biçim uzun biçimdir ve iletilen tüm parametreler yoksayılır.

V8 çalışma zamanında varsayılan biçim kısa biçimdir ve iletilen parametreler ECMA standardına göre işlenir (ayrıntılar için toLocaleDateString() dokümanlarına bakın).

Kodunuzu V8'e taşırken yerel ayara özgü tarih ve saat yöntemlerinin çıkışıyla ilgili kodunuzun beklentilerini test edip düzenleyin:

// Rhino runtime
var event = new Date(
  Date.UTC(2012, 11, 21, 12));

// Outputs "December 21, 2012" in Rhino
console.log(event.toLocaleDateString());

// Also outputs "December 21, 2012",
//  ignoring the parameters passed in.
console.log(event.toLocaleDateString(
    'de-DE',
    { year: 'numeric',
      month: 'long',
      day: 'numeric' }));
// V8 runtime
var event = new Date(
  Date.UTC(2012, 11, 21, 12));

// Outputs "12/21/2012" in V8
console.log(event.toLocaleDateString());

// Outputs "21. Dezember 2012"
console.log(event.toLocaleDateString(
    'de-DE',
    { year: 'numeric',
      month: 'long',
      day: 'numeric' }));
      

Error.fileName ve Error.lineNumber kullanmaktan kaçının

V8 untime'da standart JavaScript Error nesnesi, fileName veya lineNumber'i oluşturucu parametresi ya da nesne özelliği olarak desteklemez.

Komut dosyanızı V8'e taşırken Error.fileName ve Error.lineNumber'e olan tüm bağımlılıkları kaldırın.

Alternatif olarak Error.prototype.stack kullanabilirsiniz. Bu yığın da standart değildir ancak hem Rhino hem de V8'de desteklenir. İki platform tarafından oluşturulan yığın izlemenin biçimi biraz farklıdır:

// Rhino runtime Error.prototype.stack
// stack trace format
at filename:92 (innerFunction)
at filename:97 (outerFunction)


// V8 runtime Error.prototype.stack
// stack trace format
Error: error message
at innerFunction (filename:92:11)
at outerFunction (filename:97:5)
      

Dize haline getirilmiş enum nesnelerinin işlenmesini ayarlama

Orijinal Rhino çalışma zamanında, bir enum nesnesinde JavaScript JSON.stringify() yöntemini kullandığınızda yalnızca {} döndürülür.

V8'de, bir enum nesnesinde aynı yöntemi kullanmak enum adını döndürür.

Kodunuzu V8'e taşırken, kodunuzun enum nesnelerinde JSON.stringify() değerinin çıkışıyla ilgili beklentilerini test edip düzenleyin:

// Rhino runtime
var enumName =
  JSON.stringify(Charts.ChartType.BUBBLE);

// enumName evaluates to {}
// V8 runtime
var enumName =
  JSON.stringify(Charts.ChartType.BUBBLE);

// enumName evaluates to "BUBBLE"

Tanımlanmamış parametrelerin işlenmesi

Orijinal Rhino çalışma zamanında, undefined parametresi bir yönteme iletildiğinde "undefined" dizesi bu yönteme iletiliyordu.

V8'de yöntemlere undefined göndermek, null göndermeye eşdeğerdir.

Komut dosyanızı V8'e taşırken, kodunuzun undefined parametreleriyle ilgili beklentilerini test edip düzenleyin:

// Rhino runtime
SpreadsheetApp.getActiveRange()
    .setValue(undefined);

// The active range now has the string
// "undefined"  as its value.
      
// V8 runtime
SpreadsheetApp.getActiveRange()
    .setValue(undefined);

// The active range now has no content, as
// setValue(null) removes content from
// ranges.

Global this öğesinin işlenmesini ayarlama

Rhino çalışma zamanı, onu kullanan komut dosyaları için gizli bir özel bağlam tanımlar. Komut dosyası kodu, gerçek global this'ten farklı olarak bu gizli bağlamda çalışır. Bu, koddaki "global this" referanslarının aslında yalnızca komut dosyasında tanımlanan kodu ve değişkenleri içeren özel bağlamda değerlendirildiği anlamına gelir. Yerleşik Apps Komut Dosyası hizmetleri ve ECMAScript nesneleri, this'ün bu kullanımından hariç tutulur. Bu durum şu JavaScript yapısına benziyordu:

// Rhino runtime

// Apps Script built-in services defined here, in the actual global context.
var SpreadsheetApp = {
  openById: function() { ... }
  getActive: function() { ... }
  // etc.
};

function() {
  // Implicit special context; all your code goes here. If the global this
  // is referenced in your code, it only contains elements from this context.

  // Any global variables you defined.
  var x = 42;

  // Your script functions.
  function myFunction() {
    ...
  }
  // End of your code.
}();

V8'de, örtülü özel bağlam kaldırılır. Komut dosyasında tanımlanan global değişkenler ve işlevler, yerleşik Apps Komut Dosyası hizmetlerinin ve Math ile Date gibi ECMAScript yerleşiklerinin yanında global bağlama yerleştirilir.

Komut dosyanızı V8'e taşırken, kodunuzun this'un genel bağlamda kullanımıyla ilgili beklentilerini test edip düzenleyin. Çoğu durumda farklılıklar yalnızca kodunuz genel this nesnesinin anahtarlarını veya mülk adlarını inceliyorsa belirgindir:

// Rhino runtime
var myGlobal = 5;

function myFunction() {

  // Only logs [myFunction, myGlobal];
  console.log(Object.keys(this));

  // Only logs [myFunction, myGlobal];
  console.log(
    Object.getOwnPropertyNames(this));
}





      
// V8 runtime
var myGlobal = 5;

function myFunction() {

  // Logs an array that includes the names
  // of Apps Script services
  // (CalendarApp, GmailApp, etc.) in
  // addition to myFunction and myGlobal.
  console.log(Object.keys(this));

  // Logs an array that includes the same
  // values as above, and also includes
  // ECMAScript built-ins like Math, Date,
  // and Object.
  console.log(
    Object.getOwnPropertyNames(this));
}

Kitaplıklarda instanceof öğelerinin işlenmesini ayarlama

Başka bir projedeki işlevde parametre olarak iletilen bir nesnede kitaplıkta instanceof kullanılması yanlış negatif sonuç verebilir. V8 çalışma zamanında, bir proje ve kitaplıkları farklı yürütme bağlamlarında çalıştırılır ve bu nedenle farklı genel değişkenler ve prototip zincirleri vardır.

Bunun yalnızca kitaplığınız projenizde oluşturulmayan bir nesnede instanceof kullanıyorsa geçerli olduğunu unutmayın. Projenizde oluşturulan bir nesnede (projenizdeki aynı veya farklı bir komut dosyasında) kullanıldığında beklendiği gibi çalışır.

V8'de çalışan bir proje, komut dosyanızı kitaplık olarak kullanıyorsa komut dosyanızı başka bir projeden iletilecek bir parametrede instanceof kullanıp kullanmadığını kontrol edin. instanceof'ün kullanımını ayarlayın ve kullanım alanınıza göre uygun diğer alternatifleri kullanın.

a instanceof b için bir alternatif, prototip zincirinin tamamını aramanız gerekmeyen ve yalnızca kurucuyu kontrol etmeniz gereken durumlarda a kurucusunu kullanmak olabilir. Kullanım: a.constructor.name == "b"

A Projesi'nin B Projesi'ni kitaplık olarak kullandığı A Projesi ile B Projesi'ni düşünün.

//Rhino runtime

//Project A

function caller() {
   var date = new Date();
   // Returns true
   return B.callee(date);
}

//Project B

function callee(date) {
   // Returns true
   return(date instanceof Date);
}

      
//V8 runtime

//Project A

function caller() {
   var date = new Date();
   // Returns false
   return B.callee(date);
}

//Project B

function callee(date) {
   // Incorrectly returns false
   return(date instanceof Date);
   // Consider using return (date.constructor.name ==
   // “Date”) instead.
   // return (date.constructor.name == “Date”) -> Returns
   // true
}

Diğer bir alternatif de ana projede instanceof değerini kontrol eden bir işlev tanıtmak ve bir kitaplık işlevi çağırırken diğer parametrelere ek olarak bu işlevi iletmek olabilir. İletilen işlev, kitaplıktaki instanceof öğesini kontrol etmek için kullanılabilir.

//V8 runtime

//Project A

function caller() {
   var date = new Date();
   // Returns True
   return B.callee(date, date => date instanceof Date);
}

//Project B

function callee(date, checkInstanceOf) {
  // Returns True
  return checkInstanceOf(date);
}
      

Paylaşılmayan kaynakların kitaplıklara aktarılmasını ayarlama

Paylaşılmayan bir kaynağı ana komut dosyasından bir kitaplığa aktarma işlemi, V8 çalışma zamanında farklı şekilde çalışır.

Rhino çalışma zamanında, paylaşılmayan bir kaynak iletme işlemi çalışmaz. Kitaplık bunun yerine kendi kaynağını kullanır.

V8 çalışma zamanında, paylaşılmayan bir kaynağı kitaplığa aktarmak işe yarar. Kitaplık, iletilen paylaşılmayan kaynağı kullanır.

Paylaşılmayan kaynakları işlev parametresi olarak iletmeyin. Paylaşılmayan kaynakları her zaman, bunları kullanan komut dosyasında tanımlayın.

A Projesi'nin B Projesi'ni kitaplık olarak kullandığı A Projesi ile B Projesi'ni düşünün. Bu örnekte PropertiesService, paylaşılmayan bir kaynaktır.

// Rhino runtime
// Project A
function testPassingNonSharedProperties() {
  PropertiesService.getScriptProperties()
      .setProperty('project', 'Project-A');
  B.setScriptProperties();
  // Prints: Project-B
  Logger.log(B.getScriptProperties(
      PropertiesService, 'project'));
}

//Project B function setScriptProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-B'); } function getScriptProperties( propertiesService, key) { return propertiesService.getScriptProperties() .getProperty(key); }

// V8 runtime
// Project A
function testPassingNonSharedProperties() {
  PropertiesService.getScriptProperties()
      .setProperty('project', 'Project-A');
  B.setScriptProperties();
  // Prints: Project-A
  Logger.log(B.getScriptProperties(
      PropertiesService, 'project'));
}

// Project B function setProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-B'); } function getScriptProperties( propertiesService, key) { return propertiesService.getScriptProperties() .getProperty(key); }

Bağımsız komut dosyalarına erişimi güncelleme

V8 çalışma zamanında çalışan bağımsız komut dosyalarında, komut dosyasının tetikleyicilerinin düzgün çalışması için kullanıcılara komut dosyasına en az görüntüleme erişimi sağlamanız gerekir.