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 dizimi ile özelliklerinden yararlanmak istiyorsanız komut dosyasını V8'e taşımanız gerekir.

Rhino çalışma zamanı kullanılarak yazılan çoğu komut dosyası, ayarlama yapılmadan bir V8 çalışma zamanı kullanarak çalışabilir. Genellikle bir komut dosyasına V8 söz dizimi ve özellik eklemenin tek ön koşulu, V8 çalışma zamanını etkinleştirmektir.

Bununla birlikte, V8 çalışma zamanı etkinleştirildikten sonra komut dosyasının başarısız olmasına veya beklenmedik şekilde davranmasına neden olabilecek küçük bir grup uyumsuzluk ve başka farklılıklar da vardır. Bir komut dosyasını V8 kullanmak üzere 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 şu prosedürü uygulayın:

  1. Komut dosyası için V8 çalışma zamanını etkinleştirin.
  2. Aşağıda listelenen uyumsuzlukları dikkatlice inceleyin. Herhangi bir uyumsuzluk olup olmadığını belirlemek için komut dosyanızı inceleyin. Bir veya daha fazla uyumsuzluk varsa sorunu gidermek ya da önlemek için komut dosyası kodunuzu ayarlayın.
  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. Komut dosyanızı bu davranışı düzeltecek şekilde düzenleyin.
  4. Tespit edilen uyumsuzlukları veya diğer farklılıkları düzelttikten sonra, V8 söz dizimini ve diğer özellikleri istediğiniz gibi kullanmak için kodunuzu güncellemeye başlayabilirsiniz.
  5. Kod ayarlamalarını bitirdikten sonra, beklendiği gibi çalıştığından emin olmak için komut dosyanızı kapsamlı bir şekilde test edin.
  6. Komut dosyanız bir web uygulaması veya yayınlanmış eklenti ise V8 düzenlemeleriyle komut dosyasının yeni bir sürümünü oluşturmanız gerekir. Kullanıcıların V8 sürümünü kullanabilmesini sağlamak için komut dosyasını bu sürümle yeniden yayınlamanız gerekir.

Uyumsuzluklar

Rhino tabanlı orijinal Apps Komut Dosyası çalışma zamanı maalesef bazı standart olmayan ECMAScript davranışlarına izin veriyordu. V8 standartlara uygun olduğundan taşıma işleminden sonra bu davranışlar 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 taşıma sırasında komut dosyası kodunuzu 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'in lehine kaldırıldı.

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

Bunun yerine for (variable in object) işlevini 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() işlevi, 1900-1999 arasındaki yıllar için iki basamaklı yılları, JavaScript 1.2 ve önceki sürümlerde kullanılan diğer tarihler için ise dört basamaklı yıl olarak döndürür.

V8 çalışma zamanında Date.prototype.getYear(), ECMAScript standartlarının gerektirdiği şekilde yıl eksi 1900 değerini döndürür.

Komut dosyanızı V8'e taşırken daima Date.prototype.getFullYear() kullanın. Bu işlem, tarihten bağımsız olarak dört basamaklı 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 birçoğuna izin veriyordu. Bu nedenle, kodunuzda bu kelimelerin kullanılması durumunda işlevlerinizi veya değişkenlerinizi yeniden adlandırmanız gerekir.

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

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

Orijinal Rhino çalışma zamanında, const kullanarak bir değişken bildirebilirsiniz. Bu, simgenin değerinin hiçbir zaman değişmediği ve sembole ileride yapılacak atamalar yoksayılacağı anlamına gelir.

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

Komut dosyanızı V8'e taşırken bir const değişkeninin değerini yeniden atamayı denemeyin:

// 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
      

Değişmez XML değerlerinden ve XML nesnesinden kaçının.

ECMAScript'in bu standart dışı uzantısı, Apps Komut Dosyası 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ğerlerini veya XML nesnesini 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 yineleme işlevleri derlemeyin

JavaScript 1.7'de, ilgili sınıfın prototipinde bir __iterator__ işlevi tanımlayarak tüm sınıflara özel iteratörler eklemeye olanak tanıyan bir özellik eklendi. Bu özellik, geliştiricilere kolaylık sağlamak amacıyla Apps Komut Dosyası'nın Rhino çalışma zamanına da eklendi. Ancak bu özellik hiçbir zaman ECMA-262 standardının bir parçası değildi ve ECMAScript uyumlu JavaScript motorlarında kaldırılmıştı. V8 kullanan komut dosyaları, bu yineleme yapısını kullanamaz.

Komut dosyanızı V8'e taşırken özel yinelemeler oluşturmak için __iterator__ işlevinden kaçının. Bunun yerine ECMAScript 6 yinelemelerini kullanın.

Şu dizi yapısını göz önünde bulundurun:

// 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, Rhino çalışma zamanında bir iteratörün nasıl oluşturulabileceğini ve V8 çalışma zamanında yedek bir iteratörün nasıl oluşturulacağını göstermektedir:

// 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 ifadeleri kullanmaktan kaçının

V8 çalışma zamanı, standartlarla uyumlu olmadığı için catch..if koşullu yakalama deyimlerini desteklemez.

Komut dosyanızı V8'e taşırken, yakalama gövdesindeki yakalama koşullarını 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 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 kodunuzdan tüm Object.prototype.toSource() kullanımlarını 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ç farklılık daha vardır.

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

Yerel ayara özel tarih ve saat biçimlendirmesini ayarla

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

Rhino'da varsayılan biçim uzun biçimdir ve geçirilen 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() belgelerine bakın).

Komut dosyanızı V8'e taşırken kodunuzun yerel ayara özel tarih ve saat yöntemlerinin çıkışına ilişkin beklentilerini test edin ve ayarlayın:

// 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 zamanlamasında standart JavaScript Error nesnesi, kurucu parametre veya nesne özelliği olarak fileName ya da lineNumber öğelerini desteklemez.

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

Alternatif olarak Error.prototype.stack kullanılabilir. Bu yığın da standart dışıdır ancak hem Rhino hem de V8'de desteklenir. Bu iki platformun oluşturduğu 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)
      

Dizeleştirilmiş enum nesnelerinin işlenmesini ayarlama

Orijinal Rhino çalışma zamanında, bir numaralandırma nesnesinde JavaScript JSON.stringify() yöntemi yalnızca {} değerini döndürür.

V8'de, bir numaralandırma nesnesinde aynı yöntemin kullanılması enum adını geri alır.

Komut dosyanızı V8'e taşırken kodunuzun enum nesnelerinde JSON.stringify() çıkışıyla ilgili beklentilerini test edin ve 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şlenmesini ayarlayın

Orijinal Rhino çalışma zamanında, undefined parametresinin parametre olarak bir yönteme geçirilmesi, "undefined" dizesinin söz konusu yönteme iletilmesiyle sonuçlandı.

V8'de, undefined yönteminin yöntemlere aktarılması, null değerinin iletilmesiyle eşdeğerdir.

Komut dosyanızı V8'e taşırken kodunuzun undefined parametreleriyle ilgili beklentilerini test edin ve ayarlayın:

// 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 için işlenmeyi ayarlayın

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

// 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 genel değişkenler ve işlevler, genel bağlama, yerleşik Apps Komut Dosyası hizmetlerinin ve Math ile Date gibi ECMAScript yerleşiklerinin yanına yerleştirilir.

Komut dosyanızı V8'e taşırken kodunuzun küresel bağlamda this kullanımına ilişkin beklentilerini test edin ve ayarlayın. Çoğu durumda farklılıklar yalnızca kodunuzda global this nesnesinin anahtarları veya özellik adları incelendiğinde görülür:

// 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));
}

instanceof öğesinin kitaplıklarda işlenmesini düzenleyin

Başka bir projedeki işlevde parametre olarak iletilen bir nesnede instanceof özelliğinin kullanılması yanlış negatif sonuçlar verebilir. V8 çalışma zamanında bir proje ve kitaplıkları farklı yürütme bağlamlarında çalıştırıldığından farklı global anahtarlar ve prototip zincirleri bulunur.

Bu durumun yalnızca kitaplığınız projenizde oluşturulmamış bir nesnede instanceof kullandığında geçerli olduğunu unutmayın. Projenizde oluşturulan bir nesnede, ister aynı ister farklı bir komut dosyası içinde olsun, bu komutun beklendiği gibi çalışması beklenir.

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

Prototip zincirinin tamamını aramanıza gerek olmadığı ve sadece oluşturucuyu kontrol etmenizin gerekmediği durumlarda, a instanceof b için a oluşturucusunu kullanmak da bir alternatif olabilir. Kullanım: a.constructor.name == "b"

A Projesi’nin kitaplık olarak kullanıldığı A ve B projelerini 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
}

Başka bir alternatif de, ana projede instanceof öğesini kontrol eden bir işlev sunmak ve bir kitaplık işlevini çağırırken diğer parametrelere ek olarak bu işlevi iletmek de olabilir. İletilen işlev, daha sonra kitaplıkta 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 iletimini ayarlama

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

Rhino çalışma zamanında paylaşılmayan bir kaynağın iletilmesi işe yaramaz. Kitaplık bunun yerine kendi kaynağını kullanır.

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

Paylaşılmayan kaynakları işlev parametreleri olarak iletmeyin. Paylaşılmayan kaynakları her zaman, onları kullanan aynı komut dosyasında bildirin.

A Projesi’nin kitaplık olarak kullanıldığı A ve B projelerini 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ı tetikleyicilerinin düzgün çalışması için kullanıcılara en azından komut dosyası görüntüleme erişimi sağlamanız gerekir.