Nếu bạn đã có sẵn một tập lệnh bằng môi trường thời gian chạy Rhino và muốn sử dụng Với cú pháp và tính năng của V8, bạn phải di chuyển tập lệnh sang V8.
Hầu hết các tập lệnh được viết bằng môi trường thời gian chạy Rhino đều có thể hoạt động bằng thời gian chạy V8 mà không cần điều chỉnh. Thông thường, điều kiện tiên quyết duy nhất để thêm cú pháp V8 và các tính năng của một tập lệnh bật thời gian chạy V8.
Tuy nhiên, có một nhóm nhỏ không tương thích và những điểm khác biệt khác có thể dẫn đến tập lệnh gặp lỗi hoặc hoạt động không như mong đợi sau khi bật thời gian chạy V8. Khi bạn di chuyển tập lệnh để sử dụng V8, bạn phải tìm kiếm trong dự án tập lệnh cho các vấn đề này và sửa bất kỳ nội dung nào bạn tìm thấy.
Quy trình di chuyển V8
Để di chuyển một tập lệnh sang V8, hãy làm theo quy trình sau:
- Bật thời gian chạy V8 cho tập lệnh.
- Xem xét kỹ các vấn đề không tương thích được liệt kê bên dưới. Kiểm tra tập lệnh của bạn để xác định xem có bất kỳ có sự không tương thích; nếu có một hoặc nhiều điểm không tương thích, điều chỉnh mã tập lệnh của bạn để loại bỏ hoặc tránh sự cố.
- Hãy xem kỹ các điểm khác biệt khác trong danh sách dưới đây. Kiểm tra kịch bản của bạn để xác định xem có bất kỳ sự khác biệt được liệt kê nào ảnh hưởng đến hành vi của mã. Hãy điều chỉnh tập lệnh của bạn để khắc phục hành vi.
- Sau khi bạn khắc phục mọi vấn đề không tương thích đã phát hiện được hoặc các vấn đề khác điểm khác biệt, bạn có thể bắt đầu cập nhật mã để sử dụng Cú pháp V8 và các tính năng khác như mong muốn.
- Sau khi hoàn tất việc điều chỉnh mã, hãy kiểm tra kỹ tập lệnh của bạn để đảm bảo ứng dụng hoạt động như dự kiến.
- Nếu tập lệnh của bạn là một ứng dụng web hoặc đã xuất bản tiện ích bổ sung, bạn phải tạo phiên bản mới của tập lệnh có các điều chỉnh V8. Để phiên bản V8 có sẵn cho người dùng, bạn phải xuất bản lại tập lệnh cùng với phiên bản này.
Không tương thích
Rất tiếc, môi trường thời gian chạy Apps Script ban đầu dựa trên Rhino đã cho phép một số các hành vi ECMAScript không chuẩn. Vì V8 tuân thủ tiêu chuẩn, nên không được hỗ trợ sau khi di chuyển. Không khắc phục được các vấn đề này dẫn đến lỗi hoặc hành vi của tập lệnh bị hỏng sau khi bật môi trường thời gian chạy V8.
Các phần sau đây mô tả từng hành vi trong số này và các bước bạn phải thực hiện để sửa mã tập lệnh của bạn trong quá trình di chuyển sang V8.
Tránh for each(variable in object)
Chiến lược phát hành đĩa đơn
for each (variable in object)
đã được thêm vào JavaScript 1.6 và bị xóa thay cho for...of
.
Khi di chuyển tập lệnh sang V8, bạn nên tránh sử dụng for each (variable in object)
câu lệnh.
Thay vào đó, hãy sử dụng for (variable in object)
:
// 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); } |
Tránh Date.prototype.getYear()
Trong thời gian chạy Rhino ban đầu,
Date.prototype.getYear()
trả về năm có hai chữ số cho các năm từ 1900-1999, nhưng trả về năm có bốn chữ số cho các năm khác
ngày, là hành vi trong JavaScript 1.2 trở về trước.
Trong thời gian chạy V8,
Date.prototype.getYear()
trả về năm trừ 1900 thay vì theo yêu cầu của
Các tiêu chuẩn ECMAScript.
Khi di chuyển tập lệnh của bạn sang V8, hãy luôn sử dụng
Date.prototype.getFullYear()
,
Hàm này trả về năm gồm bốn chữ số bất kể ngày.
Tránh sử dụng các từ khoá dành riêng làm tên
ECMAScript cấm sử dụng một số từ khoá đặt trước trong tên hàm và tên biến. Môi trường thời gian chạy Tê giác cho phép nhiều từ trong số này, vì vậy, nếu mã của bạn sử dụng các hàm hoặc biến đó, bạn phải đổi tên các hàm hoặc biến của mình.
Khi di chuyển tập lệnh sang phiên bản 8, hãy tránh đặt tên cho biến hoặc hàm
sử dụng một trong
từ khoá đặt trước.
Đổi tên bất kỳ biến hoặc hàm nào để tránh sử dụng tên từ khoá. Các cách sử dụng phổ biến
từ khóa dưới dạng tên là class
, import
và export
.
Tránh chỉ định lại các biến const
Trong thời gian chạy Rhino ban đầu, bạn có thể khai báo một biến bằng const
có nghĩa là giá trị của biểu tượng không bao giờ thay đổi và các chỉ định trong tương lai cho
sẽ bị bỏ qua.
Trong thời gian chạy V8 mới, từ khoá const
tuân thủ tiêu chuẩn và gán
với một biến được khai báo là const
sẽ dẫn đến
Lỗi thời gian chạy TypeError: Assignment to constant variable
.
Khi di chuyển tập lệnh của bạn sang V8, đừng cố gắng chỉ định lại giá trị của
biến const
:
// 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 |
Tránh sử dụng các giá trị cố định XML và đối tượng XML
Chiến dịch này phần mở rộng không chuẩn sang ECMAScript cho phép các dự án Apps Script trực tiếp sử dụng cú pháp XML.
Khi di chuyển tập lệnh của bạn sang V8, hãy tránh sử dụng các giá trị cố định XML trực tiếp hoặc XML đối tượng.
Thay vào đó, hãy sử dụng XmlService để phân tích cú pháp XML:
// 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 |
Không tạo hàm lặp tuỳ chỉnh bằng __iterator__
JavaScript 1.7 thêm một tính năng cho phép thêm một biến lặp tuỳ chỉnh vào mọi clas
bằng cách khai báo một hàm __iterator__
trong nguyên mẫu của lớp đó; đây là
cũng được thêm vào thời gian chạy Rhino của Apps Script để thuận tiện cho nhà phát triển. Tuy nhiên,
tính năng này chưa bao giờ nằm trong
Tiêu chuẩn ECMA-262
và đã bị xoá trong các công cụ JavaScript tuân thủ ECMAScript. Tập lệnh sử dụng V8
không thể sử dụng cấu trúc biến lặp này.
Khi di chuyển tập lệnh sang V8, hãy tránh sử dụng hàm __iterator__
để tạo bản dựng
biến lặp tuỳ chỉnh. Thay vào đó,
hãy sử dụng trình lặp ECMAScript 6.
Hãy cân nhắc việc tạo mảng sau:
// 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. |
Các mã ví dụ sau đây minh hoạ cách tạo một biến lặp trong Thời gian chạy Tê giác và cách tạo trình lặp thay thế trong thời gian chạy V8:
// 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); } |
Tránh các mệnh đề bắt có điều kiện
Thời gian chạy V8 không hỗ trợ các mệnh đề bắt có điều kiện catch..if
vì các mệnh đề này
không tuân thủ tiêu chuẩn.
Khi di chuyển tập lệnh của bạn sang V8, hãy di chuyển mọi điều kiện bắt bên trong nội dung ghi lại:
// 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 } } |
Tránh sử dụng Object.prototype.toSource()
JavaScript 1.3 có chứa Object.prototype.toSource() chưa từng là một phần của bất kỳ tiêu chuẩn ECMAScript nào. Không được hỗ trợ trong thời gian chạy V8.
Khi di chuyển tập lệnh của bạn sang V8, hãy ngừng sử dụng Object.prototype.toSource() từ mã của bạn.
Điểm khác biệt khác
Ngoài các điểm không tương thích nêu trên có thể gây ra lỗi tập lệnh, là một số khác biệt khác mà nếu không được khắc phục có thể dẫn đến V8 hành vi của tập lệnh trong thời gian chạy.
Các phần sau giải thích cách cập nhật mã tập lệnh của bạn để tránh những trường hợp này những bất ngờ bất ngờ.
Điều chỉnh định dạng ngày và giờ theo ngôn ngữ cụ thể
Date
phương thức toLocaleString()
,
toLocaleDateString()
!
và toLocaleTimeString()
hoạt động
khác với Rhino trong thời gian chạy V8.
Trong Rhino, định dạng mặc định là định dạng dài và mọi tham số được truyền vào bị bỏ qua.
Trong thời gian chạy V8, định dạng mặc định là định dạng ngắn và tham số
được xử lý theo tiêu chuẩn ECMA (xem
Tài liệu về toLocaleDateString()
để biết chi tiết).
Khi di chuyển tập lệnh sang V8, hãy kiểm thử và điều chỉnh kỳ vọng của mã liên quan đến kết quả của phương thức ngày và giờ theo từng ngôn ngữ cụ thể:
// 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' })); |
Tránh sử dụng Error.fileName
và Error.lineNumber
Trong thời gian chờ V8, JavaScript chuẩn
Error
đối tượng không hỗ trợ fileName
hoặc lineNumber
làm tham số hàm khởi tạo
hoặc thuộc tính đối tượng.
Khi di chuyển tập lệnh sang V8,
loại bỏ mọi sự phụ thuộc vào Error.fileName
và Error.lineNumber
.
Một cách thay thế là sử dụng
Error.prototype.stack
.
Ngăn xếp này cũng không theo chuẩn, nhưng được hỗ trợ trong cả Rhino và V8. Chiến lược phát hành đĩa đơn
định dạng của dấu vết ngăn xếp do hai nền tảng tạo ra hơi khác nhau:
// 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) |
Điều chỉnh cách xử lý các đối tượng enum dạng chuỗi
Trong thời gian chạy Rhino ban đầu, việc sử dụng JavaScript
JSON.stringify()
trên đối tượng enum chỉ trả về {}
.
Trong V8, việc sử dụng cùng một phương thức trên đối tượng enum sẽ bật lại tên enum.
Khi di chuyển tập lệnh sang V8,
kiểm thử và điều chỉnh kỳ vọng của mã về kết quả của
JSON.stringify()
trên đối tượng enum:
// 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" |
Điều chỉnh việc xử lý các tham số không xác định
Trong thời gian chạy Rhino ban đầu, hãy truyền undefined
đến một phương thức dưới dạng tham số
dẫn đến việc chuyển chuỗi "undefined"
đến phương thức đó.
Trong V8, việc truyền undefined
vào các phương thức tương đương với việc truyền null
.
Khi di chuyển tập lệnh sang V8,
kiểm thử và điều chỉnh kỳ vọng của mã về tham số undefined
:
// 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. |
Điều chỉnh quá trình xử lý this
chung
Môi trường thời gian chạy Rhino xác định một ngữ cảnh đặc biệt ngầm ẩn cho các tập lệnh sử dụng bối cảnh đó.
Mã tập lệnh chạy trong ngữ cảnh ngầm ẩn này, khác với mã toàn cục thực tế
this
. Điều này có nghĩa là các tham chiếu đến "this
chung" trong mã
đánh giá theo ngữ cảnh đặc biệt, trong đó chỉ chứa mã và các biến
được xác định trong tập lệnh. Các dịch vụ Apps Script và đối tượng ECMAScript tích hợp sẵn
không được phép sử dụng this
này. Trường hợp này tương tự như trường hợp này
Cấu trúc JavaScript:
// 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. }(); |
Trong V8, ngữ cảnh đặc biệt ngầm ẩn sẽ bị xoá. Biến và hàm toàn cục
được xác định trong tập lệnh được đặt trong ngữ cảnh chung, bên cạnh đoạn mã
Các dịch vụ Apps Script và ECMAScript tích hợp sẵn như Math
và Date
.
Khi di chuyển tập lệnh sang V8, hãy kiểm thử và điều chỉnh kỳ vọng của mã
về việc sử dụng this
trong ngữ cảnh toàn cầu. Trong hầu hết các trường hợp, sự khác biệt
chỉ rõ ràng nếu mã của bạn kiểm tra các khoá hoặc tên thuộc tính của
đối tượng this
toàn cục:
// 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)); } |
Điều chỉnh quá trình xử lý instanceof
trong thư viện
Sử dụng instanceof
trong thư viện trên đối tượng được truyền dưới dạng tham số trong một
từ một dự án khác có thể trả về kết quả âm tính giả. Trong thời gian chạy V8,
dự án và các thư viện của dự án đó chạy trong nhiều ngữ cảnh thực thi khác nhau và do đó có
chuỗi toàn cục và chuỗi nguyên mẫu khác nhau.
Xin lưu ý rằng trường hợp này chỉ xảy ra nếu thư viện của bạn sử dụng instanceof
trên một đối tượng
không được tạo trong dự án của bạn. Sử dụng trên đối tượng được tạo trong
dự án của bạn, cho dù trong cùng một tập lệnh hay trong một tập lệnh khác trong dự án,
sẽ hoạt động như mong đợi.
Nếu một dự án đang chạy trên V8 sử dụng tập lệnh của bạn làm thư viện, hãy kiểm tra xem
tập lệnh sử dụng instanceof
trên tham số sẽ được truyền từ một dự án khác. Điều chỉnh
việc sử dụng instanceof
và sử dụng các giải pháp thay thế khả thi khác tuỳ theo cách sử dụng của bạn
trường hợp.
Một cách khác cho a instanceof b
có thể là sử dụng hàm khởi tạo của a
trong
các trường hợp bạn không cần tìm kiếm toàn bộ chuỗi nguyên mẫu mà chỉ cần kiểm tra
hàm khởi tạo.
Cách sử dụng: a.constructor.name == "b"
Hãy xem xét Dự án A và Dự án B, trong đó Dự án A sử dụng Dự án B làm thư việ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 } |
Một cách khác có thể là giới thiệu một hàm kiểm tra instanceof
trong dự án chính
và truyền hàm này cùng với các tham số khác khi gọi một hàm thư viện. Hàm đã truyền
sau đó có thể được dùng để kiểm tra instanceof
bên trong thư viện.
//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); } |
Điều chỉnh việc truyền các tài nguyên không được chia sẻ vào thư viện
Việc chuyển tài nguyên không được chia sẻ từ tập lệnh chính đến một thư viện sẽ hoạt động theo cách khác trong thời gian chạy V8.
Trong thời gian chạy Rhino, việc truyền một tài nguyên không được chia sẻ sẽ không hoạt động. Thay vào đó, thư viện sử dụng tài nguyên riêng.
Trong thời gian chạy V8, việc truyền một tài nguyên không được chia sẻ vào thư viện sẽ hoạt động. Thư viện sử dụng tài nguyên không được chia sẻ đã truyền.
Không truyền các tài nguyên không được chia sẻ dưới dạng tham số hàm. Luôn khai báo các tài nguyên không được chia sẻ trong cùng một tập lệnh sử dụng các tài nguyên đó.
Hãy xem xét Dự án A và Dự án B, trong đó Dự án A sử dụng Dự án B làm thư viện. Trong ví dụ này, PropertiesService
là một tài nguyên không được chia sẻ.
// Rhino runtime // Project A function testPassingNonSharedProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-A'); B.setScriptProperties(); // Prints: Project-B Logger.log(B.getScriptProperties( PropertiesService, 'project')); } |
// V8 runtime // Project A function testPassingNonSharedProperties() { PropertiesService.getScriptProperties() .setProperty('project', 'Project-A'); B.setScriptProperties(); // Prints: Project-A Logger.log(B.getScriptProperties( PropertiesService, 'project')); } |
Cập nhật quyền truy cập vào các tập lệnh độc lập
Đối với các tập lệnh độc lập chạy trên thời gian chạy V8, bạn cần cung cấp cho người dùng ít nhất xem quyền truy cập vào tập lệnh để các trình kích hoạt của tập lệnh hoạt động chính xác.