Văn hay trong hiện tại, chữ tốt ở tương lai

Giải mã bản chất cốt lõi của JavaScript | Chương 3.5

Giải mã bản chất cốt lõi của JavaScript (Phần 3, chương 5) là nội dung chuyển ngữ Việt ngữ từ tác phẩm kinh điển You Don't Know JS Yet của Kyle Simpson.

55 phút đọc.

0 lượt xem.

Giải mã bản chất cốt lõi của JavaScript (Phần 3, chương 5) là nội dung chuyển ngữ Việt ngữ từ tác phẩm kinh điển You Don’t Know JS Yet của Kyle Simpson.

Mở đầu

Xuyên suốt những chương trước của tài liệu này, chúng ta đã tiến hành một cuộc hành trình khám phá tường tận và vô cùng toàn diện về bản chất của các đối tượng, cơ chế nguyên mẫu, hệ tư tưởng định hướng lớp, và mới đây nhất là sự phức tạp của từ khóa this bên trong hệ sinh thái ngôn ngữ JavaScript. Thế nhưng, ngay tại thời điểm này, chúng ta sẽ cùng nhau dừng lại một nhịp để nhìn nhận và đánh giá lại toàn bộ những kiến thức đã được tích lũy đó dưới một lăng kính kiến trúc hoàn toàn mới mẻ và mang tính chất đột phá hơn rất nhiều. Hãy thử tưởng tượng về một kịch bản lý tưởng: điều gì sẽ xảy ra nếu như bạn hoàn toàn có đủ quyền năng để khai thác triệt để toàn bộ sức mạnh hủy diệt của các đối tượng, mạng lưới nguyên mẫu, và cơ chế phân giải ngữ cảnh động của từ khóa this hoạt động song kiếm hợp bích cùng nhau, mà tuyệt đối không bao giờ phải cần đến sự hiện diện của từ khóa lớp hay bất kỳ một cơ chế dẫn xuất nào sinh ra từ nó? Trên thực tế, tôi sẵn sàng đưa ra một lập luận mang tính chất phản biện mạnh mẽ rằng bản chất cốt lõi của ngôn ngữ JavaScript vốn dĩ ít mang hơi hướng định hướng lớp hơn rất nhiều so với những gì mà cái từ khóa lớp đầy tính chất đánh lừa thị giác kia có thể khiến cho giới lập trình viên lầm tưởng. Bởi vì nền tảng của JavaScript được xây dựng như một ngôn ngữ lập trình mang tính chất động và dựa trên cơ chế nguyên mẫu một cách thuần túy, nên điểm mạnh tuyệt đối và bản ngã thực sự của nó không nằm ở sự phân cấp tĩnh, mà lại nằm ở… sự ủy quyền… Tuy nhiên, trước khi chúng ta chính thức dấn thân vào việc mổ xẻ cơ chế ủy quyền này, tôi mang trong mình một trách nhiệm phải đưa ra một lời cảnh báo chân thành về mặt thực tiễn. Góc nhìn kiến trúc mà chúng ta sắp sửa khám phá về liên kết nguyên mẫu ẩn của đối tượng và cơ chế ngữ cảnh hàm của từ khóa this hoàn toàn không phải là một xu hướng mang tính chất đại chúng trong ngành công nghiệp phần mềm hiện đại. Đó tuyệt đối không phải là cách thức mà các tác giả phát triển những bộ khung ứng dụng hay các thư viện phổ biến đang tận dụng ngôn ngữ JavaScript. Theo như những gì tôi được biết, bạn sẽ gần như không thể tìm thấy bất kỳ một ứng dụng phần mềm quy mô lớn nào ngoài kia đang áp dụng triệt để khuôn mẫu thiết kế này làm xương sống kiến trúc. Vậy thì lý do sâu xa nào khiến tôi lại quyết định cống hiến trọn vẹn một chương sách để mổ xẻ một khuôn mẫu thiết kế bị coi là không được ưa chuộng đến như vậy? Đó là một câu hỏi vô cùng sắc bén. Câu trả lời mang hơi hướng ngông cuồng sẽ là: bởi vì đây là tài liệu do chính tay tôi chắp bút và tôi hoàn toàn có đặc quyền truyền tải bất cứ thứ gì mà tôi cảm thấy tâm đắc! Thế nhưng, một câu trả lời mang tầm nhìn sâu sắc hơn là: bởi vì tôi mang một niềm tin sắt đá rằng việc trau dồi và phát triển một sự thấu hiểu tường tận về cái góc nhìn đặc thù này đối với một trong những trụ cột cốt lõi của ngôn ngữ sẽ mang lại lợi ích to lớn cho tư duy kiến trúc của bạn, ngay cả khi toàn bộ những gì bạn làm trong suốt sự nghiệp của mình chỉ là loanh quanh sử dụng các khuôn mẫu JavaScript mang phong cách từ khóa lớp… Cần phải đính chính một cách minh bạch rằng, khái niệm ủy quyền hoàn toàn không phải là một phát minh do cá nhân tôi sáng tạo ra. Nó đã tồn tại sừng sững như một khuôn mẫu thiết kế kinh điển trong suốt nhiều thập kỷ qua trong giới khoa học máy tính. Và trong một khoảng thời gian dài đằng đẵng, cộng đồng các nhà phát triển đã từng tranh luận nảy lửa và đi đến một nhận định sai lầm rằng sự ủy quyền dựa trên nguyên mẫu chỉ đơn thuần là một hình thái động của sự kế thừa truyền thống. Trong khuôn khổ học thuật của chương này, tôi sẽ trình bày cơ chế ủy quyền, dựa trên cách thức mà nó được triển khai thông qua các hệ thống cơ học của ngôn ngữ JavaScript, dưới tư cách là một khuôn mẫu thiết kế hoàn toàn thay thế, được định vị một cách độc lập và nằm lơ lửng ở một ranh giới trung gian giữa hệ tư tưởng định hướng lớp và các khuôn mẫu dựa trên cơ chế bao đóng của đối tượng hoặc mô đun. Giai đoạn thứ nhất của tiến trình này là phải phân rã hoàn toàn cơ chế từ khóa lớp về lại thành từng mảnh ghép độc lập cấu thành nên nó. Sau đó, chúng ta sẽ tiến hành chọn lọc những tinh hoa và nhào nặn các mảnh ghép đó lại với nhau theo một phương thức hoàn toàn khác biệt.

Bản chất của hàm kiến tạo và sự phân rã cấu trúc

Trong kiến trúc phần mềm định hướng lớp, lập trình viên thường bị che mắt bởi những cú pháp hào nhoáng mà quên đi bản chất thực sự của việc cấp phát bộ nhớ và quản lý trạng thái. Giai đoạn thứ nhất để thấu hiểu sự ủy quyền là chúng ta phải dũng cảm đập bỏ cái ảo giác về sự khởi tạo thống nhất do các lớp tạo ra, từ đó tiến hành bóc tách từng công đoạn độc lập trong vòng đời của một đối tượng. Việc phân rã này không chỉ giúp chúng ta nhìn thấu cơ chế vi mạch của ngôn ngữ JavaScript mà còn mở ra những chân trời mới để tái cấu trúc lại các đoạn mã theo hướng minh bạch, an toàn và tối ưu tài nguyên hơn rất nhiều.

Khởi tạo đối tượng thông qua cơ chế nhà máy

Trong những nội dung học thuật thuộc Chương 3, chúng ta đã từng chứng kiến sự hiện diện của hàm kiến tạo dưới tư cách là một cánh cổng xâm nhập chính yếu chuyên dùng để đảm nhận công tác xây dựng nên một bản sao hoàn chỉnh của một từ khóa lớp… Thế nhưng, có một sự thật phũ phàng mang tính chất đảo lộn tư duy mà chúng ta bắt buộc phải thừa nhận: bản thân cái hàm kiến tạo đó trên thực tế hoàn toàn không hề thi hành bất kỳ một công đoạn tạo lập vật lý nào trong không gian bộ nhớ, mà sứ mệnh duy nhất của nó chỉ đơn thuần là gánh vác các công việc liên quan đến khởi tạo trạng thái ban đầu. Nói một cách rành mạch và chi tiết hơn, cái bản sao đối tượng đó vốn dĩ đã được hệ thống nặn ra một cách hoàn chỉnh từ trước khi cái hàm kiến tạo có cơ hội được kích nổ và bắt đầu chạy để thực thi việc gán giá trị – lấy ví dụ như các thao tác gán mang hình hài từ khóa this chấm thuộc tính… Vậy thì một câu hỏi mang tính chất truy nguyên nguồn gốc được đặt ra là: cái công đoạn tạo lập vật lý đó thực chất đã diễn ra ở cái xó xỉnh nào trong guồng máy của hệ thống? Câu trả lời chính xác là nó nằm gọn bên trong nội tạng của toán tử tạo mới new… Như những gì đã được giải phẫu cặn kẽ trong chuyên mục Triệu hồi ngữ cảnh bằng từ khóa tạo mới ở Chương 4, tồn tại chính xác bốn giai đoạn cơ học mà từ khóa tạo mới new bắt buộc phải nai lưng ra thực thi; và giai đoạn đầu tiên trong chuỗi quy trình đó chính là việc bòn rút tài nguyên để kiến tạo ra một đối tượng hoàn toàn mới toanh và trống rỗng từ hư không. Cái hàm kiến tạo thậm chí còn chưa đủ tư cách để được hệ thống triệu hồi cho đến khi quy trình của từ khóa tạo mới new tiến bước sang giai đoạn thứ ba. Sự ngộ nhận về vai trò của hàm kiến tạo chính là một trong những nguyên nhân hàng đầu khiến cho các lập trình viên gặp khó khăn trong việc kiểm soát luồng thực thi và quản lý bộ nhớ, đặc biệt là khi đối mặt với các kịch bản kế thừa phức tạp hoặc khi cố gắng can thiệp vào vòng đời của đối tượng trước thời điểm nó được chính thức gắn kết với các thuộc tính trạng thái.

Thế nhưng, toán tử tạo mới new tuyệt đối không phải là con đường độc đạo – hay nói một cách khách quan thì nó thậm chí có thể không phải là con đường tối ưu nhất – để thi hành công đoạn tạo lập ra một bản sao đối tượng trong hệ sinh thái JavaScript. Hãy cùng nhau đặt lên bàn cân phân tích một đoạn mã minh họa hoàn toàn không dựa dẫm vào lớp: một hàm điện toán thông thường mang tên Hàm tạo điểm hai chiều, nhận vào hai tọa độ, tự tay khởi tạo một đối tượng rỗng gán vào một biến cục bộ, tiếp tục gán tọa độ vào đối tượng đó, và cuối cùng nôn cái đối tượng đó ra ngoài thông qua câu lệnh trả về. Trong cái kịch bản kiến trúc này, hoàn toàn không có sự xuất hiện của bất kỳ một từ khóa lớp nào, mà chỉ đơn thuần là một định nghĩa hàm điện toán tuân thủ theo tiêu chuẩn thông thường. Cũng hoàn toàn vắng bóng sự hiện diện của bất kỳ một lời triệu hồi nào dính dáng đến từ khóa tạo mới new, thay vào đó chỉ là một lời gọi hàm mộc mạc như bao lời gọi hàm khác. Và điều đặc biệt nhất là, hoàn toàn không có bất kỳ một sợi dây tham chiếu nào dính líu đến từ khóa this, mà chỉ thuần túy là những thao tác gán thuộc tính đối tượng trực tiếp và rõ ràng. Cái thuật ngữ mang tính chất chuyên môn thường xuyên được giới học thuật ưu ái sử dụng để gọi tên cho cái khuôn mẫu mã nguồn đặc thù này là khuôn mẫu hàm nhà máy… Việc kích nổ cái hàm nhà máy này sẽ tự động kéo theo toàn bộ quá trình xây dựng – bao hàm cả việc tạo lập vật lý lẫn khởi tạo trạng thái – của một đối tượng, và cuối cùng nó sẽ chuyển giao cái thành phẩm đó ngược trở lại cho chúng ta. Đây đích thị là một khuôn mẫu thiết kế cực kỳ thịnh hành trong thực tiễn, với mức độ phổ biến ít nhất cũng phải ngang ngửa, nếu không muốn nói là vượt trội hơn, so với các đoạn mã được viết theo định hướng lớp truyền thống. Sự minh bạch tuyệt đối trong việc cấp phát và trả về đối tượng giúp cho người lập trình nắm trong tay quyền kiểm soát tối thượng đối với mọi thực thể được sinh ra.

Tôi đã chủ động tiến hành thêm các chú thích dạng số thứ tự vào bên trong đoạn mã minh họa đó, bao gồm giai đoạn 1, giai đoạn 3, và giai đoạn 4, những con số này mang ý nghĩa phản chiếu gần như tương đồng với các giai đoạn thứ nhất, thứ ba, và thứ tư nằm trong quy trình vận hành của toán tử tạo mới new. Thế nhưng, có một mảnh ghép kiến trúc quan trọng dường như đã bốc hơi mất: giai đoạn thứ hai đang lẩn trốn ở đâu? Nếu như trí nhớ của bạn vẫn còn hoạt động tốt, thì giai đoạn thứ hai của toán tử tạo mới new gánh vác sứ mệnh móc nối cái đối tượng vừa được tạo hình ở giai đoạn thứ nhất với một đối tượng khác, thông qua một khe cắm mang tên liên kết nguyên mẫu ẩn nằm sâu bên trong nội tạng của nó – một khái niệm đã được mổ xẻ ở Chương 2. Vậy thì đứng trước bài toán này, chúng ta có thể sẽ nảy sinh khát khao muốn móc nối cái đối tượng bản sao của mình với một cái đối tượng nào đó mang hình hài ra sao? Một hướng đi vô cùng hợp lý là chúng ta hoàn toàn có thể móc nối nó trỏ thẳng vào một đối tượng chuyên đóng vai trò làm kho lưu trữ các hàm điện toán mà chúng ta mong muốn được liên kết hoặc tái sử dụng song hành cùng với cái bản sao vừa được nặn ra của mình. Bằng cách tự tay can thiệp và thiết lập cái sợi dây liên kết nguyên mẫu này, chúng ta không chỉ khôi phục lại trọn vẹn sức mạnh của mạng lưới chia sẻ hành vi, mà còn thoát khỏi sự kìm kẹp cứng nhắc của mô hình phân cấp do lớp áp đặt, mở toang cánh cửa cho phép việc định tuyến nguyên mẫu một cách linh động ngay tại thời điểm chương trình đang hoạt động.

Rủi ro tài nguyên khi lạm dụng toán tử tạo mới

Chúng ta hãy cùng nhau xắn tay áo lên để tiến hành sửa đổi và nâng cấp cái đoạn mã minh họa trước đó nhằm mục đích tích hợp thêm sợi dây liên kết nguyên mẫu. Chúng ta định nghĩa một đối tượng nguyên mẫu chứa phương thức chuyển đổi thành chuỗi, và bên trong hàm nhà máy, chúng ta nhét thêm một thuộc tính dính líu đến nguyên mẫu hai dấu gạch dưới trực tiếp vào đối tượng khi nó vừa được khai sinh, trỏ thẳng về cái đối tượng nguyên mẫu kia. Giờ đây, bạn đã có thể tận mắt chứng kiến thao tác gán thuộc tính nguyên mẫu hai dấu gạch dưới đang nai lưng ra thi hành nhiệm vụ thiết lập cái liên kết nguyên mẫu ẩn nội bộ, thứ vốn dĩ chính là cái giai đoạn thứ hai bị thất lạc mà chúng ta vừa mới nhắc đến. Tôi cố tình sử dụng cái thuộc tính nguyên mẫu hai dấu gạch dưới ở đây hoàn toàn chỉ mang ý đồ minh họa cho sự kiện này một cách trực quan nhất; trên thực tế, việc viện đến sự phục vụ của phương thức thiết lập nguyên mẫu của đối tượng như những gì đã được trình diễn ở Chương 4 cũng sẽ mang lại một kết quả kiến trúc hoàn toàn tương đồng và chuẩn mực hơn rất nhiều. Vậy thì, theo nhận định của bạn, cái kịch bản thảm họa nào sẽ giáng xuống hệ thống nếu như chúng ta dại dột xài từ khóa tạo mới new để bóp cò triệu hồi cái hàm điện toán Hàm tạo điểm hai chiều giống như cái cách mà đoạn mã dưới đây đang phô diễn? Chúng ta khai báo một biến điểm khác và gán nó bằng kết quả của lời gọi hàm kèm từ khóa tạo mới new, sau đó gọi phương thức chuyển đổi thành chuỗi trên nó. Khoan đã! Chuyện quái quỷ gì đang diễn ra ở đây vậy?

Một hàm điện toán mang tính chất nhà máy thông thường, hoàn toàn trong sạch và không dính líu gì đến từ khóa lớp, lại bị đem ra triệu hồi song hành cùng với từ khóa tạo mới new, y hệt như thể bản thân nó là một từ khóa lớp thứ thiệt vậy. Liệu cái hành vi ngông cuồng này có gây ra bất kỳ một sự xáo trộn hay thay đổi nào đối với cái kết quả đầu ra cuối cùng của đoạn mã hay không? Câu trả lời mang tính chất hai mặt là: vừa không… mà lại vừa có. Xét về mặt cấu trúc vật lý, cái đối tượng điểm khác được sinh ra ở đây sở hữu một hình hài và bản chất giống hệt y đúc với cái đối tượng mà nó đáng lý ra sẽ trở thành nếu như tôi không dúng tay vào sử dụng từ khóa tạo mới new. Thế nhưng! Cái đối tượng mà từ khóa tạo mới new đã cất công tự tay nặn ra, thiết lập liên kết nguyên mẫu, và gán làm ngữ cảnh cho từ khóa this thì có số phận ra sao? Cái đối tượng khốn khổ đó đã bị hệ thống ngó lơ một cách phũ phàng, bị ném thẳng vào sọt rác không thương tiếc, để rồi cuối cùng chờ đợi sự thanh trừng và dọn dẹp bởi bộ thu gom rác thải bộ nhớ của cỗ máy ngôn ngữ JavaScript. Đây là một sự lãng phí tài nguyên tính toán và không gian cấp phát vô cùng tai hại, một hệ quả trực tiếp của việc nhắm mắt áp dụng các khuôn mẫu cú pháp mà không hề thấu hiểu cái cơ chế vi mạch đang âm thầm cày cuốc bên dưới.

Thật không may mắn cho chúng ta, cỗ máy thực thi của ngôn ngữ JavaScript hoàn toàn không được trang bị khả năng ngoại cảm để có thể tiên tri trước được rằng bạn thực chất hoàn toàn không có ý định xài đến cái đối tượng mà bạn vừa mới ra lệnh cho từ khóa tạo mới new phải nặn ra, vì vậy cái đối tượng thừa thãi đó luôn luôn và chắc chắn vẫn sẽ bị ép buộc phải ra đời bất chấp việc nó sẽ rơi vào cảnh bị thất sủng ngay lập tức. Đúng là như vậy đấy! Việc cố đấm ăn xôi xài một từ khóa tạo mới new để chĩa vào một hàm nhà máy có thể mang lại một cảm giác gõ phím thuận tay hơn hoặc quen thuộc hơn đối với những lập trình viên mang nặng tư duy hướng lớp, thế nhưng xét về mặt tối ưu hóa hệ thống thì nó lại là một hành vi tiêu tốn tài nguyên một cách cực kỳ vô nghĩa, bởi vì nó tàn nhẫn ra lệnh cho hệ thống phải khởi tạo vật lý đến tận hai đối tượng trong bộ nhớ, để rồi sau đó lại ném đi một cái một cách vô cùng hoang phí. Hành vi này không chỉ vắt kiệt sức lực của bộ cấp phát bộ nhớ mà còn tạo ra những khoảng giật lag tiềm ẩn khi bộ thu gom rác thải buộc phải tạm dừng luồng thực thi chính để dọn dẹp cái đống lộn xộn do chính người lập trình vô ý tạo ra, minh chứng cho tầm quan trọng của việc kiến trúc phần mềm phải đồng nhất với ý đồ kỹ thuật.

Tái cấu trúc cơ chế nguyên mẫu và hàm tiện ích

Nằm trong khuôn khổ của ví dụ mã nguồn hiện tại, cái hàm điện toán Hàm tạo điểm hai chiều vẫn mang trên mình một lớp mặt nạ khiến nó trông giống một cách kỳ lạ với một hàm kiến tạo thông thường trực thuộc một định nghĩa từ khóa lớp… Thế nhưng, kịch bản kiến trúc sẽ biến hóa ra sao nếu như chúng ta quyết định bứng toàn bộ cái mớ mã nguồn đảm nhận nhiệm vụ khởi tạo trạng thái đó và ném nó sang một hàm điện toán hoàn toàn biệt lập, giả sử như chúng ta đặt tên cho nó là hàm khởi tạo ban đầu? Chúng ta định nghĩa lại đối tượng nguyên mẫu để nó chứa đựng cả phương thức khởi tạo ban đầu lẫn phương thức chuyển đổi thành chuỗi, và bên trong hàm nhà máy, chúng ta thay thế các dòng gán thuộc tính trực tiếp bằng một lời gọi duy nhất đến phương thức khởi tạo ban đầu trên chính cái đối tượng vừa được tạo ra. Lời gọi hàm mang hình hài đối tượng chấm khởi tạo ban đầu này đã khôn khéo khai thác triệt để cái lợi thế của liên kết nguyên mẫu ẩn vốn đã được giăng sẵn thông qua phép gán thuộc tính nguyên mẫu hai dấu gạch dưới. Nhờ có sự chống lưng của cái mạng lưới này, nó tiến hành ủy quyền toàn bộ quá trình tra cứu phương thức ngược lên phía trên của chuỗi nguyên mẫu để tìm đến phương thức khởi tạo ban đầu ngự trị trên đối tượng nguyên mẫu, và sau đó kích nổ nó với một ngữ cảnh từ khóa this được trói chặt vào cái đối tượng bản sao – một hành động đạt được thông qua cơ chế gán ngữ cảnh ngầm định mà chúng ta đã cày xới ở Chương 4. Sự phân rã này mang lại một ranh giới rõ ràng tuyệt đối giữa công đoạn cấp phát bộ nhớ vật lý và công đoạn tiêm nhiễm trạng thái logic vào đối tượng.

Chúng ta hãy cùng nhau tiếp tục đào sâu vào cái quá trình phân rã kiến trúc này. Hãy chuẩn bị tinh thần để đón nhận một cú lật lọng ngoạn mục về mặt cấu trúc! Tôi đã thẳng tay ném cái hàm điện toán Hàm tạo điểm hai chiều vào sọt rác, và thay vào đó, tôi quyết định đổi tên cái đối tượng nguyên mẫu kia thành Điểm hai chiều cho nó mang dáng dấp của một thực thể đại diện. Chà, một nước đi nghe có vẻ khá là dị hợm và điên rồ phải không? Thế nhưng hãy kiên nhẫn soi xét phần còn lại của cái đoạn mã đó sau khi đã bị tái cấu trúc: chúng ta khởi tạo một biến điểm bằng một đối tượng chứa liên kết nguyên mẫu ẩn trỏ đến Điểm hai chiều nhằm gom chung các giai đoạn tạo lập và liên kết, sau đó gọi phương thức khởi tạo ban đầu trên nó, và cuối cùng là phương thức chuyển đổi thành chuỗi. Và đây sẽ là bước tinh chỉnh cuối cùng mang tính chất chốt hạ: chúng ta hãy viện đến sự phục vụ của một công cụ tiện ích bản địa mà ngôn ngữ JavaScript đã dọn sẵn cho chúng ta, mang tên phương thức tạo mới của đối tượng (Object.create). Chúng ta gán biến điểm bằng kết quả của lời gọi phương thức tạo mới của đối tượng truyền vào Điểm hai chiều. Rốt cuộc thì phương thức tạo mới của đối tượng này đang âm thầm thi hành những thủ thuật cơ học nào ở tầng dưới? Thứ nhất, nó sử dụng phép thuật để nặn ra một đối tượng hoàn toàn mới toanh và trống rỗng từ hư không. Thứ hai, nó tiến hành móc nối sợi dây liên kết nguyên mẫu ẩn của cái đối tượng trống rỗng vừa mới chào đời đó trỏ thẳng vào đối tượng nguyên mẫu của hàm được truyền vào. Nếu như hai cái giai đoạn cơ học này mang lại cho bạn một cảm giác quen thuộc đến rùng mình, thì đó là bởi vì chúng chính xác là những bản sao y hệt của hai giai đoạn đầu tiên nằm trong quy trình vận hành của toán tử tạo mới new. Việc ứng dụng phương thức tiện ích này giúp loại bỏ hoàn toàn sự lộn xộn của các thuộc tính nội bộ tà đạo và chuẩn hóa quá trình cấp phát liên kết.

Bây giờ, hãy cùng nhau gom toàn bộ những mảnh ghép kiến trúc vừa bị băm vằm này lại thành một khối thống nhất. Chúng ta có đối tượng Điểm hai chiều chứa phương thức khởi tạo và chuyển đổi, sau đó dùng phương thức tạo mới của đối tượng để nặn ra đối tượng điểm, gọi hàm khởi tạo trên nó, và thử nghiệm phương thức chuyển đổi. Hừm. Hãy dành ra một vài khoảnh khắc tĩnh tâm để nghiền ngẫm xem rốt cuộc thì chúng ta vừa mới chắt lọc và phát minh ra được cái thứ triết lý gì ở đây. Nếu đem nó lên bàn cân để so đọ với phương pháp tiếp cận bằng từ khóa lớp, thì nó mang hình hài như thế nào? Cái khuôn mẫu thiết kế này đã thẳng tay vứt bỏ cả từ khóa lớp lẫn toán tử tạo mới new vào thùng rác lịch sử, thế nhưng nó lại hoàn thành xuất sắc một mục tiêu cuối cùng giống hệt y đúc đến từng chi tiết. Cái cái giá phải trả cho sự phản nghịch này là gì? Đó là cái thao tác vĩ đại và đơn độc của toán tử tạo mới new giờ đây đã bị băm vằm ra thành hai câu lệnh hoàn toàn tách biệt: một lệnh tạo mới của đối tượng và một lệnh gọi khởi tạo ban đầu. Nếu như cái viễn cảnh phải chứng kiến hai thao tác này nằm chỏng chơ tách biệt nhau khiến cho tư duy của bạn cảm thấy ngứa ngáy khó chịu – liệu có phải nó đã bị phân rã một cách quá đáng rồi chăng!? – thì đừng lo, chúng luôn luôn có thể được hàn gắn và nhào nặn lại với nhau bên trong một cái hàm tiện ích nhà máy bé nhỏ: một hàm chế tạo nhận vào kiểu đối tượng và các đối số, thực thi lệnh tạo mới, gọi hàm khởi tạo với tham số trải rộng, và trả về đối tượng. Một hàm tiện ích nhà máy mang tên chế tạo giống như thế này có khả năng vận hành trơn tru và phổ quát cho bất kỳ một kiểu đối tượng nào, miễn là bạn ngoan ngoãn tuân thủ theo một cái quy ước ngầm định rằng mỗi một kiểu đối tượng mà bạn muốn trỏ sợi dây liên kết tới đều phải sở hữu sẵn một hàm điện toán mang tên khởi tạo ban đầu ngự trị trên cơ thể nó. Và dĩ nhiên, bạn vẫn hoàn toàn nắm giữ quyền năng tối thượng để kiến tạo ra bao nhiêu bản sao tùy thích thông qua cái hàm tiện ích đó, chứng minh rằng sự đơn giản hóa cấu trúc không hề tước đi bất kỳ sức mạnh khởi tạo quy mô lớn nào của hệ thống.

Tư duy từ bỏ lớp và nguyên lý ủy quyền

Một khi chúng ta đã dũng cảm phanh phui bản chất của các hàm kiến tạo và nhận ra sự thừa thãi của toán tử tạo mới, bước ngoặt tư duy tiếp theo là phải rũ bỏ hoàn toàn cái bóng ma của hệ tư tưởng định hướng lớp. Việc từ bỏ này không chỉ dừng lại ở mặt cú pháp bề ngoài, mà nó đòi hỏi một cuộc cách mạng sâu sắc trong cách chúng ta tư duy về mối quan hệ giữa các thực thể trong hệ thống. Nguyên lý ủy quyền ngang hàng chính là chìa khóa vàng để khai phá sự tự do và tính linh hoạt tột độ mà ngôn ngữ JavaScript luôn hứa hẹn mang lại.

Sự xung đột giữa phân cấp truyền thống và bản chất ngôn ngữ

Thành thật mà nói, cái cuộc đại phẫu phân rã cấu trúc mà chúng ta vừa mới cùng nhau trải qua rốt cuộc cũng chỉ đưa chúng ta hạ cánh xuống một thứ mã nguồn có hình hài hơi khác biệt một chút, và có lẽ chỉ ưu việt hơn một chút xíu hoặc tồi tệ hơn một chút xíu khi đem ra đọ sức với phong cách viết mã bằng từ khóa lớp… Nếu như toàn bộ triết lý của sự ủy quyền chỉ dừng lại ở cái mức độ nông cạn như vậy, thì có lẽ nó thậm chí còn chẳng xứng đáng để được nhét vào phần chú thích cuối trang, chứ đừng nói đến việc tôi phải vung bút dành hẳn một chương sách đồ sộ để ca ngợi nó. Thế nhưng, chính tại cái ngã ba đường này, chúng ta sẽ bắt đầu dùng sức để gạt phăng cái tư duy định hướng lớp ra khỏi tâm trí, chứ không chỉ đơn thuần là ném bỏ lớp vỏ bọc cú pháp của nó. Hệ tư tưởng thiết kế định hướng lớp, tự trong sâu thẳm bản chất của nó, luôn luôn ép buộc người ta phải kiến tạo nên một cấu trúc phân cấp mang tính chất phân loại học, có nghĩa là nó bắt buộc chúng ta phải cày xới xem làm thế nào để chia cắt và gom nhóm các đặc tính hành vi lại với nhau, để rồi sau đó xếp chồng chúng lên nhau theo một trục dọc tuyến tính nằm trong một cái chuỗi phả hệ kế thừa cứng nhắc. Hơn thế nữa, cái việc định nghĩa ra một lớp con thực chất là hành động chuyên biệt hóa từ một lớp cơ sở mang tính chất khái quát cao hơn. Việc nặn ra một bản sao lại tiếp tục là một hành động chuyên biệt hóa từ một định nghĩa lớp khái quát đó. Sự phân cấp theo chiều dọc này tạo ra những gông cùm kiến trúc khiến cho mã nguồn trở nên giòn gãy và cực kỳ khó tái cấu trúc khi yêu cầu nghiệp vụ thay đổi phức tạp theo thời gian.

Các hành vi nghiệp vụ nằm lọt thỏm bên trong một cấu trúc phân cấp lớp truyền thống luôn luôn mang bản chất là sự kết hợp theo chiều dọc đâm xuyên qua nhiều tầng lớp chồng chất của cái chuỗi phả hệ kế thừa. Xuyên suốt nhiều thập kỷ phát triển của ngành công nghiệp phần mềm, đã có không ít những nỗ lực được tung ra, và thậm chí có những thời kỳ chúng còn trở thành những trào lưu vô cùng thịnh hành, nhằm mục đích đập bẹp và san phẳng những cái cấu trúc phả hệ kế thừa sâu thăm thẳm đó, để chuyển hướng sang ưu ái một cơ chế kết hợp mang tính chất chiều ngang linh hoạt hơn rất nhiều, thông qua các khái niệm như hàm pha trộn và hàng tá những ý tưởng kiến trúc tương tự. Tôi hoàn toàn không có ý định buông lời miệt thị hay khẳng định rằng có bất kỳ một sự sai trái nào lẩn khuất bên trong những phương pháp tiếp cận mã nguồn đó. Thế nhưng, tôi đang mạnh dạn tuyên bố một chân lý rằng chúng hoàn toàn không phải là cái cách thức vận hành thuận tự nhiên của ngôn ngữ JavaScript, và hệ lụy là việc cố đấm ăn xôi để ép buộc áp dụng chúng vào bên trong hệ sinh thái JavaScript luôn luôn là một cuộc hành trình dài đằng đẵng, quanh co, và ngập tràn sự phức tạp. Sự gượng ép này đã vô tình bồi đắp thêm hàng núi những mớ cú pháp góc cạnh và đầy tiểu tiết chỉ để chắp vá và giả lập những tính năng đó lên trên cái nền tảng cốt lõi của JavaScript vốn dĩ chỉ dựa trên hai trụ cột là liên kết nguyên mẫu ẩntừ khóa this… Việc đi ngược lại với dòng chảy tự nhiên của ngôn ngữ không chỉ làm tăng độ phức tạp của cỗ máy biên dịch mà còn tạo ra một rào cản nhận thức khổng lồ cho những thế hệ lập trình viên mới bước chân vào nghề.

Xuyên suốt phần dung lượng còn lại của chương sách này, tôi mang trong mình một ý đồ rõ ràng là sẽ thẳng tay ném bỏ cả cái lớp vỏ bọc cú pháp của từ khóa lớp VÀ vứt luôn cả cái hệ tư tưởng giáo điều của việc phân lớp… Chúng ta sẽ không còn bị ám ảnh bởi những khái niệm cha con, siêu lớp hay lớp dẫn xuất nữa, mà thay vào đó sẽ mở rộng tầm mắt để đón nhận một mạng lưới các thực thể tồn tại song song, tương tác và hỗ trợ lẫn nhau một cách bình đẳng. Bước nhảy vọt về mặt tư duy này là điều kiện tiên quyết để có thể lĩnh hội được sức mạnh thực sự của mô hình ủy quyền, một mô hình tôn vinh sự linh hoạt, tính tái sử dụng cao và sự phân tách mối quan tâm một cách triệt để trong môi trường thực thi của JavaScript.

Nguyên lý cấu thành của mô hình ủy quyền ngang hàng

Vậy thì rốt cuộc cái khái niệm ủy quyền này sinh ra để ám chỉ điều gì? Tận sâu bên trong cái cốt lõi triết học của nó, sự ủy quyền là câu chuyện về hai hay nhiều thực thể cùng nhau chung vai sát cánh san sẻ gánh nặng để hoàn tất một khối lượng công việc cụ thể. Thay vì phải hì hục định nghĩa một thực thể cha mang tính chất khái quát khổng lồ mang tên Điểm hai chiều chuyên dùng để đại diện cho một mớ hành vi dùng chung mà một tập hợp bao gồm một hay nhiều thực thể con mang tên điểm hay điểm khác sẽ phải xếp hàng để nhận thừa kế từ nó, sự ủy quyền lại dẫn dắt tư duy của chúng ta rẽ sang một hướng đi khác: kiến tạo nên chương trình của mình bằng cách lắp ghép những thực thể nằm ở vị thế ngang hàng và tách biệt hoàn toàn, để chúng có cơ hội bắt tay và hợp tác vui vẻ với nhau. Mô hình này xóa nhòa ranh giới phân tầng, biến mọi đối tượng trong hệ thống thành những cá thể độc lập có khả năng đóng góp năng lực chuyên biệt của mình vào một luồng xử lý chung mà không cần phải mang trên mình gánh nặng của một chuỗi phả hệ phức tạp.

Tôi sẽ phác họa cái triết lý đó ra thành những dòng mã nguồn cụ thể để bạn dễ hình dung. Chúng ta tạo ra một đối tượng Tọa độ chứa các phương thức thiết lập hoành độ, tung độ và thiết lập cả hai thông qua từ khóa this; sau đó tạo thêm một đối tượng Kiểm tra chứa phương thức chuyển đổi thành chuỗi. Tiếp theo, chúng ta tạo ra một đối tượng điểm trống rỗng, và gọi phương thức thiết lập tọa độ từ đối tượng Tọa độ cũng như phương thức chuyển đổi từ đối tượng Kiểm tra bằng cách sử dụng tiện ích call và ép ngữ cảnh vào đối tượng điểm. Cuối cùng, chúng ta tạo ra một đối tượng điểm khác được liên kết nguyên mẫu trỏ thẳng vào Tọa độ, rồi gọi phương thức thiết lập tọa độ trực tiếp trên nó, và lại dùng tiện ích call để gọi phương thức chuyển đổi từ Kiểm tra. Hãy cùng nhau mổ xẻ từng mảnh ghép xem cái quái gì đang thực sự diễn ra ở đây. Tôi đã tự tay định nghĩa ra một cái Tọa độ dưới thân phận là một đối tượng vật lý mang tính chất cụ thể, nó ôm giữ một vài cái hành vi mà tôi muốn gắn kết với cái thao tác thiết lập hệ tọa độ của một điểm. Đồng thời, tôi cũng định nghĩa ra một cái Kiểm tra dưới thân phận là một đối tượng vật lý cụ thể khác, chuyên trị việc tàng trữ một số logic dùng để kiểm tra và gỡ lỗi, chẳng hạn như phương thức chuyển đổi thành chuỗi. Sau đó, tôi lại tiếp tục nhào nặn ra thêm hai cái đối tượng vật lý cụ thể nữa, mang tên điểmđiểm khác… Sự phân tách này giúp cô lập logic xử lý dữ liệu và logic hiển thị thành hai thực thể độc lập, tuân thủ nguyên tắc trách nhiệm duy nhất một cách vô cùng tự nhiên và thanh lịch.

Cái đối tượng điểm hoàn toàn không sở hữu bất kỳ một sợi dây liên kết nguyên mẫu ẩn đặc thù nào (nó ngoan ngoãn sử dụng giá trị mặc định trỏ về nguyên mẫu của đối tượng tổng). Bằng cách vận dụng chiêu thức gán ngữ cảnh tường minh (đã được mổ xẻ ở Chương 4), tôi tiến hành bóp cò triệu hồi các hàm tiện ích từ Tọa độKiểm tra nhưng lại ép buộc chúng phải chạy bên trong cái bối cảnh của đối tượng điểm… Đó chính xác là cái cơ chế mà tôi trân trọng đặt cho một cái tên là sự ủy quyền tường minh… Trái ngược lại, cái đối tượng điểm khác lại được hệ thống giăng sẵn một sợi dây liên kết nguyên mẫu ẩn trỏ thẳng vào đối tượng Tọa độ, mục đích chủ yếu là để vớt vát lại một chút sự tiện lợi khi gõ mã. Sợi dây liên kết đó bật đèn xanh cho phép tôi có thể vô tư sử dụng chiêu thức gán ngữ cảnh ngầm định thông qua cú pháp gọi hàm trực tiếp trên đối tượng. Thế nhưng, tôi vẫn hoàn toàn giữ nguyên cái quyền năng được tường minh chia sẻ cái đối tượng điểm khác để nó đóng vai trò làm ngữ cảnh cho cái lời gọi hàm chuyển đổi thành chuỗi từ đối tượng Kiểm tra kia. Đó chính là cái cơ chế mà tôi gọi là sự ủy quyền ngầm định… Sự kết hợp nhuần nhuyễn giữa hai cơ chế ủy quyền này mang lại một độ đàn hồi vô song cho kiến trúc phần mềm, cho phép người kỹ sư tự do pha trộn các khối hành vi ngay tại thời điểm thực thi mà không cần phải viết lại mã nguồn hay tái cấu trúc lớp.

Cơ chế ủy quyền ngầm định và tường minh

Đừng nhắm mắt làm ngơ trước cái triết lý tối thượng này: Chúng ta rốt cuộc vẫn đạt được cái cảnh giới của sự kết hợp: chúng ta đã thành công trong việc kết hợp các khối hành vi từ đối tượng Tọa độ và đối tượng Kiểm tra lại với nhau, ngay trong khoảng thời gian chương trình đang chạy bục mặt, thông qua các lời triệu hồi hàm kết hợp với trò chia sẻ ngữ cảnh từ khóa this… Chúng ta hoàn toàn không bị ép buộc phải đóng vai một gã thợ hàn hì hục nhào nặn và nhồi nhét toàn bộ những cái hành vi đó vào chung một cái từ khóa lớp duy nhất – hay một cái phả hệ từ khóa lớp từ cơ sở đến dẫn xuất – chỉ để phục vụ cho cái mục đích cho phép đối tượng điểm hay điểm khác được quyền nhận thừa kế từ nó. Tôi cực kỳ tâm đắc và muốn tôn vinh cái kỹ thuật kết hợp tại thời điểm chạy này bằng một cái tên hoa mỹ là sự kết hợp ảo… Cái trọng tâm kiến trúc mà tôi muốn xoáy sâu vào ở đây là: hoàn toàn không có bất kỳ một ai trong số bốn cái đối tượng vật lý này phải cam chịu đóng vai trò làm cha hay làm con của kẻ khác. Tất cả bọn chúng đều ngẩng cao đầu tự hào là những thực thể nằm ở vị thế ngang hàng với nhau, và mỗi một thực thể trong số đó đều mang trong mình một sứ mệnh và mục đích tồn tại hoàn toàn khác biệt. Sự giải phóng khỏi ách thống trị của cây phả hệ này cho phép hệ thống mở rộng một cách vô hạn mà không sợ gặp phải thảm họa phình to của lớp cơ sở – một căn bệnh trầm kha của lập trình hướng đối tượng cổ điển.

Chúng ta hoàn toàn có đặc quyền tổ chức và sắp xếp các khối hành vi của mình thành những phân vùng logic cực kỳ gọn gàng – nằm chễm chệ trên mỗi cái đối tượng tương ứng của riêng chúng – và sau đó tiến hành chia sẻ cái bối cảnh hoạt động thông qua từ khóa this – và nếu thích, có thể xài thêm cả sợi dây liên kết nguyên mẫu ẩn – thứ cơ chế rốt cuộc sẽ nôn ra cho chúng ta những kết quả kết hợp kiến trúc hoàn toàn tương đồng và mạnh mẽ y hệt như những khuôn mẫu thiết kế khác mà chúng ta đã từng mang ra mổ xẻ từ đầu cuốn sách đến giờ. Đó mới đích thực là cái trái tim đang đập mạnh mẽ của khuôn mẫu ủy quyền, được hiện thân một cách hoàn hảo thông qua các cỗ máy cơ học của ngôn ngữ JavaScript. Sự vĩ đại của nó nằm ở tính linh hoạt tuyệt đối: các chức năng không bị trói buộc vật lý vào dữ liệu mà chúng thao tác, tạo ra một sự phân tách hoàn hảo giúp tối ưu hóa khả năng bảo trì và tái sử dụng mã nguồn trong các dự án phức tạp.

Trong lần xuất bản đầu tiên của loạt sách chuyên sâu này, cái cuốn sách đặc thù này đã từng khai sinh ra một thuật ngữ kỹ thuật mang tên Các đối tượng liên kết với các đối tượng khác (OLOO) – cốt chỉ để dựng lên một bức tường tương phản gay gắt chống lại cái khái niệm Định hướng đối tượng truyền thống. Chìm đắm trong cái đoạn mã minh họa vừa diễn ra trước mắt, bạn hoàn toàn có thể chiêm ngưỡng và cảm nhận được cái tinh túy cốt lõi của triết lý OLOO: toàn bộ vốn liếng mà chúng ta sở hữu trong tay chỉ đơn thuần là các đối tượng vật lý, chúng móc nối với nhau và hợp tác vui vẻ với những đối tượng vật lý khác. Cá nhân tôi tìm thấy một vẻ đẹp lay động lòng người toát ra từ chính sự tối giản đến mức thuần khiết của nó. Triết lý này đưa chúng ta quay trở về với cái nguồn cội sơ khai nhất của tư duy tính toán, nơi mà mọi thứ được giải quyết bằng những thông điệp truyền qua lại giữa các thực thể mạng lưới, thay vì phải cõng trên lưng một hệ thống phân loại học cồng kềnh và nặng nề mang nặng tính chất quan liêu.

Xây dựng hệ thống bằng các đối tượng ngang hàng

Khi đã nắm vững triết lý ủy quyền ở mức độ cơ bản, việc tiếp theo là đẩy giới hạn của nó lên một tầm cao mới để giải quyết những bài toán nghiệp vụ phức tạp hơn. Việc xây dựng hệ thống bằng các đối tượng ngang hàng không chỉ dừng lại ở việc chia sẻ vài hàm tiện ích đơn giản, mà nó còn mở ra khả năng kiến tạo nên một mạng lưới các thực thể tương tác chéo, điều phối dữ liệu và logic kết xuất một cách cực kỳ tinh vi mà không hề làm mất đi tính độc lập của từng module.

Tích hợp và tương tác đa đối tượng trong thực tiễn

Hãy cùng nhau vặn ga và đẩy cái triết lý ủy quyền này đi xa thêm một vạn dặm nữa. Nằm lọt thỏm trong cái đoạn mã ngắn ngủi vừa rồi, đối tượng điểmđiểm khác thực chất chỉ đóng vai trò là những cái thùng rỗng tuếch chuyên dùng để tàng trữ dữ liệu, và toàn bộ những cái hành vi tính toán mà chúng đi vay mượn thông qua cơ chế ủy quyền đều nằm chễm chệ trên những đối tượng vật lý ngoại lai khác. Thế nhưng, sức mạnh tối thượng của hệ thống là chúng ta hoàn toàn có đặc quyền đắp thêm các khối hành vi trực tiếp lên cơ thể của bất kỳ một cái đối tượng nào đang đứng chầu chực trong cái chuỗi dây chuyền ủy quyền đó, và những cái khối hành vi này thậm chí còn có khả năng giao tiếp và tương tác qua lại lẫn nhau, toàn bộ cái màn ảo thuật này đều được đạo diễn thông qua phép màu của cơ chế kết hợp ảo (hay nói cách khác là trò chia sẻ ngữ cảnh từ khóa this). Để phác họa một cách sống động cho cái khái niệm trừu tượng này, chúng ta sẽ tiến hành nâng cấp và tiến hóa cái ví dụ về đối tượng điểm hiện tại lên một mức độ phức tạp đáng kể. Và như một phần quà khuyến mãi thêm, chúng ta sẽ thực sự bắt tay vào việc vẽ vời những cái điểm ảo này lên trên một phần tử bảng vẽ vật lý nằm chễm chệ trên Mô hình đối tượng tài liệu. Hãy cùng nhau soi xét kỹ lưỡng cái đoạn mã đồ sộ này: chúng ta định nghĩa một đối tượng bảng vẽ chứa các hàm thiết lập gốc tọa độ, vẽ điểm ảnh và kết xuất khung cảnh, trong đó hàm kết xuất gọi phương thức vẽ từ khóa this; đối tượng tọa độ cũng gọi phương thức kết xuất từ khóa this sau khi thiết lập xong; đối tượng điểm điều khiển thì được liên kết nguyên mẫu với tọa độ, sở hữu ngữ cảnh bảng vẽ, và tự định nghĩa các hàm xoay, vẽ và kết xuất bằng cách ủy quyền tường minh ngược lại cho bảng vẽ. Cấu trúc mạng lưới này cho thấy sự đan xen logic cực kỳ chặt chẽ nhưng lại hoàn toàn không bị trói buộc bởi bất kỳ một hệ thống phân cấp cứng nhắc nào.

Chà, đó quả thực là một khối lượng mã nguồn khổng lồ đòi hỏi bạn phải tốn khá nhiều nơ ron thần kinh để tiêu hóa. Hãy cứ thong thả dành ra mọi sự tập trung cần thiết và cày đi cày lại cái đoạn mã đó vài ba lần cho đến khi thấm nhuần. Tôi đã cố tình đắp thêm vào đó một vài cái đối tượng vật lý hoàn toàn mới toanh (bao gồm Bảng vẽĐiểm điều khiển) để chúng đứng kề vai sát cánh bên cạnh cái đối tượng Tọa độ cũ kỹ từ ví dụ trước. Nhiệm vụ sống còn của bạn là phải căng mắt ra để soi và thấu hiểu cho bằng được cái mạng lưới tương tác chằng chịt đang diễn ra giữa ba cái đối tượng vật lý cụ thể này. Đối tượng Điểm điều khiển đã được hệ thống giăng sẵn một sợi dây móc nối (thông qua thuộc tính nguyên mẫu hai dấu gạch dưới) nhằm mục đích ủy quyền một cách ngầm định (dựa dẫm vào chuỗi liên kết nguyên mẫu ẩn) để bám vào đối tượng Tọa độ… Và đây là một ví dụ điển hình cho chiêu thức ủy quyền tường minh: lời gọi thiết lập gốc tọa độ từ Bảng vẽ nhưng lại ép ngữ cảnh vào Điểm điều khiển; tôi đang nã đạn triệu hồi cái hàm thiết lập gốc tọa độ đó nhưng lại giam cầm nó bên trong cái bối cảnh của đối tượng Điểm điều khiển… Cú bóp cò đó đã tạo ra một hiệu ứng phụ cực kỳ tinh vi là nó tiến hành chia sẻ cái thuộc tính ngữ cảnh bảng vẽ cho hàm thiết lập gốc tọa độ thông qua kênh truyền dẫn từ khóa this… Sự phân chia trách nhiệm ở đây đạt đến độ hoàn hảo: bảng vẽ chỉ quan tâm đến việc thao tác đồ họa, tọa độ chỉ lo việc tính toán số học, và điểm điều khiển đóng vai trò như một nhạc trưởng điều phối sự tương tác giữa hai thực thể kia.

Lời gọi thiết lập tọa độ của Điểm điều khiển thì lại giở trò ủy quyền ngầm định để nhờ vả hàm thiết lập tọa độ của đối tượng Tọa độ, thế nhưng cái bối cảnh không gian thì vẫn bị trói chặt vào đối tượng Điểm điều khiển… Ở đây lẩn khuất một chi tiết kỹ thuật cực kỳ tinh quái mà nhãn quan thông thường rất dễ bị bỏ sót: bạn có để ý thấy cái lệnh gọi kết xuất bằng từ khóa this nằm lọt thỏm ở bên trong nội tạng của hàm thiết lập tọa độ thuộc đối tượng Tọa độ không? Rốt cuộc thì cái hàm kết xuất đó từ lỗ nẻ nào chui lên vậy? Bởi vì cái ngữ cảnh từ khóa this ngay tại thời điểm đó đang bị ép buộc phải mang hình hài của đối tượng Điểm điều khiển (chứ tuyệt đối không phải là Tọa độ), nên cái lệnh gọi đó thực chất đang bóp cò để kích nổ hàm kết xuất của đối tượng Điểm điều khiển… Hàm kết xuất của Điểm điều khiển lại tiếp tục giở chiêu ủy quyền tường minh để réo tên hàm kết xuất khung cảnh của Bảng vẽ, và một lần nữa, cái bối cảnh không gian vẫn ngoan cố nằm yên ở đối tượng Điểm điều khiển… Hàm kết xuất khung cảnh lại tiếp tục réo gọi hàm vẽ bằng từ khóa this, thế nhưng nguồn gốc của hàm vẽ này lại từ đâu mà ra? Chuẩn không cần chỉnh, nó lại một lần nữa xuất phát từ đối tượng Điểm điều khiển (thông qua cơ chế chia sẻ bối cảnh của từ khóa this). Và hàm vẽ của Điểm điều khiển thì sao? Nó lại sử dụng ủy quyền tường minh để cầu viện hàm vẽ điểm ảnh của Bảng vẽ, và vòng lặp bối cảnh lại được duy trì ở đối tượng Điểm điều khiển… Cơ chế độ trễ phân giải ngữ cảnh này chính là thứ phép màu đã biến những khối mã nguồn rời rạc trở thành một hệ thống phản ứng linh hoạt và cực kỳ thông minh.

Tính linh hoạt của ngữ cảnh và lợi thế kiểm thử

Toàn bộ ba cái đối tượng vật lý này đều ôm giữ những phương thức mà rốt cuộc lại quay ra bóp cò triệu hồi lẫn nhau một cách chéo ngoe. Thế nhưng cái mạng lưới triệu hồi này hoàn toàn không bị đổ bê tông hay nối dây cứng ngắc vào nhau một chút nào. Hàm kết xuất khung cảnh của Bảng vẽ tuyệt đối không hề réo thẳng tên hàm vẽ của Điểm điều khiển, mà nó chỉ khiêm nhường gọi hàm vẽ thông qua từ khóa this… Sự phân định rạch ròi này mang một tầm quan trọng sống còn, bởi vì nó mang lại một ý nghĩa vĩ đại rằng hàm kết xuất khung cảnh của Bảng vẽ sở hữu một độ đàn hồi cực cao để có thể bị vác đi xài ở trong một cái bối cảnh từ khóa this hoàn toàn khác biệt – lấy ví dụ, nó có thể bị ép phải chạy chung với một chủng loại đối tượng điểm ngoại lai nào đó khác chứ không nhất thiết cứ phải là đối tượng Điểm điều khiển… Chính sự linh hoạt tuyệt đối này đã tháo gỡ hoàn toàn mọi rào cản phụ thuộc vòng, giúp cho các thành phần phần mềm có thể tồn tại độc lập, dễ dàng được nâng cấp hoặc thay thế mà không gây ra những cơn địa chấn sụp đổ dây chuyền trong toàn bộ hệ thống.

Chính nhờ sự can thiệp của cái bối cảnh từ khóa this, song hành cùng với sợi dây liên kết nguyên mẫu ẩn, mà ba cái đối tượng vật lý này về cơ bản đã bị hệ thống nhào nặn (hay nói cách khác là kết hợp) lại với nhau một cách ảo diệu, chính xác theo những gì được hệ thống đòi hỏi ở từng bước thực thi, để rồi rốt cuộc chúng phối hợp hành động ăn ý với nhau y hệt như thể chúng là một khối đối tượng duy nhất khổng lồ chứ không phải là ba cá thể tách biệt… Đó chính là cái vẻ đẹp rực rỡ và lộng lẫy nhất của triết lý kết hợp ảo khi nó được hiện thực hóa thông qua khuôn mẫu ủy quyền trong hệ sinh thái ngôn ngữ JavaScript. Tôi đã từng bóng gió nhắc đến ở phần trước rằng chúng ta hoàn toàn có thể ném thêm những đối tượng vật lý khác vào trong cái mớ hỗn độn này một cách cực kỳ trơn tru và dễ dàng. Để chứng minh cho lời nói đó, đây là một ví dụ minh họa: chúng ta định nghĩa thêm hàm vẽ đường thẳng cho Bảng vẽ, tạo một hàm nhà máy sinh ra điểm neo đường thẳng kế thừa từ Tọa độ, và cuối cùng tạo ra một đối tượng Đường dẫn hướng sở hữu các điểm neo này, gọi hàm vẽ đường thẳng từ Bảng vẽ thông qua ủy quyền tường minh. Kiến trúc này cho thấy chúng ta có thể tái sử dụng gần như toàn bộ cơ sở hạ tầng đồ họa và tọa độ đã xây dựng từ trước chỉ bằng vài dòng mã liên kết ngữ cảnh.

Thật sự là quá đỗi thanh lịch và tuyệt vời, theo quan điểm cá nhân của tôi là như vậy! Thế nhưng tôi mang một niềm tin sắt đá rằng vẫn còn lẩn khuất một cái siêu lợi ích khác tinh tế hơn rất nhiều và không dễ bị nhãn quan thông thường phát hiện ra, đó là cái việc sở hữu những đối tượng được móc nối với nhau một cách cực kỳ biến động thông qua bối cảnh của từ khóa this có xu hướng biến cái công cuộc viết mã kiểm thử độc lập cho từng phân vùng khác nhau của chương trình trở nên dễ thở hơn gấp bội phần. Lấy một ví dụ điển hình, phương thức thiết lập nguyên mẫu của đối tượng hoàn toàn có thể bị lôi ra xài để dùng vũ lực bẻ lái sợi dây liên kết nguyên mẫu ẩn của một đối tượng ngay tại thời điểm chạy, ép nó phải ủy quyền sang cho một đối tượng hoàn toàn khác biệt, chẳng hạn như một đối tượng giả lập dùng để chạy thử nghiệm. Hoặc là bạn hoàn toàn có thừa thẩm quyền để định nghĩa lại hàm vẽ và hàm kết xuất của đối tượng Đường dẫn hướng ngay tại thời điểm thực thi để ép chúng phải ủy quyền tường minh sang cho một cái Bảng vẽ giả lập thay vì xài cái Bảng vẽ hàng thật giá thật kia. Cái từ khóa this đó, song hành cùng với sợi dây liên kết nguyên mẫu ẩn, đích thị là một cỗ máy cơ học mang trong mình sức mạnh đàn hồi khủng khiếp một khi bạn thực sự thấu cảm và bòn rút được toàn bộ tinh hoa tiềm ẩn của chúng.

So sánh triết lý truyền ngữ cảnh và tương lai thiết kế

Mọi thứ đến thời điểm này hy vọng đã trở nên sáng tỏ như ban ngày rằng khuôn mẫu thiết kế ủy quyền đang dựa dẫm một cách cực kỳ nặng nề vào các nguồn dữ liệu đầu vào mang tính chất ngầm định, thực thi việc san sẻ bối cảnh hoạt động thông qua sức mạnh của từ khóa this thay vì phải nai lưng ra truyền nó qua lại một cách tường minh dưới dạng một tham số đàng hoàng. Bạn hoàn toàn có đầy đủ lý lẽ kỹ thuật sắc bén để vặn vẹo và đặt ra một câu hỏi chất vấn rằng, tại sao chúng ta không lựa chọn một con đường an toàn hơn là cứ việc truyền cái bối cảnh đó đi khắp nơi một cách tường minh cho nó rõ ràng minh bạch? Chúng ta chắc chắn một trăm phần trăm là CÓ THỂ làm được cái trò đó, thế nhưng… để có thể tự tay truyền cái bối cảnh cần thiết đó đi theo các hàm, chúng ta sẽ bị ép buộc phải tiến hành phẫu thuật và sửa đổi gần như toàn bộ mọi cái chữ ký hàm đang tồn tại trong hệ thống, và kéo theo đó là một cuộc đại tu toàn diện toàn bộ những cái vị trí triệu hồi tương ứng với các hàm đó. Việc nhồi nhét thêm hàng tá những tham số ngữ cảnh vào chữ ký hàm không chỉ làm cho đoạn mã trở nên phình to và xấu xí một cách khó chịu, mà nó còn tạo ra một sự gắn kết chặt chẽ chết người giữa các hàm, phá vỡ đi tính trừu tượng vốn có và làm giảm sút nghiêm trọng khả năng tái sử dụng của các module phần mềm.

Chúng ta hãy cùng nhau lật lại cái ví dụ về sự ủy quyền của đối tượng Điểm điều khiển trước đó, và thử tự tay triển khai lại nó nhưng lần này là cấm tiệt mọi hình thức san sẻ bối cảnh từ khóa this mang định hướng ủy quyền. Hãy căng mắt ra và soi thật kỹ vào những điểm dị biệt đang phơi bày: trong mọi hàm của Bảng vẽTọa độ, chúng ta bắt buộc phải nhét thêm tham số ngữ cảnh bảng vẽ hoặc tham số thực thể vào chữ ký hàm; trong các hàm của Điểm điều khiển, chúng ta phải gọi tường minh các hàm đó và tự tay truyền đối tượng hiện tại hoặc ngữ cảnh bảng vẽ vào làm tham số. Để nói một lời công bằng và không thiên vị, rất có khả năng một số người trong số các bạn đang đọc cuốn sách này sẽ nghiêng về và dành sự ưu ái cho cái phong cách viết mã rành mạch đó. Và nếu như bạn đã chọn cắm trại ở phe đó thì điều đó cũng hoàn toàn ổn thôi và không có gì đáng trách cả. Cái đoạn mã chắp vá đó đã thành công trong việc đoạn tuyệt hoàn toàn với mạng lưới liên kết nguyên mẫu ẩn, và giờ đây nó chỉ biết cắn răng dựa dẫm vào một số lượng ít ỏi hơn rất nhiều những sợi dây tham chiếu mang phong cách từ khóa this chấm mộc mạc chuyên dùng để truy cập vào các thuộc tính và phương thức. Cách tiếp cận này tuy loại bỏ được sự kỳ diệu của ngữ cảnh động nhưng lại đánh đổi bằng một lớp mã nguồn mang tính thủ tục khô khan và nặng nề hơn rất nhiều.

Trái ngược hoàn toàn với cái sự rành mạch đó, cái phong cách viết mã định hướng ủy quyền mà tôi đang khản cổ bênh vực và cổ xúy xuyên suốt cái chương sách này lại mang một dáng vẻ xa lạ và sử dụng mạng lưới liên kết nguyên mẫu ẩn cũng như trò san sẻ từ khóa this theo những phương thức ma quỷ mà rất có khả năng bạn chưa từng được làm quen hay trải nghiệm qua. Để có thể vận dụng cái phong cách dị hợm đó một cách sắc bén và mang lại hiệu quả thực chiến, bạn sẽ bị ép buộc phải đầu tư một khối lượng khổng lồ thời gian và công sức cày cuốc thực hành để bồi đắp cho mình một sự thấu cảm sâu sắc hơn đến tận xương tủy. Thế nhưng theo góc nhìn và sự đánh giá của cá nhân tôi, cái cái giá phải trả của việc ngoan cố chối bỏ sức mạnh kết hợp ảo thông qua cơ chế ủy quyền sẽ bị phản chiếu một cách đau đớn và rõ nét lên toàn bộ hàng tá những chữ ký hàm và các vị trí triệu hồi trong chương trình; tôi thấy chúng thực sự trở nên bừa bộn, cồng kềnh và rác rưởi hơn rất nhiều. Cái gánh nặng phải tự tay nhồi nhét và truyền cái bối cảnh đó một cách tường minh quả thực là một khoản thuế khóa vô lý và cực kỳ đắt đỏ giáng xuống đầu những người bảo trì mã nguồn. Thực tế mà nói, tôi sẽ không bao giờ đời nào hé môi ra để khuyên bạn nên xài cái phong cách viết mã rườm rà đó. Nếu như tâm trí của bạn đã nhất quyết muốn đoạn tuyệt và xa lánh cơ chế ủy quyền, thì giải pháp tối ưu nhất có lẽ là bạn chỉ việc an phận và bám chặt lấy cái phong cách viết mã dựa trên từ khóa lớp an toàn, giống y hệt như những gì mà chúng ta đã từng chiêm ngưỡng ở Chương 3. Để kết thúc, tôi xin gửi lại một bài tập vận động trí não dành cho độc giả: hãy thử tự tay mình xắn áo lên và chuyển đổi toàn bộ những cái đoạn mã minh họa về Điểm điều khiểnĐường dẫn hướng lúc nãy sang xài từ khóa lớp xem sao.

Kết luận

Chương sách này đã khép lại một hành trình tái cấu trúc tư duy vô cùng gian nan nhưng cũng đầy sự giác ngộ về bản chất thực sự của ngôn ngữ JavaScript. Chúng ta đã cùng nhau đập bỏ cái vỏ bọc hào nhoáng của từ khóa lớp, phanh phui sự lãng phí của toán tử tạo mới, và từ chối sự kìm kẹp của các cấu trúc phân cấp truyền thống để vươn tới một triết lý thiết kế phẳng và linh hoạt hơn rất nhiều: mô hình ủy quyền ngang hàng. Thông qua việc phân tích chuyên sâu về sự kết hợp ảo, mạng lưới liên kết nguyên mẫu ẩn, và cơ chế độ trễ phân giải ngữ cảnh của từ khóa this, chúng ta nhận ra rằng JavaScript sở hữu một sức mạnh kiến trúc vô song mà các ngôn ngữ lập trình tĩnh khác phải thèm khát. Việc làm chủ mô hình ủy quyền không chỉ giúp chúng ta giải phóng mã nguồn khỏi những phụ thuộc cứng nhắc, tối ưu hóa quá trình kiểm thử và tái sử dụng, mà nó còn là một bài tập rèn luyện tư duy để thấu hiểu đến tận cùng cốt lõi của cỗ máy thực thi. Dù bạn có quyết định ứng dụng mô hình này vào thực tiễn sản xuất hay vẫn trung thành với các khung làm việc định hướng lớp hiện đại, thì việc thấu tỏ triết lý ủy quyền chắc chắn sẽ biến bạn trở thành một kiến trúc sư phần mềm sắc bén và bản lĩnh hơn rất nhiều.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 1.1 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 1.2 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 1.3 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 1.4 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.1 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.2 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.3 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.4 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.5 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.6 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.7 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 2.8 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 3.1 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 3.2 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 3.3 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 3.4 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 3.5 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 4.1 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 4.2 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 4.3 tại đây.

Đọc Giải mã bản chất cốt lõi của JavaScript chương 4.4 tại đây.

Giải mã bản chất cốt lõi của JavaScript | Chương 3.5 818 – thu vien, viet lach, javascript, lap trinh, lap trinh web, web development, ydkjs, get started, you dont know js yet, chua biet javascript, chua biet ro javascript, kyle simpson, coercion, type awareness, triet ly lap trinh, giai ma javascript, giai ma ban chat coi loi javascript.
Giải mã bản chất cốt lõi của JavaScript | Chương 3.5.

Chuyên mục you-dont-know-js-yet

Chuyên mục javascript

Theo dõi hành trình

Hãy để lại thông tin, khi có gì mới thì Nhà văn sẽ gửi thư đến bạn để cập nhật. Cam kết không gửi email rác.

Họ và tên

Email liên lạc

Đôi dòng chia sẻ