Tuần tự hóa và hủy tuần tự hóa trong JavaScript

OMG tại sao mọi người lại làm điều gì đó đồi trụy này?

Tất cả bắt đầu thật ngây thơ. Anh chàng ở bàn bên cạnh tôi hỏi "này, có thư viện javascript nào có thể biến mớ hỗn độn tuần tự hóa php này thành thứ mà tôi có thể đọc được không?" . Anh ấy giải thích rằng anh ấy đang cố gắng kết hợp một khai thác thử nghiệm js cho một tập hợp các dịch vụ REST trả về PHP được tuần tự hóa dưới dạng biểu diễn truyền tải của chúng

Tìm kiếm trên google đã tìm ra thứ gì đó nên tôi quay lại nghe album OMM mới nhất. Mười lăm phút sau, dòng lời nguyền rủa phát ra từ Gallilama bắt đầu khiến tôi khó chịu. Hóa ra hàm phpjs đáng kính chỉ xử lý một tập hợp con cụ thể của đầu ra serialize của PHP. Cụ thể, nó hoàn toàn không xử lý các tham chiếu và đối tượng. Google đã tìm thấy một triển khai java trông hoàn chỉnh hơn. Tôi đã chuyển nhanh nó sang javascript và chuyển sang danh sách phát $wingin' Utter$ của mình

Ngày hôm sau tôi đăng ký và phát hiện ra rằng những điều kỳ lạ đang xảy ra với cảng của tôi. Hóa ra các thành viên riêng tư và được bảo vệ serialize theo một cách "thú vị". PHP thêm vào trước tên thành viên bằng tên lớp (riêng tư) hoặc dấu hoa thị (được bảo vệ) được bao quanh bởi các byte rỗng (\u0000). Trình phân tích cú pháp hack đã đi vào một vòng lặp vô hạn khi nó cố trích xuất các giá trị này

Đến thời điểm này tôi đã hoàn toàn cam kết. Không có gì khác ngoài một thư viện được xác thực TDD có thể xử lý bất kỳ sự điên rồ nào mà tôi ném vào nó. Tôi chắc chắn rằng vẫn còn những lỗ hổng, nhưng "hack nhanh" này đang hoạt động cho những nhu cầu phức tạp của chúng tôi

Chi tiết triển khai

Định dạng tuần tự hóa của PHP không được ghi lại rõ ràng, nhưng chức năng này sử dụng cách tiếp cận phỏng đoán tốt nhất để phân tích cú pháp và diễn giải nó. Số nguyên tuần tự hóa, số float, booleans, chuỗi, mảng, đối tượng và tham chiếu hiện được hỗ trợ

Kiểu mảng PHP là sự kết hợp giữa kiểu mảng và đối tượng của javascript. phpUnserialize dịch các mảng PHP chỉ có các khóa số liên tiếp dựa trên 0 thành các mảng javascript. Tất cả các mảng khác được dịch sang các đối tượng javascript

Các thành viên được tuần tự hóa của một đối tượng PHP mang thông tin phạm vi thông qua xáo trộn tên. phpUnserialize loại bỏ tiền tố biểu thị phạm vi này khỏi các thành viên riêng tư và được bảo vệ

Kiểm tra các bài kiểm tra để biết thêm chi tiết hoặc đọc nguồn

Cách sử dụng

Tệp phpUnserialize.js triển khai mẫu Định nghĩa mô-đun chung cố gắng tương thích với nhiều trình tải tập lệnh bao gồm AMD, CommonJS và sử dụng trực tiếp trong tệp HTML

Tanagra. js được thiết kế đơn giản và nhẹ và hiện tại nó hỗ trợ Node. các lớp js và ES6. Việc triển khai chính hỗ trợ JSON và phiên bản thử nghiệm hỗ trợ Bộ đệm giao thức của Google

Qua

Luke Wilson

Luke có 12 năm kinh nghiệm với tư cách là kỹ sư, trưởng nhóm và bậc thầy scrum

CHIA SẺ

CHIA SẺ

Hiệu suất trang web và bộ nhớ đệm dữ liệu

Các trang web hiện đại thường truy xuất dữ liệu từ một số vị trí khác nhau, bao gồm cơ sở dữ liệu và API của bên thứ ba. Ví dụ: khi xác thực người dùng, một trang web có thể tra cứu hồ sơ người dùng từ cơ sở dữ liệu, sau đó tô điểm thêm bằng dữ liệu từ một số dịch vụ bên ngoài thông qua lệnh gọi API. Giảm thiểu các lệnh gọi tốn kém tới các nguồn dữ liệu này, chẳng hạn như quyền truy cập đĩa cho các truy vấn cơ sở dữ liệu và các vòng lặp internet cho các lệnh gọi API, là điều cần thiết để duy trì một trang web phản hồi nhanh. Bộ nhớ đệm dữ liệu là một kỹ thuật tối ưu hóa phổ biến được sử dụng để đạt được điều này

Các quy trình lưu trữ dữ liệu làm việc của chúng trong bộ nhớ. Nếu một máy chủ web chạy trong một tiến trình đơn lẻ (chẳng hạn như Node. js/Express), thì dữ liệu này có thể dễ dàng được lưu vào bộ nhớ cache bằng cách sử dụng bộ nhớ cache chạy trong cùng một quy trình. Tuy nhiên, các máy chủ web cân bằng tải mở rộng trên nhiều quy trình và ngay cả khi làm việc với một quy trình duy nhất, bạn có thể muốn bộ nhớ cache tiếp tục tồn tại khi máy chủ được khởi động lại. Điều này đòi hỏi một giải pháp bộ nhớ đệm ngoài quy trình, chẳng hạn như Redis, có nghĩa là dữ liệu cần được tuần tự hóa bằng cách nào đó và giải tuần tự hóa khi đọc từ bộ đệm

Tuần tự hóa và giải tuần tự hóa tương đối đơn giản để đạt được trong các ngôn ngữ được nhập tĩnh như C#. Tuy nhiên, bản chất động của JavaScript khiến vấn đề phức tạp hơn một chút. Mặc dù ECMAScript 6 (ES6) đã giới thiệu các lớp, nhưng các trường trên các lớp này (và kiểu của chúng) không được xác định cho đến khi chúng được khởi tạo—điều này có thể không xảy ra khi lớp được khởi tạo—và kiểu trả về của các trường và hàm không được xác định . Hơn nữa, cấu trúc của lớp có thể dễ dàng thay đổi trong thời gian chạy—có thể thêm hoặc bớt các trường, có thể thay đổi các loại, v.v. Mặc dù điều này có thể thực hiện được bằng cách sử dụng sự phản chiếu trong C#, nhưng sự phản chiếu đại diện cho “nghệ thuật đen tối” của ngôn ngữ đó và các nhà phát triển hy vọng nó sẽ phá vỡ chức năng

Tôi đã gặp vấn đề này tại nơi làm việc cách đây vài năm khi làm việc trong nhóm nòng cốt Toptal. Chúng tôi đang xây dựng một bảng điều khiển linh hoạt cho các nhóm của mình, bảng điều khiển này cần phải nhanh chóng; . Chúng tôi lấy dữ liệu từ một số nguồn. hệ thống theo dõi công việc, công cụ quản lý dự án và cơ sở dữ liệu của chúng tôi. Trang web được xây dựng trong Node. js/Express và chúng tôi có bộ nhớ cache để giảm thiểu các cuộc gọi đến các nguồn dữ liệu này. Tuy nhiên, quá trình phát triển lặp đi lặp lại nhanh chóng của chúng tôi có nghĩa là chúng tôi đã triển khai (và do đó khởi động lại) nhiều lần trong ngày, làm mất hiệu lực bộ đệm và do đó mất đi nhiều lợi ích của nó

Một giải pháp rõ ràng là bộ đệm ngoài quy trình, chẳng hạn như Redis. Tuy nhiên, sau một số nghiên cứu, tôi thấy rằng không có thư viện tuần tự hóa tốt nào tồn tại cho JavaScript. JSON tích hợp. xâu chuỗi/JSON. các phương thức phân tích cú pháp trả về dữ liệu của loại đối tượng, làm mất bất kỳ chức năng nào trên các nguyên mẫu của các lớp ban đầu. Điều này có nghĩa là các đối tượng đã khử lưu huỳnh không thể được sử dụng đơn giản “tại chỗ” trong ứng dụng của chúng tôi, do đó sẽ yêu cầu tái cấu trúc đáng kể để hoạt động với một thiết kế thay thế

Yêu cầu đối với Thư viện

Để hỗ trợ tuần tự hóa và giải tuần tự hóa dữ liệu tùy ý trong JavaScript, với các biểu diễn được giải tuần tự hóa và bản gốc có thể sử dụng thay thế cho nhau, chúng tôi cần một thư viện tuần tự hóa với các thuộc tính sau

  • Các biểu diễn deserialized phải có cùng một nguyên mẫu (hàm, getters, setters) như các đối tượng ban đầu
  • Thư viện nên hỗ trợ các loại phức tạp lồng nhau (bao gồm mảng và bản đồ), với các nguyên mẫu của các đối tượng lồng nhau được đặt chính xác
  • Có thể tuần tự hóa và giải tuần tự hóa cùng một đối tượng nhiều lần—quá trình phải bình thường hóa
  • Định dạng tuần tự hóa phải dễ dàng truyền qua TCP và có thể lưu trữ bằng Redis hoặc một dịch vụ tương tự
  • Cần thay đổi mã tối thiểu để đánh dấu một lớp là có thể tuần tự hóa
  • Các thói quen thư viện nên được nhanh chóng
  • Lý tưởng nhất là nên có một số cách để hỗ trợ giải tuần tự hóa các phiên bản cũ của một lớp, thông qua một số loại ánh xạ/phiên bản

Thực hiện

Để thu hẹp khoảng cách này, tôi quyết định viết Tanagra. js, thư viện tuần tự hóa mục đích chung cho JavaScript. Tên của thư viện liên quan đến một trong những tập phim yêu thích của tôi về Star Trek. Thế hệ tiếp theo, nơi phi hành đoàn của Doanh nghiệp phải học cách giao tiếp với một chủng tộc ngoài hành tinh bí ẩn có ngôn ngữ khó hiểu. Thư viện tuần tự hóa này hỗ trợ các định dạng dữ liệu phổ biến để tránh các sự cố như vậy

tanagra. js được thiết kế đơn giản và nhẹ và hiện tại nó hỗ trợ Node. js (nó chưa được thử nghiệm trong trình duyệt, nhưng về lý thuyết, nó sẽ hoạt động) và các lớp ES6 (bao gồm cả Bản đồ). Việc triển khai chính hỗ trợ JSON và phiên bản thử nghiệm hỗ trợ Bộ đệm giao thức của Google. Thư viện chỉ yêu cầu JavaScript tiêu chuẩn (hiện đã được thử nghiệm với ES6 và Node. js), không phụ thuộc vào các tính năng thử nghiệm, chuyển mã Babel hoặc TypeScript

Các lớp có thể tuần tự hóa được đánh dấu như vậy bằng một lệnh gọi phương thức khi lớp được xuất

module.exports = serializable(Foo, myUniqueSerialisationKey)

Phương thức trả về một proxy cho lớp, nó sẽ chặn hàm tạo và đưa vào một mã định danh duy nhất. (Nếu không được chỉ định, giá trị này sẽ mặc định là tên lớp. ) Khóa này được đánh số thứ tự với phần còn lại của dữ liệu và lớp cũng hiển thị nó dưới dạng trường tĩnh. Nếu lớp chứa bất kỳ kiểu lồng nhau nào (i. e. , thành viên có loại cần tuần tự hóa), chúng cũng được chỉ định trong lệnh gọi phương thức

________số 8

(Các loại lồng nhau cho các phiên bản trước của lớp cũng có thể được chỉ định theo cách tương tự, do đó, ví dụ: nếu bạn tuần tự hóa một Foo1, thì nó có thể được giải tuần tự hóa thành một Foo2. )

Trong quá trình tuần tự hóa, thư viện xây dựng đệ quy một bản đồ khóa toàn cầu cho các lớp và sử dụng bản đồ này trong quá trình giải tuần tự hóa. (Hãy nhớ rằng khóa được đánh số thứ tự với phần còn lại của dữ liệu. ) Để biết loại của lớp “cấp cao nhất”, thư viện yêu cầu điều này được chỉ định trong lệnh gọi deserialization

const foo = decodeEntity(serializedFoo, Foo)

Thư viện ánh xạ tự động thử nghiệm đi qua cây mô-đun và tạo ánh xạ từ tên lớp, nhưng điều này chỉ hoạt động đối với các lớp được đặt tên duy nhất

Bố cục dự án

Dự án được chia thành một số mô-đun

  • tanagra-core - chức năng phổ biến được yêu cầu bởi các định dạng tuần tự hóa khác nhau, bao gồm chức năng đánh dấu các lớp là có thể tuần tự hóa
  • tanagra-json - tuần tự hóa dữ liệu thành định dạng JSON
  • tanagra-protobuf - tuần tự hóa dữ liệu thành định dạng bộ đệm trước của Google (thử nghiệm)
  • tanagra-protobuf-redis-cache - một thư viện trợ giúp để lưu trữ các protobuf tuần tự trong Redis
  • tanagra-auto-mapper - duyệt cây mô-đun trong Node. js để xây dựng bản đồ các lớp, nghĩa là người dùng không phải chỉ định loại để giải tuần tự hóa thành (thử nghiệm)

Lưu ý rằng thư viện sử dụng chính tả Hoa Kỳ

Ví dụ sử dụng

Ví dụ sau khai báo một lớp có thể tuần tự hóa và sử dụng mô-đun tanagra-json để tuần tự hóa/giải tuần tự hóa nó

const serializable = require('tanagra-core').serializable
class Foo {
  constructor(bar, baz1, baz2, fooBar1, fooBar2) {
	this.someNumber = 123
	this.someString = 'hello, world!'
	this.bar = bar // a complex object with a prototype
	this.bazArray = [baz1, baz2]
	this.fooBarMap = new Map([
  	['a', fooBar1],
  	['b', fooBar2]
	])
  }
}

// Mark class `Foo` as serializable and containing sub-types `Bar`, `Baz` and `FooBar`
module.exports = serializable(Foo, [Bar, Baz, FooBar])

...

const json = require('tanagra-json')
json.init()
// or:
// require('tanagra-protobuf')
// await json.init()

const foo = new Foo(bar, baz)
const encoded = json.encodeEntity(foo)

...

const decoded = json.decodeEntity(encoded, Foo)

Hiệu suất

Tôi đã so sánh hiệu suất của hai bộ nối tiếp (bộ nối tiếp JSON và bộ nối tiếp protobufs thử nghiệm) với một điều khiển (JSON gốc. phân tích cú pháp và JSON. xâu chuỗi). Tôi đã tiến hành tổng cộng 10 thử nghiệm với mỗi

Tôi đã thử nghiệm điều này trên máy tính xách tay Dell XPS15 2017 với bộ nhớ 32Gb, chạy Ubuntu 17. 10

Tôi đã tuần tự hóa đối tượng lồng nhau sau

foo: {
  "string": "Hello foo",
  "number": 123123,
  "bars": [
	{
  	"string": "Complex Bar 1",
  	"date": "2019-01-09T18:22:25.663Z",
  	"baz": {
    	"string": "Simple Baz",
    	"number": 456456,
    	"map": Map { 'a' => 1, 'b' => 2, 'c' => 2 }
  	}
	},
	{
  	"string": "Complex Bar 2",
  	"date": "2019-01-09T18:22:25.663Z",
  	"baz": {
    	"string": "Simple Baz",
    	"number": 456456,
    	"map": Map { 'a' => 1, 'b' => 2, 'c' => 2 }
  	}
	}
  ],
  "bazs": Map {
	'baz1' => Baz {
  	string: 'baz1',
  	number: 111,
  	map: Map { 'a' => 1, 'b' => 2, 'c' => 2 }
	},
	'baz2' => Baz {
  	string: 'baz2',
  	number: 222,
  	map: Map { 'a' => 1, 'b' => 2, 'c' => 2 }
	},
	'baz3' => Baz {
  	string: 'baz3',
  	number: 333,
  	map: Map { 'a' => 1, 'b' => 2, 'c' => 2 }
	}
  },
}

Viết hiệu suất

Phương pháp tuần tự hóa Ave. tập đoàn. bản dùng thử đầu tiên (ms)StDev. tập đoàn. bản dùng thử đầu tiên (ms)Ave. Ví dụ. bản dùng thử đầu tiên (ms)StDev. Ví dụ. bản dùng thử đầu tiên (ms)JSON0. 1150. 09030. 08790. 0256Google Protobufs2. 002. 7481. 130. 278Nhóm kiểm soát0. 01550. 007260. 01390. 00570

Đọc

Phương pháp tuần tự hóa Ave. tập đoàn. bản dùng thử đầu tiên (ms)StDev. tập đoàn. bản dùng thử đầu tiên (ms)Ave. Ví dụ. bản dùng thử đầu tiên (ms)StDev. Ví dụ. bản dùng thử đầu tiên (ms)JSON0. 1330. 1020. 1040. 0429 Nguyên mẫu Google 2. 621. 122. 280. 364Nhóm kiểm soát0. 01350. 007290. 01150. 00390

Tóm lược

Trình tuần tự hóa JSON chậm hơn khoảng 6-7 lần so với trình tuần tự hóa gốc. Trình nối tiếp protobufs thử nghiệm chậm hơn khoảng 13 lần so với trình tuần tự hóa JSON hoặc chậm hơn 100 lần so với trình tuần tự hóa gốc

Ngoài ra, bộ nhớ đệm bên trong của thông tin lược đồ/cấu trúc trong mỗi bộ tuần tự hóa rõ ràng có ảnh hưởng đến hiệu suất. Đối với trình nối tiếp JSON, lần ghi đầu tiên chậm hơn khoảng bốn lần so với mức trung bình. Đối với trình nối tiếp protobuf, nó chậm hơn chín lần. Vì vậy, việc ghi các đối tượng có siêu dữ liệu đã được lưu vào bộ nhớ cache sẽ nhanh hơn nhiều trong cả hai thư viện

Hiệu ứng tương tự cũng được quan sát thấy đối với các lần đọc. Đối với thư viện JSON, lần đọc đầu tiên chậm hơn khoảng bốn lần so với mức trung bình và đối với thư viện protobuf, nó chậm hơn khoảng hai lần rưỡi

Các vấn đề về hiệu suất của trình nối tiếp protobuf có nghĩa là nó vẫn đang trong giai đoạn thử nghiệm và tôi chỉ khuyên dùng nó nếu bạn cần định dạng vì lý do nào đó. Tuy nhiên, đáng để đầu tư thời gian vào, vì định dạng này ngắn hơn nhiều so với JSON và do đó tốt hơn để gửi qua mạng. Stack Exchange sử dụng định dạng cho bộ nhớ đệm nội bộ của nó

Trình nối tiếp JSON rõ ràng hiệu quả hơn nhiều nhưng vẫn chậm hơn đáng kể so với triển khai gốc. Đối với cây đối tượng nhỏ, sự khác biệt này không đáng kể (một vài mili giây trên yêu cầu 50 mili giây sẽ không làm hỏng hiệu suất trang web của bạn), nhưng điều này có thể trở thành vấn đề đối với cây đối tượng cực lớn và là một trong những ưu tiên phát triển của tôi

lộ trình

Thư viện vẫn đang trong giai đoạn thử nghiệm. Trình nối tiếp JSON được thử nghiệm hợp lý và ổn định. Đây là lộ trình cho vài tháng tới

  • Cải thiện hiệu suất cho cả hai serializers
  • Hỗ trợ tốt hơn cho JavaScript trước ES6
  • Hỗ trợ cho trang trí ES-Next

Tôi biết không có thư viện JavaScript nào khác hỗ trợ tuần tự hóa dữ liệu đối tượng phức tạp, lồng nhau và giải tuần tự hóa thành loại ban đầu của nó. Nếu bạn đang triển khai chức năng sẽ được hưởng lợi từ thư viện, vui lòng dùng thử, liên hệ với phản hồi của bạn và cân nhắc đóng góp

Trang chủ dự án
Kho GitHub

Hiểu những điều cơ bản

Các đối tượng được lưu trữ trong JavaScript như thế nào?

Nói chung, các đối tượng không được lưu trữ. Chúng được khởi tạo khi cần thiết, được sử dụng trong quá trình xử lý, sau đó bị xóa khỏi bộ nhớ khi không còn cần thiết. Nếu chúng tôi cần tạm thời sử dụng dữ liệu ở nơi khác, chúng tôi sẽ tuần tự hóa và giải tuần tự hóa dữ liệu đó thành một cấu trúc khác

Đối tượng JavaScript là gì?

Một đối tượng là một đoạn mã đóng gói một cấu trúc, cùng với các thao tác có thể được thực hiện trên cấu trúc đó. Nói chung, nó giống như một đối tượng trong bất kỳ ngôn ngữ lập trình hướng đối tượng nào

Đối tượng dữ liệu là gì?

Đối tượng dữ liệu là một đoạn mã tạm thời chứa dữ liệu từ kho lưu trữ dữ liệu để có thể đọc hoặc xử lý trong ứng dụng. Trừ khi có một cách để giữ dữ liệu đó trong bộ nhớ, nó sẽ được ghi lại hoặc không được giữ lại khi đối tượng vượt quá phạm vi hoặc không cần thiết

Tại sao chúng ta cần serialization và deserialization?

Tuần tự hóa cho phép chúng tôi giữ dữ liệu để sử dụng trong quy trình đang chạy hoặc các quy trình khác nếu cần. Chúng tôi lưu trữ dữ liệu, sau đó giải tuần tự hóa khi dữ liệu được sử dụng ở nơi khác

thẻ

JavaScriptSerializingObjectsCachingJavaScriptObjects

Người làm việc tự do? Tìm công việc tiếp theo của bạn.

Công việc lập trình viên JavaScript

Xem thông tin đầy đủ

Luke Wilson

Nhà phát triển JavaScript tự do

Giới thiệu về tác giả

Luke đã viết chương trình chào thế giới đầu tiên của mình bằng QBasic khi mới 8 tuổi và đã viết mã kể từ đó. Với 13 năm kinh nghiệm chuyên môn với tư cách là giám đốc kỹ thuật, bậc thầy scrum và kiến ​​trúc sư, ông bắt đầu sự nghiệp của mình bằng việc viết các hệ thống giao dịch trong. NET trước khi mở rộng kinh nghiệm của mình trong bối cảnh khởi nghiệp ở London. Sau đó, ông giữ nhiều vai trò lãnh đạo và kỹ thuật khác nhau trong nhóm cốt lõi của Toptal (làm việc với Rails và Node. js) trước khi chuyển sang vai trò quản lý kỹ thuật và lãnh đạo công nghệ từ xa

Tuần tự hóa trong JavaScript là gì?

Quá trình theo đó một đối tượng hoặc cấu trúc dữ liệu được dịch sang định dạng phù hợp để truyền qua mạng hoặc lưu trữ (e. g. trong bộ đệm mảng hoặc định dạng tệp). Ví dụ, trong JavaScript, bạn có thể tuần tự hóa một đối tượng thành chuỗi JSON bằng cách gọi hàm JSON. xâu chuỗi ().

Tuần tự hóa và không tuần tự hóa là gì?

Tuần tự hóa là một cơ chế chuyển đổi trạng thái của một đối tượng thành luồng byte. Deserialization là quá trình ngược lại trong đó luồng byte được sử dụng để tạo lại đối tượng Java thực tế trong bộ nhớ. Cơ chế này được sử dụng để duy trì đối tượng

Tuần tự hóa và giải tuần tự hóa trong JS là gì?

Tuần tự hóa lấy cấu trúc dữ liệu trong bộ nhớ và chuyển đổi nó thành một chuỗi byte có thể được lưu trữ và chuyển giao. Deserialization lấy một loạt byte và chuyển đổi nó thành cấu trúc dữ liệu trong bộ nhớ có thể được sử dụng theo chương trình

Làm cách nào để hủy xác thực chuỗi trong JavaScript?

hàm unserialize() . Ví dụ về php. hàm unserialize() trong js. Trong tài liệu web sau, hàm unserialize() chuyển đổi dữ liệu tuần tự hóa thành định dạng thực tế.