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 komut dosyalarının çoğu, herhangi bir ayarlama yapılmadan V8 çalışma zamanı kullanarak çalışabilir. Genellikle bir komut dosyasına V8 söz dizimini ve özelliklerini 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 bir şekilde davranmasına neden olabilecek küçük uyumsuzluklar ve başka farklılıklar vardır. Bir komut dosyasını V8 kullanacak şekilde taşırken, komut dosyası projesinde bu sorunları aramanız ve bulduğunuz çözümleri 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. Uyumsuzluklardan herhangi birinin olup olmadığını belirlemek için komut dosyanızı inceleyin. Bir veya daha fazla uyumsuzluk varsa, sorunu kaldırmak 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ı düzenleyin.
  4. Tespit edilen uyumsuzlukları veya diğer farklılıkları düzelttikten sonra, kodunuzu istediğiniz şekilde V8 söz dizimini ve diğer özellikleri kullanacak şekilde güncellemeye başlayabilirsiniz.
  5. Kod ayarlamalarınızı bitirdikten sonra, beklendiği gibi işlediğinden emin olmak için komut dosyanızı kapsamlı bir şekilde test edin.
  6. Komut dosyanız bir web uygulaması veya yayınlanmış bir eklenti ise V8 düzenlemeleriyle komut dosyasının yeni bir sürümünü oluşturmanız gerekir. V8 sürümünü kullanıcılara sunmak için komut dosyasını bu sürümle yeniden yayınlamanız gerekir.

Uyumsuzluklar

Orijinal Rhino tabanlı Apps Komut Dosyası çalışma zamanı, maalesef bazı standart olmayan ECMAScript davranışına izin vermiştir. 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 komut dosyası kodunuzu V8'e taşıma sırasında düzeltmek için uygulamanız gereken adımlar açıklanmaktadır.

Kaçınma: for each(variable in object)

for each (variable in object) ifadesi JavaScript 1.6'ya eklenmiş ve for...of yerine kaldırılmıştır.

Komut dosyanızı V8'e taşırken for each (variable in object) ifadeleri 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);
}
      

Kaçınma: Date.prototype.getYear()

Orijinal Rhino çalışma zamanında Date.prototype.getYear(), 1900-1999 arasındaki yıllar için iki basamaklı yıllar değerini, diğer tarihler için ise dört basamaklı yıl değerini döndürür. Bu, JavaScript 1.2 ve önceki sürümlerde olduğu gibidir.

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 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ına izin vermez. Rhino çalışma zamanı bu kelimelerin birçoğuna izin verir. Bu nedenle, kodunuz bunları kullanıyorsa 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ırmayın. Anahtar kelime adını kullanmaktan kaçınmak için 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şkeni 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 gelecekte yapılacak atamaların yok sayıldığı anlamına gelir.

Yeni V8 çalışma zamanında, const anahtar kelimesi standartla uyumludur ve const olarak tanımlanan bir değişkene atanması, TypeError: Assignment to constant variable çalışma zamanı hatasıyla sonuçlanır.

Komut dosyanızı 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ğerlerinden ve XML nesnesinden kaçının

ECMAScript'e yapılan bu standart olmayan uzantı, 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'yi 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 yineleyici işlevleri oluşturmayın

JavaScript 1.7, herhangi bir sınıfın prototipinde bir __iterator__ işlevi tanımlayarak herhangi bir klas öğesine özel yineleyici eklenmesine olanak tanıyan bir özellik ekledi. Bu işlev, geliştiricilere kolaylık sağlamak için 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ğildir ve ECMAScript uyumlu JavaScript motorlarında kaldırılmıştır. V8 kullanan komut dosyaları, bu yineleme yapısını kullanamaz.

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

Aşağıdaki 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 iterasyonun nasıl oluşturulabileceğini ve V8 çalışma zamanında bir yedek yinelemenin nasıl oluşturulacağını 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çınma

V8 çalışma zamanı, standartla uyumlu olmadıkları için catch..if koşullu yakalama yan tümcelerini desteklemez.

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

Diğer farklar

Komut dosyası hatalarına neden olabilecek yukarıdaki uyumsuzluklara ek olarak, düzeltilmemesi halinde 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ürprizleri önlemek 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 Rhino'ya kıyasla V8 çalışma zamanında farklı şekilde davranır.

Rhino'da varsayılan biçim uzun biçimdir ve aktarılan tüm parametreler ignoreed (yoksayılır) biçimindedir.

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

Komut dosyanızı V8'e taşırken, yerel ayara özel tarih ve saat yöntemlerinin çıktısıyla ilgili olarak kodunuzun 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 zaman aşımında, standart JavaScript Error nesnesi, oluşturucu parametreleri veya nesne özellikleri olarak fileName ya da lineNumber öğelerini desteklemez.

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

Alternatif olarak, Error.prototype.stack kullanılabilir. 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)
      

Dizeleştirilmiş enum nesnelerinin işlenmesini ayarlama

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

V8'de, bir enum nesnesinde aynı yöntemin kullanılması enum adını yeniden döndürür.

Komut dosyanızı V8'e taşırken kodunuzun enum nesnelerinin çıktısıyla ilgili JSON.stringify() beklentilerini test edin ve ayarlayın:

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

Orijinal Rhino çalışma zamanında, undefined parametresini bir yönteme geçirmek, "undefined" dizesinin bu yönteme geçirilmesiyle sonuçlanır.

V8'de undefined yöntemlerinin geçirilmesi, null öğesinin geçirilmesiyle 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.

Genel this öğesinin kullanımını ayarlayın

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

// 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, yerleşik Apps Komut Dosyası hizmetlerinin ve Math ile Date gibi yerleşik ECMAScript öğelerinin yanına, genel bağlamda yerleştirilir.

Komut dosyanızı V8'e taşırken kodunuzun küresel bağlamda this kullanımıyla ilgili beklentilerini test edin ve ayarlayın. Çoğu durumda, farklar yalnızca kodunuz genel this nesnesinin anahtarlarını veya özellik adlarını incelerse görünü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));
}

Kitaplıklarda instanceof öğesinin işlenmesini ayarlayın

Kitaplıkta, başka bir projedeki bir işlevde parametre olarak geçirilen nesnedeki instanceof kullanımı yanlış negatif 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 ve prototip zincirleri olur.

Bu durumun, yalnızca kitaplığınızda instanceof projesinin projenizde oluşturulmamış bir nesnede kullanılması durumunda geçerli olduğunu unutmayın. Bu anahtar kelimeyi projenizin içinde oluşturulan bir nesne üzerinde, projenizin içindeki aynı veya farklı bir komut dosyasında kullanmak beklendiği gibi işlemelidir.

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 göre diğer uygun alternatifleri kullanın.

a instanceof b için bir alternatif, prototip zincirinin tamamında arama yapmanız ve yalnızca oluşturucuyu kontrol etmeniz gerekmediği durumlarda a oluşturucuyu kullanmak olabilir. Kullanım: a.constructor.name == "b"

A Projesinin ve B Projesinin kütüphane olarak kullandığı proje A'yı 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 işlevini kontrol eden bir işlev sunmak ve bir kitaplık işlevini çağırırken bu işlevi diğer parametrelere ek olarak iletmek olabilir. İletilen işlev daha sonra kitaplık içinde instanceof işlevini 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 iletilmesini ayarla

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

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

V8 çalışma zamanında, paylaşılmayan bir kaynağı kitaplığa iletmek 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 komut dosyasında bildirin.

A Projesinin ve B Projesinin kütüphane olarak kullandığı proje A'yı 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ına görüntüleme erişimi sağlamanız gerekir.