Giải mã bản chất cốt lõi của JavaScript (Phần 3, chương 1) 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
Mọi thứ trong ngôn ngữ lập trình JavaScript đều là một đối tượng. Đây là một trong những quan niệm phổ biến nhất, lan truyền rộng rãi nhất, nhưng đồng thời cũng là một trong những sự thật sai lầm nghiêm trọng nhất liên tục được lưu truyền trong cộng đồng lập trình viên về ngôn ngữ JavaScript. Hành trình khám phá cuốn sách này sẽ được bắt đầu bằng việc đập tan những huyền thoại và lầm tưởng kinh điển đó để mang lại một góc nhìn học thuật chuẩn xác hơn. Ngôn ngữ JavaScript chắc chắn sở hữu hệ thống các đối tượng, thế nhưng điều đó tuyệt đối không đồng nghĩa với việc mọi giá trị tồn tại trong ngôn ngữ này đều mặc nhiên mang thân phận là một đối tượng. Mặc dù vậy, chúng ta không thể phủ nhận một thực tế rành rành rằng đối tượng chính là kiểu giá trị mang tầm quan trọng sống còn nhất, đồng thời cũng là kiểu dữ liệu biến hóa đa dạng nhất trong toàn bộ hệ sinh thái của ngôn ngữ này. Chính vì lý do đó, việc rèn luyện để đạt đến cảnh giới thuần thục trong việc thao tác và làm chủ hệ thống đối tượng là một bước đệm mang tính chất bắt buộc và mang tính quyết định trên con đường chinh phục ngôn ngữ JavaScript của bất kỳ nhà phát triển nào.
Cơ chế đối tượng tồn tại trong ngôn ngữ JavaScript chắc chắn là một hình thái vật chứa linh hoạt nhất và mang trong mình quyền năng to lớn nhất – một thứ không gian kiến trúc hoàn hảo để các lập trình viên có thể nhồi nhét những giá trị khác vào bên trong để tiến hành quản lý và lưu trữ. Gần như mọi chương trình JavaScript mà các nhà phát triển chắp bút viết ra đều sẽ phải viện đến sự phục vụ của các đối tượng theo cách này hay cách khác, chứng minh tính ứng dụng phổ quát của chúng trong thực tiễn. Thế nhưng, tính hữu dụng thực tế đó vẫn chưa phải là lý do sâu xa nhất giải thích vì sao đối tượng lại chiếm giữ vị trí độc tôn và được vinh danh ở vị trí trung tâm trong cuốn sách này. Nguyên nhân cốt lõi nằm ở chỗ đối tượng đóng vai trò là khối bê tông nền móng vững chắc nâng đỡ cho trụ cột thứ hai trong ba trụ cột vĩ đại định hình nên toàn bộ ngôn ngữ JavaScript: nguyên mẫu. Khái niệm nguyên mẫu, song hành cùng với từ khóa this vốn sẽ được mổ xẻ chi tiết ở phần sau của cuốn sách, nắm giữ một vị trí cốt lõi mang tính chất sinh tử đối với ngôn ngữ JavaScript. Bên cạnh hàng tá những lý do học thuật khác, nguyên mẫu chính là con đường độc đạo giúp cho hệ thống đối tượng của ngôn ngữ JavaScript có thể diễn đạt và phô diễn được khuôn mẫu thiết kế lớp, một trong những khuôn mẫu thiết kế được tôn sùng và ứng dụng rộng rãi bậc nhất trong toàn bộ lịch sử của thế giới lập trình phần mềm. Vì vậy, chuyến du hành học thuật của chúng ta trong ấn bản này sẽ được khởi động bằng việc mổ xẻ sâu sắc về các đối tượng, từng bước xây dựng một nền tảng nhận thức toàn diện về các nguyên mẫu, vén bức màn bí ẩn bao trùm lên từ khóa this, và cuối cùng là thực hiện một cuộc thám hiểm sâu vào hệ thống cấu trúc lớp.
Hành trình khám phá hệ thống đối tượng và cấu trúc sách
Ấn bản thứ ba trong chuỗi tài liệu nghiên cứu này đánh dấu một bước chuyển mình quan trọng trong việc cập nhật kiến thức cốt lõi của ngôn ngữ JavaScript, đặt trọng tâm vào hệ thống đối tượng và các khuôn mẫu thiết kế hiện đại.
Nền tảng kiến thức và bối cảnh ra đời của ấn bản mới
Chào mừng các nghiên cứu sinh và các nhà phát triển bước vào cánh cửa của cuốn sách thứ ba nằm trong chuỗi tài liệu Giải mã bản chất cốt lõi của JavaScript, (You don’t know JS yet) Nếu độc giả đã xuất sắc vượt qua cuốn sách đầu tiên mang tên Khởi đầu và cuốn sách thứ hai mang tên Phạm vi và bao đóng, thì độc giả đã trang bị cho mình một hành trang hoàn hảo và đang đứng ở đúng tọa độ cần thiết để tiếp thu những kiến thức trong ấn bản này. Trong trường hợp độc giả chưa kịp hoàn thành hai tác phẩm tiền nhiệm đó, tôi cực kỳ khuyến khích độc giả nên quay lại và tiêu hóa trọn vẹn nền tảng lý thuyết trong hai cuốn sách đó để làm bệ phóng học thuật vững chắc trước khi dấn thân vào những khái niệm phức tạp trong cuốn sách này. Việc xây dựng nền móng vững chắc về phạm vi từ vựng và hiện tượng bao đóng là điều kiện tiên quyết để có thể thấu hiểu trọn vẹn cách thức hệ thống đối tượng và nguyên mẫu vận hành dưới lớp vỏ bọc của ngôn ngữ.
Phiên bản đầu lòng của cuốn sách này từng được ra mắt với tiêu đề Từ khóa this và nguyên mẫu đối tượng… Trong cuốn sách tiền nhiệm đó, chúng tôi đã khởi động hành trình bằng cách dồn toàn bộ hỏa lực phân tích vào từ khóa this, bởi vì không thể phủ nhận nó là một trong những chủ đề học thuật gây lú lẫn và tạo ra nhiều sự hoang mang tột độ nhất trong toàn bộ hệ sinh thái ngôn ngữ JavaScript. Phần lớn thời lượng còn lại của ấn bản đầu tiên đó đã được dành ra để phơi bày hệ thống nguyên mẫu một cách trần trụi, đồng thời cổ vũ mạnh mẽ cho việc dang tay đón nhận khuôn mẫu thiết kế ủy quyền vốn ít được biết đến hơn, thay vì cứ mãi bám víu vào các thiết kế lớp truyền thống. Tại thời điểm chắp bút cho ấn bản đó vào năm 2014, viễn cảnh chuẩn mực Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6 chính thức hoàn thiện vẫn còn là một chặng đường dài gần hai năm nữa. Do đó, tôi đã nhận định rằng những bản phác thảo sơ khai đầu tiên về từ khóa class lúc bấy giờ chỉ xứng đáng nhận được một phần phụ lục ngắn gọn để lướt qua nhằm cung cấp thông tin bề mặt mà thôi.
Sẽ là một sự đánh giá thấp đến mức nực cười nếu như chúng ta chỉ nói nhẹ nhàng rằng đã có rất nhiều cuộc bể dâu và sự thay đổi long trời lở đất xảy ra trong bức tranh toàn cảnh của ngôn ngữ JavaScript trong suốt quãng thời gian ngót nghét tám năm kể từ khi ấn bản đầu tiên đó ra đời. Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6 giờ đây đã lùi sâu vào dĩ vãng và trở thành một thứ tin tức cũ kỹ; ngay tại cái khoảnh khắc mà cuốn sách này đang được viết ra, ngôn ngữ JavaScript đã kiêu hãnh trải qua bảy đợt nâng cấp tiêu chuẩn thường niên liên tiếp kể từ sau thời kỳ Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6, kéo dài từ Tiêu chuẩn ngôn ngữ kịch bản châu Âu 2016 cho đến tận Tiêu chuẩn ngôn ngữ kịch bản châu Âu 2022. Sự tiến hóa vũ bão này đòi hỏi một góc nhìn hoàn toàn mới mẻ và những sự diễn giải sâu sắc hơn đối với các khái niệm cốt lõi.
Sự tiến hóa của ngôn ngữ JavaScript từ quá khứ đến hiện tại
Xuyên suốt quá trình phát triển không ngừng nghỉ của ngôn ngữ, có một thực tế không thể chối cãi là cho đến tận thời điểm hiện tại, chúng ta vẫn bắt buộc phải đưa lên bàn mổ học thuật cái cách mà từ khóa this thực sự vận hành, cũng như cái sợi dây liên kết chặt chẽ giữa nó với các phương thức được gọi ra để thực thi chống lại các đối tượng muôn hình vạn trạng. Hơn thế nữa, bản thân từ khóa class thực chất vẫn đang âm thầm thao túng các hành vi lập trình thông qua cái chuỗi nguyên mẫu nằm tít sâu bên dưới lớp vỏ bọc, trong đại đa số các trường hợp vận hành. Tuy nhiên, thực tại phũ phàng và khách quan đang diễn ra trong ngành công nghiệp phần mềm là các nhà phát triển JavaScript trong năm 2022 gần như vĩnh viễn không bao giờ phải hì hục tự tay viết mã để nối dây một cách tường minh cho cái cơ chế kế thừa nguyên mẫu nữa. Các công cụ hiện đại và cú pháp mới đã trừu tượng hóa toàn bộ những công đoạn thủ công phức tạp này.
Bất chấp việc cá nhân tôi có khao khát một viễn cảnh khác biệt đến thế nào đi chăng nữa, thì sự thật là các khuôn mẫu thiết kế lớp – chứ tuyệt đối không phải là hệ tư tưởng ủy quyền hành vi – mới chính là thứ ngôn ngữ thống trị để diễn đạt phần lớn các cách thức tổ chức dữ liệu và cấu trúc hành vi trong hệ sinh thái ngôn ngữ JavaScript hiện đại. Sự chuyển dịch từ mô hình nguyên mẫu thuần túy sang mô hình hướng đối tượng dựa trên lớp đã đánh dấu một bước ngoặt về mặt nhận thức của cộng đồng lập trình viên. Điều này không có nghĩa là nguyên mẫu đã chết, mà nó đã được bao bọc bởi một lớp cú pháp thanh lịch hơn, dễ tiếp cận hơn cho những người chuyển từ các ngôn ngữ lập trình cổ điển sang.
Sự trỗi dậy của cú pháp lớp trong ngôn ngữ JavaScript đã mang lại một sự đồng nhất nhất định trong tư duy thiết kế phần mềm toàn cầu, giúp giảm thiểu rào cản học tập cho các tân binh. Tuy nhiên, sự tiện lợi bề mặt này cũng đồng thời sinh ra một thế hệ lập trình viên chỉ hiểu bề nổi mà hoàn toàn mù tịt về cơ chế vận hành nội tại. Chính sự thiếu hụt kiến thức nền tảng này đã dẫn đến những lỗi thiết kế kiến trúc nghiêm trọng khi xử lý các bài toán phức tạp liên quan đến tính kế thừa đa tầng và hiện tượng rò rỉ bộ nhớ. Do đó, việc giáo dục lại cộng đồng về bản chất thực sự của đối tượng và lớp là một sứ mệnh mang tính cấp thiết hơn bao giờ hết.
Trọng tâm cốt lõi và định hướng tiếp cận của cuốn sách
Cuốn sách này là tấm gương phản chiếu chân thực nhất cái thực tại phũ phàng nhưng cũng đầy sinh động đó của ngôn ngữ JavaScript thời kỳ mới. Đó chính là lý do trực tiếp dẫn đến sự ra đời của một cái tiêu đề phụ hoàn toàn mới, một cấu trúc tổ chức và định hướng chủ đề hoàn toàn mới toanh, và toàn bộ phần văn bản cốt lõi của phiên bản trước đã bị đập đi xây lại hoàn toàn để đáp ứng tiêu chuẩn học thuật hiện tại. Chúng tôi không chỉ đơn thuần là cập nhật cú pháp, mà chúng tôi đang tiến hành tái cấu trúc lại toàn bộ hệ tư tưởng tiếp cận hệ thống đối tượng để nó phù hợp với cách thức mà thế giới phần mềm hiện đại đang vận hành.
Mục tiêu tối thượng của ấn bản này không chỉ là liệt kê các tính năng mới, mà là cung cấp một bản thiết kế kiến trúc hoàn chỉnh về cách ngôn ngữ JavaScript xử lý dữ liệu thông qua đối tượng. Bằng cách mổ xẻ từng phương thức thao tác đối tượng, từng quy tắc gán thuộc tính và từng cơ chế truy cập, chúng tôi kỳ vọng sẽ xây dựng lại một nền tảng tư duy vững chắc cho độc giả. Lập trình viên không nên chỉ là những người thợ gõ mã nguồn, mà họ cần phải trở thành những nhà kiến trúc sư thực thụ, những người thấu hiểu từng luồng dữ liệu chạy qua bộ nhớ thông qua các vật chứa đối tượng.
Cuối cùng, sự thống nhất giữa lý thuyết và thực tiễn sẽ được đan cài xuyên suốt các chương tiếp theo. Chúng tôi sẽ phân tích các tình huống thực tế, từ những cú pháp viết tắt tiện lợi cho đến những bài toán sao chép bộ nhớ phức tạp. Hành trình này không hứa hẹn sẽ dễ dàng, nhưng nó cam kết sẽ mang lại sự bừng tỉnh về mặt nhận thức, giúp độc giả hoàn toàn làm chủ được công cụ mạnh mẽ nhất trong ngôn ngữ JavaScript: Đối tượng.
Đối tượng với tư cách là vật chứa dữ liệu
Khái niệm đối tượng trong lập trình không chỉ là một danh từ trừu tượng, mà nó đại diện cho một cơ chế cấu trúc bộ nhớ tinh vi giúp tối ưu hóa việc quản lý và lưu chuyển dữ liệu.
Khái niệm cơ bản và cú pháp khởi tạo đối tượng trực tiếp
Một chiến thuật vô cùng phổ biến và mang tính ứng dụng cao nhất để gom tụ hàng tá các giá trị riêng lẻ lại với nhau và nhét chung vào một cái vật chứa duy nhất chính là sử dụng một đối tượng. Hiểu theo một cách tường minh và mang tính học thuật nhất, đối tượng về bản chất là một bộ sưu tập chứa đựng các cặp định danh bao gồm khóa và giá trị. Bên trong hệ sinh thái phức tạp của ngôn ngữ JavaScript, còn tồn tại vô số những kiểu phụ của đối tượng, mang trong mình những hành vi dị biệt và được chuyên biệt hóa cho những mục đích cụ thể, điển hình như kiểu mảng vốn được lập chỉ mục bằng những con số tuyến tính, và thậm chí là cả các hàm vốn có khả năng được gọi ra để thực thi các khối mã; chúng ta sẽ cùng nhau xẻ thịt những kiểu phụ đa dạng này một cách chi tiết hơn ở những phần lý thuyết chuyên sâu sau này.
Cần phải thiết lập một quy chuẩn về mặt thuật ngữ ngay từ ban đầu: các khóa thường xuyên được giới học thuật và lập trình viên xưng tụng bằng khái niệm tên thuộc tính, và sự kết đôi hoàn hảo giữa một cái tên thuộc tính với một cái giá trị được lưu trữ thường được gọi tắt là một thuộc tính… Xuyên suốt toàn bộ văn bản của cuốn sách này, chúng ta sẽ thiết lập một sự phân định rạch ròi và duy trì sự nhất quán tuyệt đối trong việc sử dụng những thuật ngữ nền tảng đó để tránh gây ra bất kỳ sự nhầm lẫn nào về mặt ngữ nghĩa. Những đối tượng JavaScript thông thường đa phần đều được khai sinh và khởi tạo bằng cú pháp khai báo trực tiếp, ví dụ như myObj = . Vẫn còn tồn tại một con đường tà đạo khác để khởi tạo một đối tượng thông qua việc sử dụng câu lệnh myObj = new Object(), thế nhưng đây hoàn toàn không phải là một cách tiếp cận phổ biến hay được giới tinh hoa lập trình khuyến khích, và nó gần như vĩnh viễn không bao giờ là một sự lựa chọn khôn ngoan hay phù hợp trong thực tiễn xây dựng phần mềm. Lời khuyên xương máu là các nhà phát triển hãy cứ trung thành tuyệt đối với cú pháp khai báo đối tượng trực tiếp.
Bộ não con người rất dễ bị đánh lừa và rơi vào trạng thái lú lẫn khi cố gắng giải mã xem rốt cuộc những cặp ngoặc nhọn mang ý nghĩa thực sự là gì, bởi vì ngôn ngữ JavaScript đã lạm dụng một cách quá đà ký hiệu ngoặc nhọn này để bắt nó phải gồng gánh nhiều ý nghĩa khác nhau, phụ thuộc hoàn toàn vào ngữ cảnh cú pháp mà nó đang đứng. Cụ thể, cặp ngoặc nhọn có thể làm hàng rào phân cách các giá trị trong cú pháp khai báo đối tượng trực tiếp, hoặc được dùng để vạch ra những khuôn mẫu phân rã đối tượng, làm ranh giới cho các biểu thức chuỗi nội suy, định hình các khối mã trong các câu lệnh điều kiện và vòng lặp, hoặc định nghĩa phần nội tạng của các hàm. Mặc dù sự đa nghĩa này thỉnh thoảng có thể biến việc phân tích mã nguồn thành một thử thách khắc nghiệt, nhưng hãy tập thói quen soi xét xem liệu một cặp dấu ngoặc nhọn đang nằm ở một tọa độ nào trong chương trình nơi mà ngữ pháp cho phép một giá trị hoặc biểu thức được quyền xuất hiện; nếu đúng là như vậy, thì nó đích thị là một cú pháp khai báo đối tượng trực tiếp, còn nếu không, nó chắc chắn là một trong những vỏ bọc mang ý nghĩa bị lạm dụng khác.
Cơ chế định nghĩa thuộc tính và sự khác biệt với định dạng dữ liệu
Nằm lọt thỏm bên trong cặp ngoặc nhọn của cú pháp khai báo đối tượng trực tiếp, các lập trình viên sẽ tiến hành định nghĩa các thuộc tính bao gồm tên và giá trị dưới hình hài của những cặp được phân tách bằng dấu hai chấm, ví dụ như tên_thuộc_tính: giá_trị. Những giá trị được phân bổ và dán cho các thuộc tính này hoàn toàn có thể là những giá trị trực tiếp đã được định sẵn, hoặc chúng có thể được cỗ máy thực thi của ngôn ngữ cày cuốc và tính toán ra thông qua một biểu thức động ngay tại thời điểm chạy chương trình. Biểu thức gắn liền với giá trị sẽ bị hệ thống đem ra đánh giá và tính toán ngay tắp lự, và kết quả thu được cuối cùng sẽ được sử dụng để gán làm giá trị chính thức cho thuộc tính đó.
Các nhà phát triển phần mềm thỉnh thoảng lại vắt óc suy nghĩ và đặt ra câu hỏi học thuật xem liệu có tồn tại một cơ chế ma thuật nào đó để có thể định nghĩa một biểu thức dùng làm giá trị cho một thuộc tính mà biểu thức đó lại mang đặc tính lười biếng hay không. Đặc tính lười biếng ở đây mang ý nghĩa là biểu thức đó sẽ được đặc cách miễn trừ việc phải thực thi tính toán ngay tại khoảnh khắc được gán, mà thay vào đó sẽ được trì hoãn để tính toán vào một thời điểm nào đó trong tương lai khi thực sự cần thiết. Sự thật phũ phàng là ngôn ngữ JavaScript hoàn toàn mù tịt và không hỗ trợ cơ chế đánh giá các biểu thức lười biếng một cách gốc rễ, thế nên con đường độc đạo duy nhất để có thể hiện thực hóa khát khao thiết kế đó là phải nhét biểu thức đó lọt thỏm vào bên trong phần thân của một cấu trúc hàm. Trong kịch bản này, thuộc tính hoàn toàn không hề ôm giữ một giá trị dữ liệu tĩnh nào cả, mà thực chất nó đang ôm một sợi dây tham chiếu trỏ thẳng đến cấu trúc hàm đó; để có thể ép hệ thống nôn ra kết quả cuối cùng, sợi dây tham chiếu hàm đó bắt buộc phải bị chủ động lôi ra và ép chạy một cách tường minh.
Rất có thể các nghiên cứu sinh đã tinh ý nhận ra một sự thật rằng cú pháp khai báo đối tượng trực tiếp mà chúng ta miệt mài phân tích từ nãy đến giờ mang một hình hài kết cấu khá là tương đồng với một thứ cú pháp có họ hàng gần gũi với nó, đó là Ký pháp đối tượng JavaScript. Những hố sâu ngăn cách sự khác biệt rõ rệt nhất giữa cú pháp khai báo đối tượng trực tiếp của ngôn ngữ JavaScript và Ký pháp đối tượng JavaScript là: đối với những đối tượng bị ép phải định nghĩa dưới dạng Ký pháp đối tượng JavaScript, toàn bộ các tên thuộc tính bắt buộc phải bị cùm kẹp chặt chẽ trong cặp ký tự ngoặc kép, và toàn bộ các giá trị thuộc tính bắt buộc phải là những giá trị trực tiếp cơ bản chứ tuyệt đối không được phép là những biểu thức ngôn ngữ tự do tùy tiện. Khi lặn ngụp trong các chương trình phần mềm thực tế, một cú pháp khai báo đối tượng trực tiếp hoàn toàn không đưa ra một yêu cầu khắt khe nào về việc các tên thuộc tính phải bị đóng ngoặc kép – các nhà phát triển hoàn toàn có quyền đóng ngoặc kép chúng, thế nhưng trong đại đa số các trường hợp thì đó là một sự lựa chọn thiết kế không bắt buộc. Tuy nhiên, vẫn lẩn khuất những ký tự hoàn toàn hợp pháp để góp mặt trong một tên thuộc tính, thế nhưng chúng lại bị tước đoạt quyền được xuất hiện trần trụi nếu như không có cặp ngoặc kép bảo vệ, lấy ví dụ điển hình như những con số đứng ở vị trí tiên phong hoặc các khoảng trắng vô hình. Một điểm khác biệt mang tính chất chí mạng khác là, cú pháp Ký pháp đối tượng JavaScript mang một bản chất phân tích khắc nghiệt và độc tài hơn rất nhiều so với ngôn ngữ JavaScript thông thường; Ký pháp đối tượng JavaScript cấm tiệt sự xuất hiện của các đoạn mã bình luận cũng như những dấu phẩy dư thừa nằm trơ trọi ở cuối các biểu thức, dù nó vẫn còn vớt vát lại chút nhân đạo khi cho phép các khoảng trắng được xuất hiện một cách tự do.
Đặc tính của tên thuộc tính và sự trỗi dậy của kiểu dữ liệu biểu tượng
Các tên thuộc tính nằm chễm chệ trong các cú pháp khai báo đối tượng trực tiếp gần như vĩnh viễn đều bị cỗ máy thực thi của hệ thống đối xử và ép buộc chuyển đổi thành những giá trị mang kiểu chuỗi ký tự chuẩn mực. Có một ngoại lệ bé nhỏ phá vỡ quy luật thép này, đó là dành cho những tên thuộc tính mang hình hài vật lý của một số nguyên hoặc trông có vẻ giống như một số nguyên. Một tên thuộc tính dạng số sẽ nghiễm nhiên được hệ thống đối xử như một tên thuộc tính mang kiểu số nguyên hay còn gọi là chỉ mục mảng; một giá trị chuỗi chứa con số cũng sẽ bị ép chịu chung số phận y hệt như vậy bởi vì hình hài bên ngoài của nó trông quá giống với một con số nguyên. Ở một thái cực hoàn toàn đối lập, một giá trị boolean nguyên thủy sẽ ngay lập tức bị biến hình thành một tên thuộc tính kiểu chuỗi ký tự tương ứng, và một tham chiếu định danh đối tượng được hệ thống tính toán thông qua cặp ngoặc vuông bao bọc bên ngoài sẽ tiến hành ép buộc chuyển đổi giá trị của đối tượng đó thành một chuỗi ký tự, với giá trị mặc định thu được thường là [object Object]…
Nếu như các nhà phát triển đang bị dồn vào đường cùng và bắt buộc phải lấy một đối tượng ra để làm khóa hoặc tên thuộc tính, thì xin tuyệt đối đừng bao giờ dại dột đặt cược vào thủ thuật ép kiểu chuỗi được tính toán này; hành vi mà nó sinh ra sẽ là một cú sốc mang tính chất hủy diệt và gần như chắc chắn đi ngược lại hoàn toàn với những gì được kỳ vọng về mặt logic, hệ lụy tất yếu là những lỗi phần mềm nghiêm trọng sẽ thi nhau nảy nở trong chương trình. Lối thoát kiến trúc duy nhất là chuyển hướng sang cầu cứu một cấu trúc dữ liệu chuyên biệt hóa hơn, mang tên Bản đồ vốn là một thứ vũ khí mới được bổ sung vào từ thời Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6, nơi mà những đối tượng được lấy ra làm tên thuộc tính sẽ được bảo tồn nguyên vẹn hình hài tham chiếu ban đầu thay vì bị tàn nhẫn ép biến thành một giá trị chuỗi vô tri. Các lập trình viên hoàn toàn được trao quyền tính toán bất kỳ một tên thuộc tính nào ngay tại thời khắc mà cú pháp khai báo đối tượng trực tiếp đang được định nghĩa thông qua cặp ngoặc vuông; biểu thức tính toán đó sẽ bị hệ thống đem ra xử lý ngay tắp lự, và kết quả chuỗi thu được sẽ vinh dự được chọn làm tên thuộc tính chính thức.
Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6 đã mang đến một cơn địa chấn trong ngành khi bổ sung thêm một kiểu giá trị nguyên thủy mới toanh mang tên Biểu tượng, một thứ vũ khí thường xuyên được trọng dụng như một tên thuộc tính đặc chủng chuyên dùng để lưu trữ và truy xuất các giá trị thuộc tính ẩn. Chúng được hệ thống khởi tạo thông qua lời gọi hàm tạo không có từ khóa new, và có khả năng mở cửa đón nhận một chuỗi mô tả tùy chọn chỉ mang duy nhất một mục đích là làm cho quá trình gỡ lỗi trở nên thân thiện hơn đối với con người. Chuỗi mô tả đó hoàn toàn bị niêm phong và bất khả xâm phạm đối với chương trình, do đó nó vĩnh viễn không bao giờ được dùng cho bất kỳ mục đích logic nào khác ngoại trừ việc in ra kết quả phân tích gỡ lỗi. Biểu tượng mang một nét hao hao giống với những con số hay những chuỗi ký tự thông thường, nhưng điểm làm nên sự khác biệt kinh thiên động địa là giá trị thực sự của chúng hoàn toàn bị che giấu và mang tính độc nhất vô nhị trên toàn cầu bên trong không gian bộ nhớ của chương trình đó. Các nhà phát triển hoàn toàn có quyền tự do khởi tạo và đem các biểu tượng ra sử dụng, thế nhưng cỗ máy thực thi lại tàn nhẫn tước đoạt đi quyền được biết bất cứ thứ gì về, hoặc thao túng bất cứ thứ gì đối với, giá trị vật lý nằm sâu bên dưới; bí mật đó được chôn giấu vô cùng cẩn thận như một chi tiết triển khai nội bộ của trình biên dịch. Bởi vì các biểu tượng được sinh ra với định danh độc nhất vô nhị, nên vĩnh viễn không bao giờ có chuyện xảy ra một vụ va chạm dữ liệu nơi mà một phân vùng nào đó của chương trình lại vô tình định nghĩa ra một tên thuộc tính trùng khớp y hệt như tên mà một phân vùng khác đã từng cố gắng gán trước đó, giúp chúng trở thành công cụ hoàn hảo để móc nối với những hành vi mặc định đặc thù của các hệ thống đối tượng.
Các kỹ thuật nâng cao trong khai báo và nhân bản đối tượng
Để tối ưu hóa hiệu suất lập trình và cấu trúc dữ liệu, ngôn ngữ JavaScript hiện đại cung cấp hàng loạt các kỹ thuật viết tắt và cơ chế sao chép linh hoạt nhằm phục vụ các kiến trúc phần mềm đồ sộ.
Kỹ thuật khai báo thuộc tính và phương thức ngắn gọn
Khi các nhà phát triển đang mải mê nhào nặn ra một cú pháp khai báo đối tượng trực tiếp, một kịch bản thiết kế cực kỳ quen thuộc là họ muốn sử dụng một tên thuộc tính trùng khớp hoàn toàn với một định danh biến đã tồn tại sẵn trong không gian phạm vi, mà định danh đó lại đang lưu giữ giá trị cần được gán. Việc viết lặp lại cả tên thuộc tính lẫn định danh biến thực chất chẳng khác biệt gì so với kiểu định nghĩa bằng tên thuộc tính đóng ngoặc kép truyền thống, thế nhưng đại đa số các nhà phát triển đều hướng tới sự tối ưu và hiếm khi nào chịu viết dài dòng trừ phi họ bị bắt buộc bởi các quy chuẩn cũ. Và quả thực, việc né tránh những đoạn mã dư thừa đã trở thành một thứ văn phong đặc trưng của ngôn ngữ hiện đại, vì vậy việc cố chấp nhét chúng vào một cách thừa thãi là một hành vi bị cộng đồng học thuật kịch liệt lên án vì làm giảm tính thanh lịch của mã nguồn.
Rơi vào kịch bản này, nơi mà tên thuộc tính và định danh của biểu thức giá trị là một cặp sinh đôi giống hệt nhau về mặt ký tự, các lập trình viên hoàn toàn có đặc quyền thẳng tay chém bỏ phần tên thuộc tính của đoạn định nghĩa, để tạo ra một cấu trúc được giới học thuật xưng tụng là định nghĩa thuộc tính ngắn gọn. Tên thuộc tính lúc này vẫn hiển nhiên được ngầm định là một chuỗi ký tự, và giá trị được dán cho thuộc tính đó chính là thứ đang được ôm giữ bên trong biến tham chiếu ngay tại khoảnh khắc khởi tạo. Lần đầu chạm trán, thứ cú pháp viết tắt tiện lợi này có thể sẽ dội vào mặt những người mới học một cảm giác cực kỳ lú lẫn và thiếu tường minh. Thế nhưng khi não bộ đã dần làm quen với việc chứng kiến tính năng cực kỳ phổ biến và được sủng ái này tung hoành ngang dọc trong các dự án thực tế, gần như chắc chắn các nhà phát triển sẽ trở thành tín đồ cuồng nhiệt của nó vì nó giúp tiết kiệm được khối lượng lớn thời gian gõ phím cũng như tăng tốc độ đọc hiểu mã nguồn.
Một tuyệt kỹ viết tắt có họ hàng gần gũi khác là việc định nghĩa các hàm hoặc phương thức nằm lọt thỏm bên trong một cú pháp khai báo đối tượng trực tiếp bằng cách sử dụng một hình hài súc tích hơn rất nhiều thông qua việc loại bỏ từ khóa chức năng. Nhân cơ hội mổ xẻ chủ đề về các thuộc tính phương thức ngắn gọn, hệ thống cũng hoàn toàn cung cấp khả năng định nghĩa ra các hàm máy phát vốn là một tính năng xử lý bất đồng bộ cực kỳ mạnh mẽ của Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6. Và mặc dù nó không phải là một khuôn mẫu thiết kế phổ biến cho lắm trong thực tiễn, nhưng các phương thức hoặc máy phát ngắn gọn thậm chí còn được quyền mang những tên đóng ngoặc kép hoặc những tên được tính toán động từ các biểu thức toán học hoặc thao tác chuỗi.
Cơ chế trải rộng đối tượng và giới hạn của sao chép nông
Vẫn còn một con đường học thuật khác để các nhà phát triển có thể định nghĩa các thuộc tính ngay tại khoảnh khắc mà cú pháp khai báo đối tượng trực tiếp vừa được khai sinh, đó chính là sử dụng một hình thái của cú pháp ba dấu chấm – về mặt kỹ thuật lý thuyết thì nó hoàn toàn không phải là một toán tử thực thụ, thế nhưng vỏ bọc bên ngoài của nó thì lại trông y hệt như một toán tử – một thứ thường xuyên được giới kiến trúc phần mềm gọi tên là cơ chế trải rộng đối tượng. Khi cú pháp ba dấu chấm này lọt thỏm vào bên trong một khai báo đối tượng trực tiếp, nó sẽ thực hiện thao tác trải rộng toàn bộ nội tạng bao gồm các thuộc tính hay các cặp khóa và giá trị của một đối tượng nguồn khác và nhồi nhét thẳng vào đối tượng mục tiêu đang được định nghĩa.
Hành động trải rộng các thuộc tính này mang một bản chất kỹ thuật là sao chép nông, có nghĩa là nó chỉ tiến hành trích xuất những thuộc tính nằm ở tầng lớp bề mặt trên cùng của đối tượng nguồn; bất kỳ giá trị nào mà những thuộc tính đó đang nắm giữ đều sẽ chỉ đơn thuần được thực hiện phép gán tham chiếu sang đối tượng mới. Nếu lỡ như có bất kỳ giá trị nào trong số đó lại là những sợi dây tham chiếu trỏ đến những đối tượng phức tạp khác, thì chính bản thân những sợi dây tham chiếu đó sẽ bị lôi ra gán sao chép, thế nhưng những khối dữ liệu đối tượng nằm tít sâu bên dưới thì tuyệt đối không hề được hệ thống nhân bản tạo ra một bản sao mới – vì vậy kết cục phân bổ bộ nhớ mà chúng ta gặt hái được là một mớ những sợi dây tham chiếu dùng chung cùng trỏ đến những vùng nhớ đối tượng y hệt nhau. Các nhà nghiên cứu hoàn toàn có thể tự hình dung cơ chế trải rộng đối tượng này hoạt động theo thuật toán giống y hệt như một vòng lặp chạy quét qua từng thuộc tính một và thực hiện một thao tác gán tiêu chuẩn từ đối tượng gốc sang đối tượng mục tiêu.
Hơn thế nữa, hãy luôn ghim vào tư duy thiết kế một chân lý là những thao tác định nghĩa thuộc tính này sẽ diễn ra theo một trật tự tuyến tính nghiêm ngặt, chảy từ đỉnh xuống đáy của khối khai báo đối tượng trực tiếp. Nếu đối tượng nguồn vốn dĩ đã sở hữu sẵn một thuộc tính trùng tên với một thuộc tính đã được khai báo trước đó trong đối tượng mục tiêu, thì cơ chế trải rộng đối tượng rốt cuộc sẽ thẳng tay đè bẹp và xóa sổ thao tác gán thuộc tính ở ngay dòng lệnh trước đó. Một điểm mù kỹ thuật cần lưu ý là cơ chế trải rộng đối tượng chỉ trích xuất những thuộc tính thuộc quyền sở hữu riêng bám trực tiếp trên đối tượng nguồn mà lại còn mang cờ trạng thái có thể liệt kê được. Nó tuyệt đối không nhân bản thuộc tính theo nghĩa bắt chước chính xác từng đường tơ kẽ tóc các đặc tính siêu dữ liệu của thuộc tính đó, mà nó chỉ đơn thuần thực hiện một cuộc sao chép mang phong cách phép gán giá trị thuần túy.
Bài toán nhân bản sâu và giải pháp nhân bản cấu trúc
Bởi vì cú pháp trải rộng hoàn toàn bất lực trong việc thực thi một cuộc nhân bản đối tượng toàn diện và đệ quy sâu thẳm, thế nên cơ chế này nhìn chung chỉ xứng đáng được giao phó cho nhiệm vụ nhân bản những đối tượng chỉ ôm giữ những giá trị nguyên thủy mộc mạc nhất, chứ tuyệt đối không phải là những cây cấu trúc dữ liệu chứa các sợi dây tham chiếu trỏ chằng chịt đến những đối tượng khác. Quá trình nhân bản đối tượng sâu thực chất là một bài toán khoa học máy tính cực kỳ phức tạp và đầy rẫy những cạm bẫy về mặt quản lý bộ nhớ. Việc nhân bản một giá trị vô hướng thì quá đỗi hiển nhiên và dễ dàng, thế nhưng việc sao chép một hàm tính toán vốn thực chất là một kiểu đối tượng lai căng đặc biệt cũng bị hệ thống nắm giữ thông qua sợi dây tham chiếu, hay là việc phải đi sao chép một sợi dây tham chiếu đối tượng ngoại lai hoàn toàn không thuộc phạm vi quản lý nội tại của ngôn ngữ JavaScript như một phần tử Mô hình đối tượng tài liệu thì lại là những thách thức khổng lồ.
Thảm họa bộ nhớ sẽ giáng xuống nếu như một đối tượng lại tự dệt nên những tham chiếu vòng tròn quái gở, điển hình như một đối tượng con cháu nằm lọt thỏm tít sâu bên dưới lại cả gan thò một tham chiếu ngược lên để tóm lấy đối tượng tổ tiên bao bọc bên ngoài. Đang có một cuộc chiến tranh học thuật giữa vô vàn những luồng tư tưởng thiết kế trái chiều ngoài kia về việc phải xử trí như thế nào cho ổn thỏa đối với toàn bộ những trường hợp dị biệt điên rồ này, và hệ lụy tất yếu là vĩnh viễn không hề tồn tại một quy chuẩn độc tôn duy nhất nào dùng để làm thước đo vạn năng cho việc nhân bản đối tượng sâu cả. Đối với bài toán nhân bản đối tượng sâu, giới kỹ sư phần mềm thường hay xài hai chiêu thức mang tính tiêu chuẩn truyền thống: thứ nhất là viện đến sự cứu rỗi của một công cụ tiện ích trong các thư viện bên thứ ba vốn mang hệ tư tưởng rõ ràng về cách xử lý các cạm bẫy nhân bản; thứ hai là lạm dụng thủ thuật khứ hồi phân tích Ký pháp đối tượng JavaScript. Thủ thuật khứ hồi này chỉ sống sót một cách trọn vẹn nếu như đối tượng đó hoàn toàn miễn nhiễm với các tham chiếu vòng tròn, và nếu như bên trong đối tượng đó vĩnh viễn không chứa chấp bất kỳ giá trị nào vô phương cứu chữa không thể được tuần tự hóa một cách chuẩn mực, điển hình là các hàm.
Thế nhưng, thời thế công nghệ đã thay đổi, một con đường tiếp cận thứ ba ưu việt hơn vừa mới hạ phàm trong hệ sinh thái. Đây tuyệt đối không phải là một tính năng nội tại của bản thân ngôn ngữ JavaScript, mà thực chất nó là một Giao diện lập trình ứng dụng đồng hành được các môi trường thực thi như nền tảng web ban phát để cung cấp sức mạnh bổ sung. Các đối tượng giờ đây đã có thể được nhân bản sâu thông qua thuật toán nhân bản cấu trúc tích hợp sẵn. Thuật toán bí mật nằm ẩn sâu bên dưới công cụ tiện ích tích hợp sẵn này được trang bị một thứ sức mạnh kinh khủng có khả năng giải quyết trót lọt cả những rào cản tham chiếu vòng tròn, cũng như hỗ trợ một số lượng áp đảo hơn rất nhiều các kiểu giá trị dữ liệu phức tạp so với thủ thuật khứ hồi Ký pháp đối tượng JavaScript lỗi thời. Mặc dù vậy, theo góc nhìn phản biện học thuật, thuật toán tiên tiến này vẫn không thể thoát khỏi những gông cùm giới hạn của riêng nó, bao gồm cả việc hoàn toàn bất lực và từ chối hỗ trợ trong việc nhân bản các khối mã hàm hay các phần tử Mô hình đối tượng tài liệu gắn liền với giao diện người dùng.
Cơ chế truy xuất và thao tác với các thuộc tính
Việc trích xuất dữ liệu từ các vật chứa đối tượng đòi hỏi sự am hiểu về các toán tử truy cập cũng như các cơ chế an toàn nhằm ngăn chặn sự cố tràn lỗi trong các ứng dụng quy mô lớn.
Các phương thức truy cập thuộc tính và kỹ thuật phân rã đối tượng
Việc truy cập để trích xuất một thuộc tính ra khỏi một đối tượng đang sờ sờ tồn tại trong không gian bộ nhớ thì tốt nhất là nên được thực thi bằng toán tử dấu chấm truyền thống. Nếu như con đường truy cập thuộc tính bằng ký pháp dấu chấm này vẫn còn đang rộng mở và hợp lệ về mặt cú pháp, thì các chuyên gia kiến trúc phần mềm cực kỳ khuyến khích các nhà phát triển hãy ưu tiên sử dụng nó để duy trì tính dễ đọc của mã nguồn. Nếu lỡ như tên thuộc tính lại chứa chấp những ký tự bị cấm tiệt không được phép xuất hiện trong các định danh tiêu chuẩn, điển hình như những con số tiên phong hoặc các khoảng trắng, thì ký pháp cặp ngoặc vuông hoàn toàn có thể được triệu hồi ra để thế vai cho toán tử dấu chấm nhằm vượt qua rào cản từ vựng.
Bất chấp một sự thật học thuật là những tên thuộc tính bằng số vẫn bảo toàn được hình hài con số của chúng trong dữ liệu, thế nhưng hành vi truy cập thuộc tính thông qua cặp ngoặc vuông sẽ thẳng tay ép buộc chuyển đổi một lớp vỏ bọc chuỗi ký tự thành một con số học thuật tương đương, và ngay sau đó nó sẽ tiến hành truy cập vào thuộc tính số học tương ứng một cách vô cùng trơn tru dưới tầng vi mạch. Hoàn toàn tương đồng với cú pháp khai báo đối tượng trực tiếp, tên thuộc tính cần phải được truy cập hoàn toàn có thể được hệ thống đem ra tính toán động thông qua cặp ngoặc vuông. Sự thật là, thứ được nhét vào giữa cặp ngoặc vuông đó hoàn toàn có quyền là bất kỳ một biểu thức ngôn ngữ tự do tùy tiện nào, chứ tuyệt đối không bị gò bó trong khuôn khổ của những định danh hay những giá trị trực tiếp mộc mạc. Cỗ máy thực thi sẽ dồn sức mạnh xử lý ra để đánh giá biểu thức đó trước tiên, và thành quả giá trị thu được sau đó sẽ vinh dự được chọn làm tên thuộc tính chính thức để đem đi tra cứu trong bảng băm của đối tượng.
Một chiến thuật tiếp cận hoàn toàn khác biệt để có thể thọc tay vào lấy các thuộc tính cấu trúc chính là sử dụng kỹ thuật phân rã đối tượng, một cơ chế tinh vi vừa mới được cấy ghép vào từ thời kỳ Tiêu chuẩn ngôn ngữ kịch bản châu Âu 6. Các nghiên cứu sinh hãy tự hình dung kỹ thuật phân rã này giống y hệt như việc đang phác thảo ra một khuôn mẫu cấu trúc dùng để lột tả xem một giá trị đối tượng thì đáng lý ra phải mang hình hài phân cấp như thế nào, và sau đó mạnh miệng ra lệnh cho hệ thống phải bám chặt lấy khuôn mẫu đó để tiến hành móc nối và lôi các thứ nội tạng của đối tượng ra một cách có hệ thống chuẩn mực. Đích đến cuối cùng của thủ thuật phân rã đối tượng này hoàn toàn không phải là đẻ ra thêm một đối tượng sao chép khác, mà thực chất nó là việc thi hành hàng tá những thao tác gán nhắm vào những mục tiêu không gian biến số khác bằng những giá trị được rút ruột từ đối tượng gốc. Cú pháp phân rã đối tượng luôn luôn được giới chuyên môn sủng ái và đánh giá cao nhờ vào phong cách mang tính khai báo và dễ đọc hiểu hơn rất nhiều, đập chết ăn thịt những đoạn mã gán thủ công mang nặng tính mệnh lệnh rườm rà của thời kỳ lập trình tiền sử.
Truy cập thuộc tính có điều kiện và cơ chế chuỗi tùy chọn
Trong những bản cập nhật gần đây của ngôn ngữ, cụ thể là vào thời kỳ Tiêu chuẩn ngôn ngữ kịch bản châu Âu 2020, một tính năng mang hàm lượng kỹ thuật cao mang tên cơ chế chuỗi tùy chọn đã được cấy ghép thành công vào hệ sinh thái, mang theo một sức mạnh nâng cấp đáng kể cho các khả năng truy cập thuộc tính, đặc biệt là nhằm giải quyết triệt để nỗi đau đớn mang tên truy cập thuộc tính lồng nhau đa tầng. Hình thái sơ khai của cơ chế an toàn này là toán tử ghép hai ký tự bao gồm dấu chấm hỏi và dấu chấm. Toán tử này sẽ đảm nhận nhiệm vụ đi kiểm tra sợi dây tham chiếu nằm ở vế bên trái để soi xét dưới góc độ bộ nhớ xem liệu nó có mang trạng thái rỗng tuếch, tức là bằng giá trị vô hiệu hoặc không xác định hay không. Nếu kết quả kiểm tra trả về khẳng định trạng thái rỗng tuếch, toàn bộ phần còn lại của biểu thức truy cập thuộc tính sẽ ngay lập tức bị đoản mạch và bị hệ thống bỏ qua không thương tiếc nhằm ngăn chặn lỗi sụp đổ chương trình, và giá trị không xác định sẽ ngay lập tức được trả về làm kết quả đánh giá cuối cùng.
Ở một diễn biến logic khác, nếu phép thử trạng thái rỗng tuếch đó thất bại, toán tử chuỗi tùy chọn sẽ ung dung tiến hành truy cập vào thuộc tính mục tiêu giống hệt như cách mà một toán tử dấu chấm bình thường vẫn hay làm trong điều kiện lý tưởng. Một sự thật cần phải khắc cốt ghi tâm là toán tử này hoàn toàn không thèm xác minh khắt khe xem liệu biến tham chiếu có thực sự đang ôm giữ một đối tượng hàng thật giá thật hay không, mà nó chỉ đơn thuần kiểm tra xem nó có vượt qua được bài kiểm tra phi rỗng tuếch hay không mà thôi. Tuy nhiên, một chân lý kỹ thuật là toàn bộ mọi giá trị phi rỗng tuếch đều có thể bị truy cập một cách an toàn mà không hề làm cỗ máy thực thi nổi điên ném ra ngoại lệ thông qua toán tử dấu chấm, bất chấp việc có tồn tại một thuộc tính tương ứng để mà trích xuất dữ liệu hay không.
Thêm vào đó, dưới góc độ phản biện thiết kế phần mềm, toán tử chuỗi tùy chọn này tuyệt đối không được phép bị lạm dụng một cách vô tội vạ để đi thế vai cho mọi toán tử dấu chấm xuất hiện trong các đoạn mã nghiệp vụ. Sứ mệnh tối cao của một kiến trúc sư phần mềm là phải nỗ lực hết mình để kiểm soát luồng dữ liệu và biết trước xem liệu một thao tác truy cập thuộc tính bằng dấu chấm có khả năng thành công hay thất bại trước khi thực sự bóp cò thực thi truy cập đó. Lời khuyên học thuật là chỉ nên lôi toán tử chuỗi tùy chọn ra xài khi mà bản chất của những giá trị đang bị truy cập đó phải phụ thuộc hoàn toàn vào những điều kiện biến thiên từ môi trường bên ngoài mà hệ thống nội tại hoàn toàn vô phương dự đoán hay kiểm soát được, nhằm tránh tình trạng dùng cơ chế an toàn để che đậy đi những lỗ hổng logic nghiêm trọng trong thiết kế cấu trúc dữ liệu.
Hành vi truy cập thuộc tính trên các kiểu dữ liệu nguyên thủy
Một trong những khái niệm dễ gây hoang mang và lú lẫn nhất đối với những người nghiên cứu sâu về ngôn ngữ JavaScript là việc họ hoàn toàn có đặc quyền thọc tay vào và lôi các thuộc tính hoặc phương thức ra khỏi những giá trị nguyên thủy mà bản thân chúng hoàn toàn không mang hình hài cấu trúc của những đối tượng. Hiện tượng này thoạt nhìn thì có vẻ như đang đi ngược lại hoàn toàn với trực giác logic toán học thông thường, nhưng nó lại là một cơ chế thiết kế ngôn ngữ cực kỳ tinh vi. Làm thế nào mà một lập trình viên lại có thể giáng đòn gọi phương thức chuyển đổi chuỗi nhắm thẳng vào một con số nguyên thủy vô tri vô giác?
Đây thực sự là một hố sâu tăm tối về mặt lý thuyết trình biên dịch và phức tạp hơn rất nhiều so với những kiến thức bề mặt; tuy nhiên, để giải phẫu nhanh cơ chế này: nếu như chương trình cả gan thi triển một pha truy cập thuộc tính nhắm thẳng vào một giá trị phi đối tượng và đồng thời cũng phi rỗng tuếch, thì cỗ máy thực thi theo mặc định sẽ can thiệp trong một cái chớp mắt để ép buộc chuyển đổi giá trị nguyên thủy đó vào trong một lớp vỏ bọc đại diện mang hình hài của một đối tượng vật lý thực thụ, qua đó bật đèn xanh hợp lệ cho thao tác truy cập thuộc tính nhắm thẳng vào đối tượng vừa mới được ngầm định nặn ra đó. Quy trình ma quỷ diễn ra trong tích tắc này thường xuyên được giới hàn lâm khoa học máy tính xưng tụng bằng khái niệm đóng hộp giá trị, tương đồng với hành động tống một giá trị trần trụi vào bên trong một vật chứa đối tượng có đầy đủ tiện nghi phương thức.
Hãy khắc cốt ghi tâm rằng các giá trị rỗng tuếch vô hình cũng hoàn toàn có khả năng bị biến thành đối tượng thông qua lời gọi hàm tạo ép kiểu một cách có chủ đích. Thế nhưng, ngôn ngữ JavaScript lại thiết lập một rào cản an toàn bằng cách tuyệt đối không bao giờ thèm tự động đóng hộp những giá trị rỗng tuếch này một cách ngầm định, thế nên mọi mưu đồ truy cập thuộc tính trực tiếp nhắm vào chúng mà không qua kiểm tra đều sẽ chuốc lấy thất bại thảm hại và dẫn đến sự sụp đổ của toàn bộ tiến trình thực thi. Trò đóng hộp tinh vi này luôn luôn đi kèm với một đối trọng giải phóng tài nguyên của nó mang tên mở hộp; hệ thống sẽ thẳng tay lột sạch lớp vỏ bọc đối tượng tạm thời đó ra để lôi giá trị nguyên thủy đang nằm rên rỉ bên dưới ra ngoài bất cứ khi nào mà một phép toán toán học vô tình tông phải một đối tượng mang hình hài lớp bọc như vậy nhằm đảm bảo tính toàn vẹn của các phép tính.
Quản lý vòng đời và khám phá nội tạng của vật chứa
Vòng đời của một thuộc tính bên trong đối tượng trải dài từ lúc được khởi tạo, cập nhật giá trị cho đến khi bị tiêu hủy để thu hồi tài nguyên bộ nhớ cho hệ thống.
Cơ chế gán, cập nhật và xóa bỏ thuộc tính khỏi đối tượng
Bất luận là một thuộc tính định danh được định nghĩa ngay tại khoảnh khắc mà cú pháp khai báo đối tượng trực tiếp vừa được nặn ra trong bộ nhớ, hay là được đắp thêm vào ở một pha xử lý sự kiện nào đó sau này, thì thao tác gán một giá trị bộ nhớ cho thuộc tính luôn luôn được giao phó một cách truyền thống cho toán tử bằng, y hệt như cách mà bất kỳ một thao tác gán biến cục bộ bình thường nào khác vẫn thường hay làm. Nếu như thuộc tính mục tiêu đó chưa hề có mặt trên bản đồ băm của đối tượng, thì câu lệnh gán đó sẽ ngay lập tức nặn ra một nút thuộc tính mới toanh mang tên đó và thẳng tay gán giá trị tham chiếu của nó vào hệ thống lưu trữ. Nhưng nếu lỡ như định danh đó đã chễm chệ ngự trị ở đó từ một phiên giao dịch trước rồi, thì câu lệnh đó sẽ không nể nang gì mà giáng đòn tái gán lại con trỏ giá trị của nó. Dưới góc độ cảnh báo an toàn, một thao tác gán hoàn toàn có rủi ro chuốc lấy thất bại tùy thuộc vào các cờ mô tả thuộc tính, hoặc là nó sẽ không ngây ngô đâm đầu vào gán trực tiếp giá trị đó vào bộ nhớ mà thay vào đó nó sẽ kích nổ một hàm thiết lập đánh chặn dùng để thi hành các phép toán kiểm tra hoặc thao túng logic dữ liệu ẩn.
Các kiến trúc sư phần mềm cũng hoàn toàn được ban cho quyền năng thao tác hàng loạt để có thể gán một hoặc hàng tá những thuộc tính chỉ trong một đòn thực thi duy nhất thông qua việc triệu hồi phương thức hợp nhất đối tượng tĩnh. Phương thức này sẽ tóm lấy đối tượng đối số đầu tiên làm vùng đệm mục tiêu, và các đối tượng theo sau để làm các nguồn trích xuất dữ liệu, thực thi quá trình sao chép bề mặt với cùng một thuật toán tương tự như cơ chế trải rộng đối tượng. Một khi một thuộc tính đã bị đóng đinh định nghĩa trên một đối tượng, thì con đường độc đạo duy nhất được ngôn ngữ cấp phép để có thể nhổ rễ và tống cổ nó đi hoàn toàn khỏi bảng băm là phải dùng đến toán tử xóa sổ.
Đi ngược lại hoàn toàn với những ảo tưởng sai lầm kinh điển của dân tình và nhiều tài liệu thiếu độ xác thực, toán tử xóa sổ của ngôn ngữ JavaScript tuyệt đối không hề trực tiếp nhúng tay vào bất kỳ một thao tác giải phóng hoặc thu hồi không gian bộ nhớ vật lý nào thông qua cơ chế thu gom rác thải bộ nhớ nền. Sứ mệnh duy nhất và mang tính độc tôn của nó chỉ là cắt đứt và bứt một liên kết thuộc tính ra khỏi một cấu trúc đối tượng. Nếu lỡ như giá trị đang nằm chễm chệ bên trong thuộc tính đó lại là một sợi dây tham chiếu trỏ đến một vùng nhớ đối tượng đồ sộ khác, và hệ thống phân tích đồ thị xác nhận hoàn toàn không còn bất kỳ một sợi dây tham chiếu sống sót nào khác trỏ đến vùng nhớ đó một khi thuộc tính đã bị bứt bỏ, thì vùng nhớ giá trị đó rất có khả năng sẽ bị hệ thống quản lý tài nguyên liệt vào danh sách chờ tử hình và thu hồi trong một đợt càn quét tương lai của cơ chế thu gom rác thải bộ nhớ. Việc giáng đòn gọi toán tử xóa sổ nhắm vào bất cứ một thực thể không gian nào khác ngoài một thuộc tính đối tượng hợp lệ là một hành vi lạm dụng vô cùng trơ trẽn về mặt ngữ pháp, và nó sẽ phải trả giá bằng sự thất bại âm thầm hoặc bị cỗ máy thực thi gào thét ném thẳng vào mặt một ngoại lệ vi phạm chế độ nghiêm ngặt.
Các phương pháp kiểm tra sự tồn tại của thuộc tính
Trong quá trình bảo trì và phân tích dữ liệu, các chuyên gia phát triển được trang bị cả một kho vũ khí đa dạng về mặt toán tử và phương thức để có thể moi móc xem rốt cuộc bên trong một đối tượng đang chứa chấp những định danh và dữ liệu quái gì. Để chất vấn logic xem một đối tượng có thực sự sở hữu một thuộc tính cụ thể nào đó trong bảng băm của nó hay không, ngôn ngữ cung cấp các công cụ kiểm tra sự tồn tại chuyên biệt. Thực sự có một hố sâu ngăn cách sự khác biệt mang tính chất sống còn về mặt thuật toán tìm kiếm giữa cách mà toán tử in vận hành so với phương thức kiểm tra sở hữu thuộc tính truyền thống.
Toán tử in thực hiện một chiến lược tìm kiếm đệ quy sâu: nó không những xoi mói và đối chiếu định danh trên đối tượng mục tiêu được chỉ định trực tiếp, mà nếu như nó vô dụng không tìm thấy thứ cần tìm ở tầng bề mặt đó, nó sẽ không ngần ngại kích hoạt hành vi duyệt vác mặt đi tham khảo ý kiến của toàn bộ chuỗi nguyên mẫu kế thừa của đối tượng đó chạy dài đến tận gốc rễ của ngôn ngữ. Trái ngược hoàn toàn về mặt triết lý, phương thức kiểm tra sở hữu lại là một kẻ bảo thủ cục bộ khi chỉ thèm tham khảo và đối chiếu dữ liệu trên đúng mỗi bản thân đối tượng mục tiêu duy nhất mà thôi, tuyệt đối cự tuyệt việc đi vay mượn thông tin từ các lớp cha. Tuy nhiên, việc thọc tay vào truy cập một phương thức được kế thừa ngầm định từ nguyên mẫu đối tượng gốc luôn luôn tiềm ẩn những rủi ro bảo mật và lỗi logic chết người do nguy cơ bị ghi đè thuộc tính.
Nhằm khắc phục triệt để những lỗ hổng thiết kế lịch sử này, Tiêu chuẩn ngôn ngữ kịch bản châu Âu 2022 rốt cuộc cũng đã chốt hạ và đưa vào tiêu chuẩn chính thức một tính năng kiểm tra mới toanh, mang tên phương thức kiểm tra sở hữu tĩnh. Về cơ bản thuật toán lõi thì nó cũng đi thi hành những sứ mệnh đối chiếu y hệt như phương pháp cũ, thế nhưng nó lại được hệ thống triệu hồi dưới thân phận là một công cụ trợ giúp tĩnh nằm vắt vẻo bên ngoài giá trị đối tượng thay vì phải chạy vòng vèo phụ thuộc thông qua chuỗi nguyên mẫu có nguy cơ bị tiêm nhiễm của đối tượng, điều này đã giúp nó trở thành một thứ vũ khí phòng thủ an toàn tuyệt đối và mang tính nhất quán cao hơn rất nhiều khi được lôi ra sử dụng trong các hệ thống phần mềm đòi hỏi độ tin cậy tối đa. Việc cộng đồng lập trình viên tích cực ứng dụng các bản vá tương thích ngược cho phương thức mới này chứng tỏ khát khao nâng cấp tiêu chuẩn an toàn dữ liệu bất chấp sự cản trở của các môi trường thực thi cũ kỹ thiếu tính năng.
Kỹ thuật liệt kê toàn bộ nội tạng và vai trò vận chuyển tạm thời
Để có cái nhìn toàn cảnh về cấu trúc dữ liệu, giao diện lập trình ứng dụng trích xuất mục đối tượng có nhiệm vụ đi móc nối và lôi ra một bản danh sách đa chiều, chứa đựng các thuộc tính thuộc quyền sở hữu riêng tư và thỏa mãn điều kiện cờ trạng thái có thể liệt kê được từ một đối tượng gốc. Vẫn còn cả một kho vũ khí đa dạng những cơ chế phản chiếu khác sẵn sàng phục vụ nhu cầu thao tác dữ liệu tinh vi của các kỹ sư. Phương thức trích xuất khóa sẽ nôn ra một bản danh sách mảng chứa các tên thuộc tính có thể liệt kê được đang lẩn khuất trong một đối tượng – chỉ thuần túy là tên định danh không gian, tuyệt đối không có sự hiện diện của giá trị bộ nhớ đi kèm; trong khi đó phương thức trích xuất giá trị lại chọn một con đường khai thác khác khi nôn ra một bản danh sách chứa toàn bộ những khối giá trị vật lý đang được ôm giữ bên trong các thuộc tính đó.
Thế nhưng nếu như tham vọng phân tích thực sự là muốn lôi cổ toàn bộ các khóa định danh đang nằm trong một đối tượng ra ánh sáng, bất chấp việc cờ trạng thái của chúng có cho phép liệt kê hay không, thì phương thức lấy tên thuộc tính sở hữu là công cụ hoàn hảo. Mặc dù vậy, bản danh sách kết quả này vĩnh viễn sẽ không bao giờ chứa chấp bất kỳ một tên thuộc tính mang kiểu dữ liệu Biểu tượng nào, bởi vì bọn chúng đã được cỗ máy thực thi ưu ái đối xử như những tọa độ bộ nhớ đặc chủng nằm ngoài không gian chuỗi thông thường trên đối tượng. Nếu kết hợp cả hai phương pháp trích xuất này, các nhà phân tích sẽ thâu tóm được toàn bộ mớ nội tạng trực tiếp thuộc quyền sở hữu riêng của một đối tượng. Cần phải nhấn mạnh lại một chân lý kiến trúc rằng một đối tượng cũng hoàn toàn có khả năng kế thừa nội tạng từ chuỗi nguyên mẫu của nó, và đống dữ liệu kế thừa này tuyệt đối không được hệ thống công nhận là nội tạng thuộc quyền sở hữu riêng, vì vậy chúng sẽ vĩnh viễn không bao giờ vác mặt ra trong bất kỳ bản danh sách phản chiếu trực tiếp nào kể trên.
Dưới lăng kính của các khuôn mẫu thiết kế quy mô lớn, việc lấy một đối tượng ra để làm vật chứa cho hàng tá những giá trị đôi khi chỉ thuần túy là một chiêu trò vận chuyển dữ liệu mang tính chất tạm bợ. Ví dụ điển hình trong kỹ thuật truyền thông điệp là khi nhà phát triển khát khao muốn ném hàng tá những giá trị cấu hình vào cho một khối hàm xử lý thông qua một đối số duy nhất để giữ cho chữ ký hàm được thanh lịch, hoặc khi kiến trúc luồng dữ liệu bắt buộc một hàm phải nôn ra hàng tá những kết quả tính toán độc lập cùng một lúc. Việc kết hợp khuôn mẫu đối tượng vận chuyển tạm thời này với sức mạnh hủy diệt của kỹ thuật phân rã đối tượng ngay tại ranh giới của các giao diện hàm giúp tối ưu hóa luồng dữ liệu, chứng minh rằng một đối tượng nhiều khi chỉ bị coi như một túi vận chuyển phi trạng thái thay vì là một thực thể dữ liệu mang ý nghĩa chu kỳ sống dài hạn tự thân nó.
Kết luận
Kịch bản ứng dụng kinh điển và mang tính nền tảng nhất của các hệ thống đối tượng chính là lấy chúng ra làm cấu trúc vật chứa đa năng cho hàng tá những khối giá trị dữ liệu phức tạp. Các kiến trúc sư phần mềm hoàn toàn có thể nhào nặn và quản lý vòng đời của những đối tượng đóng vai trò là vật chứa thuộc tính này thông qua hàng loạt các thao tác tinh vi: từ việc định nghĩa cấp phát các tọa độ thuộc tính đã được dán tên trong không gian bộ nhớ, gán liên kết tham chiếu cho chúng những giá trị dữ liệu từ môi trường, thọc tay vào truy cập trích xuất các giá trị đó phục vụ tính toán logic sau này, cho đến việc thẳng tay xóa sổ dọn dẹp các thuộc tính dư thừa thông qua toán tử chuyên biệt, và cuối cùng là sử dụng các công cụ phản chiếu để moi móc điều tra xem rốt cuộc bên trong kiến trúc vật chứa đang lẩn khuất những điểm mù dữ liệu nào.
Thế nhưng, thế giới của các đối tượng trong ngôn ngữ JavaScript còn ẩn chứa một sức mạnh vô biên vượt xa khỏi khuôn khổ chật hẹp chỉ là những bộ sưu tập từ điển tĩnh chứa toàn những cặp khóa và giá trị bề mặt. Ở những chương phân tích chuyên sâu tiếp theo, chúng ta sẽ thực hiện một cú nhảy học thuật lặn sâu xuống tận cùng bên dưới lớp vỏ bọc cú pháp hào nhoáng để soi xét một cách tường minh xem rốt cuộc bọn chúng đang cày cuốc và giao tiếp với hệ thống phần cứng như thế nào ở tầng nguyên mẫu. Chỉ khi nắm vững bản chất cốt lõi của vật chứa, các lập trình viên mới đủ năng lực để thiết lập nền móng cho những mô hình thiết kế lớp phức tạp và kiến tạo nên những hệ thống phần mềm đồ sộ mang đẳng cấp quốc tế.
Đọ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.

- thu-vien (1039)
- viet-lach (284)
- javascript (21)
- lap-trinh (21)
- lap-trinh-web (21)
- web-development (21)
- ydkjs (21)
- get-started (21)
- you-dont-know-js-yet (21)
- chua-biet-javascript (21)
- chua-biet-ro-javascript (21)
- kyle-simpson (21)
- coercion (21)
- type-awareness (21)
- triet-ly-lap-trinh (21)
- giai-ma-javascript (21)
- giai-ma-ban-chat-coi-loi-javascript (21)