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.4

Giải mã bản chất cốt lõi của JavaScript (Phần 3, chương 4) 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.

70 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 4) 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 quá trình tìm hiểu về ngôn ngữ lập trình JavaScript, chúng ta đã không ít lần chứng kiến sự hiện diện của từ khóa this, thế nhưng để thực sự đào sâu và thấu hiểu một cách tường tận về cơ chế vận hành chính xác của nó bên trong cỗ máy thực thi thì vẫn còn là một khoảng trống kiến thức lớn. Để có thể xây dựng một nền tảng nhận thức chuẩn xác về từ khóa this trong ngôn ngữ JavaScript, điều kiện tiên quyết và mang tính chất sống còn là bạn bắt buộc phải gạt bỏ hoàn toàn mọi định kiến hoặc những giả định có sẵn trong tâm trí, đặc biệt là những thói quen tư duy được hình thành từ việc quan sát cách từ khóa this hoạt động trong các ngôn ngữ lập trình hướng đối tượng truyền thống khác mà bạn có thể đã từng trải nghiệm qua. Chân lý quan trọng bậc nhất mà bạn cần phải khắc cốt ghi tâm về từ khóa this là: quyết định về việc từ khóa this sẽ trỏ đến giá trị nào – thông thường là một đối tượng vật lý trong bộ nhớ – tuyệt đối không bao giờ được thiết lập tại thời điểm người lập trình viết mã, mà thay vào đó, nó được cỗ máy ngôn ngữ tính toán và quyết định một cách hoàn toàn động ngay tại thời điểm chương trình đang chạy. Điều này mang một ý nghĩa kiến trúc sâu sắc rằng, bạn không thể nào chỉ đơn thuần nhìn chằm chằm vào một hàm có chứa từ khóa this – ngay cả khi đó là một phương thức được đóng gói cẩn thận bên trong một định nghĩa lớp – và tự tin khẳng định chắc nịch rằng từ khóa this sẽ ôm giữ giá trị gì trong suốt quá trình cái hàm đó được đem ra thực thi. Thay vì áp dụng lối tư duy tĩnh đó, hệ thống ép buộc bạn phải truy lùng từng ngóc ngách nơi mà hàm đó được triệu hồi, và soi xét cực kỳ tỉ mỉ xem hàm đó đã được triệu hồi bằng cách thức nào; thậm chí cái vị trí vật lý nơi hàm được gọi cũng hoàn toàn vô giá trị trong việc phân giải ngữ cảnh. Trên thực tế, một hàm duy nhất có nhận thức về từ khóa this hoàn toàn có khả năng bị triệu hồi theo ít nhất bốn phương thức hoàn toàn biệt lập, và bất kỳ phương thức tiếp cận nào trong số đó cũng sẽ dẫn đến một kết cục là cỗ máy sẽ gán một giá trị từ khóa this hoàn toàn khác biệt cho đích danh cái lần triệu hồi cụ thể đó. Do vậy, cái câu hỏi cửa miệng mà chúng ta vẫn thường hay lẩm bẩm khi đọc mã nguồn – từ khóa this trong hàm này đang trỏ đi đâu? – thực chất lại là một câu hỏi sai lầm về mặt bản chất; câu hỏi chuẩn xác mang tính học thuật mà bạn bắt buộc phải đặt ra là: khi hàm này được triệu hồi theo một cách thức nhất định, thì giá trị từ khóa this nào sẽ được hệ thống phân bổ cho riêng cái lần triệu hồi đó? Nếu như bộ não của bạn đang bắt đầu cảm thấy xoắn xuýt và choáng váng chỉ bởi việc đọc qua những dòng giới thiệu chương này… thì đó là một tín hiệu cực kỳ đáng mừng, hãy chuẩn bị tinh thần để tự thiết lập lại toàn bộ mạng lưới tư duy của bạn về cách thức nhìn nhận từ khóa this trong hệ sinh thái ngôn ngữ JavaScript.

Nhận thức về từ khóa này

Bất kỳ một khối hàm điện toán nào có chứa đựng sự hiện diện của từ khóa this bên trong nội tạng của nó đều được hệ thống phân loại là một hàm có nhận thức về từ khóa this. Nếu một hàm hoàn toàn trong sạch và không chứa bất kỳ một từ khóa this nào, thì bộ quy tắc phức tạp cai quản hành vi của từ khóa này sẽ hoàn toàn không có bất kỳ một tác động nào lên vòng đời của nó; thế nhưng, chỉ cần một từ khóa this duy nhất xuất hiện, bạn sẽ vĩnh viễn đánh mất khả năng dự đoán hành vi của hàm nếu không xác định được ngữ cảnh cho từng lần triệu hồi.

Bản chất của tham số ngầm định

Khi chúng ta thảo luận về một hàm có nhận thức về từ khóa this, chúng ta đang chạm đến một khía cạnh kiến trúc cốt lõi nơi mà hàm đó không còn là một thực thể đóng gói tĩnh đơn thuần, mà nó đã biến thành một cấu trúc phụ thuộc vào bối cảnh môi trường xung quanh để có thể hoàn tất quá trình thực thi logic của mình. Bạn hoàn toàn có thể mường tượng từ khóa this giống hệt như một biến số giữ chỗ nằm lọt thỏm bên trong một đoạn mã khuôn mẫu; cái giá trị thực sự sẽ được dùng để thay thế cho cái biến số giữ chỗ đó tuyệt đối không được hệ thống chốt hạ vào cái khoảnh khắc mà chúng ta ngồi gõ những dòng mã nguồn, mà quá trình định đoạt đó chỉ được diễn ra một cách đầy biến động trong khi toàn bộ guồng máy của chương trình đang thực sự vận hành trên bộ nhớ. Có lẽ bạn đang hoài nghi và cho rằng tôi chỉ đang cố tình chơi chữ để làm phức tạp hóa vấn đề, bởi vì theo một lẽ tự nhiên, khi bạn là người chắp bút viết ra toàn bộ chương trình, bạn cũng chính là người tự tay gõ ra toàn bộ các câu lệnh dùng để gọi đến từng hàm một, do đó bạn mặc nhiên cho rằng bản thân đã kiểm soát và định đoạt xong xuôi xem từ khóa this sẽ là cái quái gì ngay từ lúc thiết kế mã nguồn, phải không? Đừng vội vã đưa ra kết luận như vậy, bởi vì thực tế kỹ thuật phũ phàng hơn thế rất nhiều, bạn không phải lúc nào cũng là người nắm giữ quyền sinh sát đối với các câu lệnh trực tiếp triệu hồi hàm của mình. Những hàm có nhận thức về từ khóa this do bạn dày công chế tác hoàn toàn có nguy cơ bị biến thành các hàm gọi lại và bị ném sang cho những phân vùng mã nguồn ngoại lai khác quản lý, đó có thể là một mô đun khác trong cùng hệ thống của bạn, hoặc là một bộ khung thư viện của bên thứ ba, hay thậm chí là nằm sâu bên trong một cơ chế bản địa được tích hợp sẵn của chính ngôn ngữ hoặc của môi trường máy chủ đang dung chứa chương trình của bạn.

Ngay cả khi chúng ta tạm gác lại câu chuyện phức tạp về việc luân chuyển các hàm điện toán dưới thân phận là các hàm gọi lại, thì bản thân ngôn ngữ JavaScript cũng đã tự mình trang bị hàng loạt các cơ chế tinh vi cho phép những hành vi điều kiện tại thời điểm chạy có quyền can thiệp và định đoạt xem cái giá trị nào – nhắc lại một lần nữa, thông thường là một đối tượng vật lý – sẽ được cỗ máy lựa chọn để gán vào cho từ khóa this của một lần triệu hồi hàm cụ thể nào đó. Vì vậy, bất chấp một sự thật rành rành là bạn có thể đã tự tay gõ ra từng dòng mã một, thì trong kịch bản khả quan nhất, bộ não của bạn cũng sẽ phải liên tục nai lưng ra để mô phỏng và thực thi nhẩm hàng loạt các điều kiện và các nhánh logic chằng chịt, những thứ mà rốt cuộc sẽ tác động trực tiếp đến bối cảnh triệu hồi của hàm. Tại sao toàn bộ cái mớ lý thuyết phức tạp này lại mang tầm quan trọng sống còn đến như vậy đối với một kỹ sư phần mềm? Bởi vì cái gánh nặng phải giải mã và thấu hiểu những cơ chế ma quỷ này không chỉ giáng xuống đầu một mình bạn – người tác giả khởi thủy của đoạn mã – mà nó còn trở thành một cực hình đối với từng cá nhân độc lập sẽ phải căng mắt ra đọc đoạn mã của bạn, từ nay cho đến mãi mãi về sau. Nếu như bất kỳ một ai đó, thậm chí là chính bản thân bạn của vài năm trong tương lai, khát khao muốn đọc và hiểu một phân vùng mã nguồn có chứa định nghĩa của một hàm nhận thức từ khóa this, thì một hệ lụy không thể trốn tránh là người đó bắt buộc phải cày xới toàn bộ hệ thống để tìm kiếm, đọc hiểu, và phân tích từng lần triệu hồi một của cái hàm đó thì mới có hy vọng dự đoán được chính xác hành vi của nó.

Để công bằng mà nói, thì sự phức tạp trong việc đọc hiểu này vốn dĩ cũng đã đúng một phần khi chúng ta soi xét đến các tham số thông thường của một hàm điện toán; muốn thấu tỏ xem một hàm sẽ hoạt động ra sao, chúng ta đương nhiên phải biết được cái gì đang được truyền vào bụng nó thông qua các đối số. Vì vậy, bất kỳ một hàm nào sở hữu ít nhất một tham số thì xét theo một khía cạnh tương đồng, nó cũng là một hàm có nhận thức về đối số – mang ý nghĩa là những đối số nào được truyền vào sẽ được gán cho các tham số của hàm. Thế nhưng, đối với các tham số thông thường, chúng ta thường xuyên nhận được những manh mối rõ ràng hơn rất nhiều từ chính bản thân cái hàm đó về việc các tham số sẽ gánh vác sứ mệnh gì và chứa đựng loại dữ liệu nào. Chúng ta dễ dàng nhìn thấy tên của các tham số được khai báo chễm chệ ngay tại phần đầu của hàm, điều này đóng góp một phần công lao khổng lồ trong việc giải thích bản chất cũng như mục đích tồn tại của chúng trong logic nghiệp vụ. Hơn thế nữa, nếu như có sự xuất hiện của các giá trị mặc định dành cho các tham số, chúng ta cũng thường xuyên bắt gặp chúng được khai báo một cách trực tiếp ngay trên cùng một dòng thông qua các mệnh đề gán bằng giá trị cụ thể. Phụ thuộc vào phong cách lập trình của tác giả, chúng ta thậm chí có thể nhìn thấy ngay trong vài dòng mã đầu tiên của hàm một hệ thống logic chuyên biệt dùng để xử lý các tham số này; đó có thể là các bộ lọc kiểm chứng giá trị, các rào cản từ chối giá trị bất hợp lệ, hay thậm chí là các bước thao túng dữ liệu như ép kiểu hoặc định dạng lại cấu trúc. Trái ngược hoàn toàn với sự minh bạch đó, từ khóa this thực chất lại mang hình hài của một tham số đối với hàm, thế nhưng nó là một con quái vật tham số ngầm định thay vì là một tham số tường minh có thể dễ dàng kiểm soát.

Bối cảnh động thay vì phạm vi tĩnh

Sự thiếu vắng hoàn toàn các tín hiệu cảnh báo tại phần đầu khai báo của một hàm về việc liệu từ khóa this có bị lôi ra sử dụng hay không là một cái bẫy chết người đối với người đọc mã nguồn. Bạn bị ép buộc phải cày xới và đọc bằng hết toàn bộ nội tạng của khối hàm đó thì mới có thể kết luận được liệu từ khóa this có đang lẩn khuất ở một xó xỉnh nào đó hay không. Cái tên gọi của tham số này thì luôn luôn bị chết cứng là this, vì thế chúng ta gần như không thể bòn rút được bất kỳ một manh mối có giá trị nào về bản chất hay mục đích thực sự của nó thông qua một cái tên gọi mang tính chất quá đỗi chung chung và vô hồn như vậy. Xét về mặt lịch sử phát triển của ngôn ngữ, đã và đang tồn tại một sự lú lẫn và hoang mang tột độ trong giới lập trình viên về việc rốt cuộc thì cái khái niệm từ khóa this này sinh ra để ám chỉ cái quái gì. Tệ hại hơn nữa, chúng ta cực kỳ hiếm khi, nếu không muốn nói là gần như không bao giờ, bắt gặp bất kỳ một đoạn mã phòng ngự nào được viết ra nhằm mục đích kiểm chứng, xác thực hay chuyển đổi kiểu dữ liệu cho cái giá trị từ khóa this sẽ được áp dụng vào trong một lần triệu hồi hàm. Trên thực tế, gần như toàn bộ mọi đoạn mã có nhận thức về từ khóa this mà tôi từng có cơ hội chiêm ngưỡng đều lựa chọn một cách tiếp cận ngây thơ là nhắm mắt mặc định rằng cái tham số ngầm định này chắc chắn đang ôm giữ chính xác cái giá trị mà tác giả đang kỳ vọng trong đầu. Đây đích thị là một cái bẫy hoàn hảo để sản sinh ra vô vàn những lỗi logic dị thường không thể lường trước được trong hệ thống phần mềm.

Nếu như chúng ta đã thống nhất quan điểm rằng từ khóa this mang thân phận là một tham số ngầm định, vậy thì mục đích tối thượng của nó là gì và cái gì thực sự đang được bơm vào bên trong hàm thông qua con đường này? Tôi chân thành hy vọng rằng bạn đã có cơ hội nghiền ngẫm qua cuốn sách Phạm vi và cơ chế bao đóng nằm trong cùng loạt tài liệu chuyên sâu này. Nếu bạn chưa từng đọc qua nó, tôi thiết tha khuyến nghị bạn hãy quay trở lại và ngấu nghiến cuốn sách đó ngay sau khi bạn chinh phục xong chương trình hiện tại. Trong cuốn sách mang tính nền tảng đó, tôi đã cất công giải phẫu một cách cực kỳ chi tiết về cách thức mà các phạm vi – bao gồm cả cơ chế bao đóng ma thuật – vận hành, và đó là một trong những đặc tính kiến trúc mang tầm quan trọng sinh tử nhất của các hàm điện toán. Phạm vi từ vựng, bao hàm cả toàn bộ những biến số đã bị hệ thống nhốt lại bên trong cơ chế bao đóng, đóng vai trò như một môi trường ngữ cảnh mang tính chất tĩnh tuyệt đối để các sợi dây tham chiếu định danh từ vựng bên trong hàm có thể bám vào đó mà phân giải giá trị. Hệ thống này mang tính chất tĩnh và bị khóa chặt bởi vì ngay tại cái khoảnh khắc tác giả chắp bút thiết kế phần mềm, khi họ quyết định đặt các hàm và các câu lệnh khai báo biến vào bên trong hàng tá những phạm vi lồng nhau, thì những quyết định kiến trúc đó đã bị đổ bê tông cố định, và vĩnh viễn không bao giờ bị suy chuyển hay tác động bởi bất kỳ một điều kiện thay đổi nào tại thời điểm chương trình đang chạy. Bức tranh này trái ngược hoàn toàn với hệ thống của ngôn ngữ JavaScript.

Để tạo ra một phép so sánh tương phản làm nổi bật vấn đề, một ngôn ngữ lập trình khác có thể sẽ cung cấp cho người dùng một cơ chế mang tên phạm vi động, nơi mà ngữ cảnh dùng để phân giải các tham chiếu biến số của một hàm không bị trói buộc bởi những quyết định tĩnh tại thời điểm viết mã, mà nó bị thao túng hoàn toàn bởi các điều kiện biến động tại thời điểm chạy. Một hệ thống kiến trúc mang tính chất động như vậy chắc chắn sẽ mang lại một mức độ linh hoạt vượt trội hơn rất nhiều so với ngữ cảnh tĩnh truyền thống – mặc dù chúng ta cũng phải thừa nhận một chân lý rằng sự linh hoạt tột độ thì luôn luôn đi kèm với một cái giá phải trả là sự phức tạp đến mức cực hình trong việc kiểm soát logic. Cần phải khẳng định lại một cách rành mạch để tránh mọi sự hiểu lầm: cơ chế phạm vi bên trong ngôn ngữ JavaScript luôn luôn và duy nhất chỉ hoạt động theo nguyên lý từ vựng và mang tính chất tĩnh tuyệt đối – nếu như chúng ta đồng lòng nhắm mắt làm ngơ trước những trò gian lận phá vỡ chế độ nghiêm ngặt như việc xài hàm đánh giá mã nguồn động hay câu lệnh gộp phạm vi. Tuy nhiên, một trong những thứ vũ khí thực sự tạo nên sức mạnh hủy diệt và sự kỳ diệu của ngôn ngữ JavaScript chính là việc nó hào phóng cung cấp một cơ chế song song khác, mang trong mình mức độ linh hoạt và những năng lực tính toán gần như tương đồng với cơ chế phạm vi động… Cơ chế từ khóa this này, xét về mặt hiệu quả thực tế, đích thị là một cơ chế tạo ra ngữ cảnh động – chứ tuyệt đối không phải là phạm vi động; đây chính là con đường ma thuật cho phép một hàm có nhận thức về từ khóa this có thể bị đem ra triệu hồi một cách linh hoạt dựa trên hàng tá những ngữ cảnh hoàn toàn khác biệt nhau – một sức mạnh kiến trúc bất khả thi nếu chỉ dựa dẫm vào cơ chế bao đóng và các định danh thuộc phạm vi từ vựng tĩnh.

Trách nhiệm của người thiết kế kiến trúc

Bạn có thể sẽ nhíu mày tự hỏi tại sao một cơ chế kiến trúc mang tầm vóc quan trọng như một ngữ cảnh động lại bị hệ thống đùn đẩy và xử lý dưới thân phận của một đầu vào ngầm định lén lút tuồn vào hàm, thay vì được vinh danh và truyền vào như một đối số tường minh đàng hoàng. Đó thực sự là một câu hỏi học thuật vô cùng sắc sảo và mang tính trọng tâm, thế nhưng đáng tiếc thay, nó lại không phải là một câu hỏi mà chúng ta có đủ nền tảng kiến thức để đưa ra một câu trả lời thỏa đáng ngay tại thời điểm hiện tại. Hãy tạm thời cất giữ câu hỏi hóc búa đó vào một góc tâm trí của bạn. Vậy thì, tại sao tôi lại phải tốn công hao sức lải nhải và nhai đi nhai lại cái chủ đề về từ khóa this này xuyên suốt vài trang giấy vừa qua? Bạn chắc hẳn đã nắm bắt được ý đồ của tôi rồi phải không? Bạn đang cực kỳ nóng lòng muốn vượt qua những lý thuyết khô khan này để tiến tới những phần kỹ thuật tiếp theo. Quan điểm tối thượng mà tôi muốn khắc sâu vào tâm trí bạn là: chính bạn dưới tư cách là người tác giả viết mã, cùng với toàn bộ những con người khốn khổ khác sẽ phải đọc đoạn mã đó dù là trong vài năm hay hàng chục năm nữa trong tương lai, đều bắt buộc phải mang trong mình một sự nhận thức cực kỳ sắc bén về từ khóa this. Đó chính là sự lựa chọn kiến trúc, là cái gánh nặng nghiệp vụ khổng lồ mà bạn đang tự tay giáng xuống đầu quá trình đọc hiểu của đoạn mã đó.

Và vâng, cái nguyên lý trách nhiệm nặng nề này cũng hoàn toàn ứng nghiệm đối với cái quyết định sử dụng từ khóa khai báo lớp – như những gì đã được mổ xẻ ở Chương 3 – bởi vì theo một lẽ tất yếu không thể chối cãi, đại đa số các phương thức nằm bên trong lớp đều sẽ bị ép buộc phải trở thành những hàm có nhận thức về từ khóa this. Hãy duy trì một sự tỉnh táo và cảnh giác cao độ đối với cái quyết định sử dụng từ khóa this này bên trong những dòng mã mà bạn tự tay thiết kế. Hãy thi hành nó với một ý đồ kiến trúc rõ ràng, và hãy đảm bảo rằng việc ứng dụng nó sẽ kiến tạo ra một mức độ lợi ích về mặt kết quả đầu ra vượt trội hơn hẳn so với cái gánh nặng phức tạp mà nó đẻ ra. Bạn phải kiểm chứng một cách khắt khe để chắc chắn rằng việc sử dụng từ khóa this trong mã nguồn của bạn phải thực sự đáng đồng tiền bát gạo và chứng minh được giá trị cốt lõi của nó. Để tôi diễn đạt cái chân lý này theo một khía cạnh phũ phàng hơn: tuyệt đối đừng bao giờ dại dột sử dụng các đoạn mã có nhận thức về từ khóa this trừ khi bạn có đủ lý lẽ kỹ thuật sắc bén để biện minh cho sự hiện diện của nó, và trừ khi bạn đã đặt lên bàn cân tính toán cực kỳ cẩn thận mọi cái giá phải trả về mặt bảo trì hệ thống. Chỉ bởi vì bạn đã từng bắt gặp vô số những đoạn mã ví dụ nhan nhản trên mạng xã hội ném từ khóa this bừa bãi khắp mọi nơi trong mã nguồn của thiên hạ, điều đó không hề cấu thành nên một lý do chính đáng để khẳng định rằng từ khóa this xứng đáng có một chỗ đứng bên trong cái đoạn mã mà bạn đang chịu trách nhiệm phát triển này.

Cơ chế từ khóa this bên trong ngôn ngữ JavaScript, khi được kết hợp sức mạnh song kiếm hợp bích cùng với cơ chế ủy quyền thông qua chuỗi nguyên mẫu ẩn, thực sự đã tạo nên một trụ cột kiến trúc mang sức mạnh hủy diệt và cực kỳ vĩ đại của ngôn ngữ này. Thế nhưng, cũng giống hệt như câu nói kinh điển của văn hóa đại chúng: sức mạnh càng lớn thì trách nhiệm đi kèm càng nặng nề. Chia sẻ một chút về kinh nghiệm cá nhân của tôi, mặc dù tôi mang trong mình một sự yêu thích tột độ và trân trọng sâu sắc cái trụ cột kiến trúc ma thuật này của ngôn ngữ JavaScript, nhưng theo những con số thống kê thực tế, có lẽ tôi chỉ xài đến nó trong chưa đầy năm phần trăm tổng dung lượng mã nguồn JavaScript mà tôi từng viết ra trong suốt sự nghiệp. Và ngay cả trong những khoảnh khắc hiếm hoi mà tôi quyết định viện đến sự phục vụ của nó, tôi luôn luôn thi hành điều đó với một sự tiết chế và tính kỷ luật sắt đá. Nó tuyệt đối không bao giờ là một thứ vũ khí JavaScript mặc định mà tôi sẽ mù quáng vớ lấy đầu tiên mỗi khi đối mặt với một bài toán lập trình.

Các quy tắc gán ngữ cảnh cốt lõi

Được rồi, tôi nghĩ chúng ta đã nghe đủ những bài thuyết giáo dài dòng về mặt triết lý. Bạn đã hoàn toàn sẵn sàng tinh thần để lao thẳng vào mổ xẻ những dòng mã thực tế thao túng từ khóa this rồi, phải không? Chúng ta hãy cùng nhau lôi cái đối tượng điểm hai chiều từ Chương 3 ra để tái thiết kế và mở rộng thêm một chút, thế nhưng lần này chúng ta sẽ chỉ xây dựng nó dưới hình hài của một đối tượng mộc mạc chứa đựng các thuộc tính dữ liệu và các hàm điện toán đính kèm, thay vì viện đến sự phục vụ đao to búa lớn của từ khóa lớp. Như bạn có thể dễ dàng nhận thấy qua cấu trúc mã nguồn, các hàm khởi tạo, hàm xoay, và hàm chuyển đổi thành chuỗi đều là những thực thể có nhận thức về từ khóa this. Rất có khả năng bạn đã hình thành một thói quen xấu là nhắm mắt mặc định rằng sợi dây tham chiếu từ khóa this hiển nhiên sẽ luôn luôn và mãi mãi trỏ thẳng về cái đối tượng điểm đó. Thế nhưng, đó là một ảo tưởng kiến trúc hoàn toàn không có bất kỳ một sự bảo chứng kỹ thuật nào. Hãy liên tục tự vả vào tâm trí mình cái chân lý này xuyên suốt phần còn lại của chương: giá trị của từ khóa this phân bổ cho một hàm bị định đoạt hoàn toàn bởi việc cái hàm đó được triệu hồi như thế nào. Điều đó đồng nghĩa với việc bạn không thể nào rình mò vào định nghĩa nội tạng của hàm, hay soi xét cái xó xỉnh nơi mà hàm được định nghĩa – kể cả cái vỏ bọc lớp đang bao bọc nó – để tìm kiếm câu trả lời. Thậm chí, cái vị trí vật lý nơi hàm được gọi cũng chẳng có ý nghĩa quái gì. Chúng ta chỉ cần soi xét duy nhất một thứ là các hàm đang được gọi theo phương thức nào; đó là cái nhân tố quyền lực duy nhất mang ý nghĩa quyết định.

Triệu hồi ngữ cảnh ngầm định và mặc định

Hãy bắt đầu bằng việc đưa một cú pháp triệu hồi cực kỳ kinh điển lên bàn mổ: cú pháp gọi hàm kèm theo tên đối tượng đứng trước. Chúng ta đang tiến hành kích nổ hàm khởi tạo, thế nhưng bạn có tinh mắt nhận ra cái cụm định danh đối tượng cùng với dấu chấm chễm chệ đứng ngay phía trước nó không? Cấu trúc ngữ pháp này chính là một lệnh ràng buộc ngữ cảnh ngầm định… Nó gửi một tối hậu thư cho cỗ máy JavaScript rằng: hãy ngay lập tức triệu hồi cái hàm khởi tạo này và đồng thời ép buộc sợi dây tham chiếu từ khóa this của nó phải trỏ thẳng vào cái đối tượng điểm đó. Đây đích thị là cái kịch bản hoạt động bình thường và thuận tự nhiên nhất mà trí não con người có thể kỳ vọng ở một từ khóa this, và đồng thời nó cũng vinh dự là một trong những phương thức phổ cập nhất mà chúng ta vẫn hay dùng để kích hoạt các hàm. Vì vậy, một kiểu triệu hồi mang tính chất điển hình như thế này luôn luôn nôn ra cho chúng ta một kết quả đầu ra vô cùng trực quan và dễ hiểu. Và đó thực sự là một điều đáng để ăn mừng trong thế giới lập trình đầy rẫy sự phức tạp này!

Thế nhưng, bức tranh sẽ bị bóp méo ra sao nếu như chúng ta giở một thủ thuật khác: tách rời hàm ra khỏi đối tượng và gán nó vào một hằng số, sau đó mới tiến hành triệu hồi cái hằng số đó? Bạn rất có thể sẽ rơi vào cái bẫy tư duy và tự huyễn hoặc bản thân rằng chúng ta vẫn sẽ thu về một kết quả đầu ra giống y hệt như cái đoạn mã trước đó. Đáng buồn thay, đó hoàn toàn không phải là cái cơ chế mà hệ thống gán giá trị từ khóa this của ngôn ngữ JavaScript được thiết kế để vận hành. Cái vị trí triệu hồi của hàm lúc này đã biến thành một lời gọi hàm trơ trọi, một cấu trúc hoàn toàn dị biệt so với lời gọi hàm có dính kèm đối tượng. Khoảnh khắc mà hoàn toàn không có sự chống lưng của bất kỳ một ngữ cảnh ngầm định nào – tức là không có cụm định danh đối tượng – và cũng vắng bóng hoàn toàn bất kỳ một cơ chế can thiệp gán từ khóa this nào khác, thì quy luật gán ngữ cảnh mặc định sẽ tự động được hệ thống kích hoạt để lấp đầy khoảng trống. Vậy thì, từ khóa this sẽ nhắm mũi tên trỏ vào cái thực thể nào khi một hàm trơ trọi bị kích nổ theo kiểu như vậy? Câu trả lời đầy tính triết học là: điều đó còn phụ thuộc vào nhiều yếu tố… Ôi chao. Phụ thuộc ư? Cái từ ngữ đó nghe sặc mùi của sự lú lẫn và bất an.

Nhưng xin bạn đừng quá hoảng loạn, tình hình thực tế không đến mức tồi tệ như cái ấn tượng ban đầu đâu. Việc hệ thống ra quyết định gán ngữ cảnh mặc định phụ thuộc hoàn toàn vào việc liệu cái đoạn mã đó có đang bị nhốt trong lồng ấp của chế độ nghiêm ngặt hay không. Và thật may mắn cho nhân loại, gần như toàn bộ mọi đoạn mã JavaScript được sinh ra trong cái thời đại tân tiến này đều đang ngoan ngoãn vận hành bên trong chế độ nghiêm ngặt; lấy một ví dụ điển hình, các Mô đun Tiêu chuẩn ngôn ngữ kịch bản châu Âu luôn luôn mặc định chạy trong chế độ nghiêm ngặt, và bộ quy luật thép này cũng áp dụng y hệt cho toàn bộ các đoạn mã lọt thỏm bên trong một khối cấu trúc lớp. Hơn thế nữa, gần như toàn bộ các đoạn mã JavaScript được nhào nặn qua các công cụ chuyển đổi mã – điển hình như Babel hay hệ sinh thái TypeScript – đều được lập trình viên ép buộc phải chèn thêm chỉ thị khai báo chế độ nghiêm ngặt ngay từ ban đầu. Vì vậy, trong hầu hết mọi trường hợp thực tiễn, các đoạn mã JavaScript hiện đại đều sẽ mặc định vận hành trong môi trường chế độ nghiêm ngặt, và hệ lụy tất yếu là cái ngữ cảnh của phép gán mặc định sẽ chẳng thèm phụ thuộc vào bất kỳ một điều kiện ngoại cảnh nào nữa; kết quả của nó vô cùng thẳng thắn và tàn nhẫn: giá trị không xác định. Đúng vậy, chỉ ngắn gọn như vậy thôi! Hãy luôn khắc cốt ghi tâm một chân lý lịch sử: giá trị không xác định hoàn toàn không mang cái ý nghĩa nguyên thủy là chưa được định nghĩa; bản chất thực sự của nó là đã được hệ thống định nghĩa, nhưng lại được nhét cho một cái giá trị rỗng tuếch mang tính chất đặc chủng là giá trị không xác định… Tôi hoàn toàn thấu hiểu nỗi bức xúc của bạn… cái tên gọi và cái ý nghĩa thực sự của nó lệch pha nhau một cách vô lý. Đó chính là cái di sản thiết kế mang tính chất nghiệp chướng của ngôn ngữ mà chúng ta đành phải cắn răng chấp nhận.

Điều này dẫn đến một hệ quả kiến trúc là, nếu một lời gọi hàm trơ trọi bị ném vào chạy trong môi trường chế độ nghiêm ngặt, nó sẽ ngay lập tức gào thét và ném ra một ngoại lệ lỗi làm sập chương trình. Căn nguyên sâu xa là do thao tác truy cập thuộc tính bên trong hàm lúc này đã biến thành một nỗ lực tuyệt vọng nhằm moi móc thuộc tính từ một giá trị không xác định, một hành vi ngông cuồng bị luật pháp ngôn ngữ nghiêm cấm tuyệt đối. Hãy dành ra một khoảnh khắc tĩnh tâm để chiêm nghiệm: tại sao những bộ óc thiết kế ngôn ngữ JavaScript lại đưa ra một quyết định tàn nhẫn là ép buộc cái ngữ cảnh mặc định phải rơi vào trạng thái không xác định, để rồi bất kỳ một lần triệu hồi hàm ngữ cảnh mặc định nào vô tình dính líu đến một hàm có nhận thức về từ khóa this cũng sẽ chuốc lấy một kết cục bi thảm là chết chìm trong một ngoại lệ lỗi như vậy? Câu trả lời mang đậm tính chất thiết kế phòng ngự là bởi vì một hàm có nhận thức về từ khóa this luôn luôn và vĩnh viễn thèm khát một giá trị từ khóa this để có thể hoạt động bình thường. Cái cú pháp triệu hồi hàm trơ trọi hoàn toàn không hề bơm bất kỳ một từ khóa this nào vào bụng hàm, vì vậy bản thân cái hành động gọi hàm đó đích thị là một sai lầm ngớ ngẩn của lập trình viên, và hệ thống có nghĩa vụ phải ném ra một ngoại lệ lỗi chói tai để cái sai lầm đó có cơ hội được lôi ra ánh sáng và khắc phục triệt để. Bài học xương máu được rút ra ở đây là: tuyệt đối, vĩnh viễn không bao giờ được phép triệu hồi một hàm có nhận thức về từ khóa this mà lại keo kiệt không chịu đút cho nó một từ khóa this đàng hoàng!

Gán ngữ cảnh tường minh thông qua tiện ích

Để mở rộng tầm nhìn về bức tranh toàn cảnh: trong cái môi trường chế độ không nghiêm ngặt vốn ngày càng trở nên lỗi thời, ngữ cảnh mặc định sẽ tự động bám rễ vào đối tượng toàn cục – thứ mà ngôn ngữ JavaScript đã chuẩn hóa và định nghĩa bằng cái tên đối tượng toàn cục globalThis, trong hệ sinh thái trình duyệt web thì nó về cơ bản là một cái tên mạo danh của đối tượng cửa sổ, còn trong môi trường Node thì nó lộ diện dưới cái tên đối tượng toàn cục. Vì vậy, khi một hàm trơ trọi chạy rông trong chế độ không nghiêm ngặt, biểu thức thao tác thuộc tính của nó sẽ biến tướng thành một cuộc tấn công trực tiếp vào đối tượng toàn cục. Hậu quả là, các thuộc tính của đối tượng toàn cục bị ghi đè một cách không thương tiếc, trong khi cái đối tượng mục tiêu mà chúng ta thực sự muốn nhắm tới lại hoàn toàn bình an vô sự và không hề bị tác động một chút nào. Đây đích thị là một thảm họa kiến trúc, bởi vì nó gần như chắc chắn một trăm phần trăm không phải là cái kết quả đầu ra mà tác giả đã kỳ vọng trong đầu. Nó không chỉ tồi tệ ở chỗ tự dưng đẻ ra một đống biến toàn cục rác rưởi, mà nó còn thất bại thảm hại trong việc thay đổi trạng thái của đối tượng mục tiêu, dẫn đến việc các con bọ lỗi logic chắc chắn sẽ sinh sôi nảy nở khắp nơi trong chương trình. Thật là một cái giá quá đắt! Chẳng có bất kỳ một kỹ sư phần mềm tỉnh táo nào lại khao khát cái cảnh hàng tá những biến toàn cục do tai nạn được hệ thống lén lút sinh ra từ vô vàn những xó xỉnh khác nhau trong mã nguồn. Bài học khắc cốt ghi tâm: hãy luôn luôn sử dụng các biện pháp răn đe để đảm bảo rằng mã nguồn của bạn phải bị nhốt trong chế độ nghiêm ngặt!

Các hàm điện toán hoàn toàn có đặc quyền được hệ thống triệu hồi song hành cùng với một ngữ cảnh tường minh, bằng cách viện đến sức mạnh của các phương thức tiện ích được tích hợp sẵn từ thuở hồng hoang là tiện ích call hoặc tiện ích apply. Cấu trúc gọi hàm thông qua tiện ích call kết hợp truyền đối tượng mục tiêu về cơ bản mang lại một kết quả kiến trúc giống hệt như thao tác gọi hàm ngầm định thông qua đối tượng, bởi vì cả hai thứ vũ khí này đều thi hành một mệnh lệnh là cưỡng ép cái đối tượng mục tiêu đó phải trở thành ngữ cảnh từ khóa this dành riêng cho cái lần triệu hồi hàm hiện tại. Cả hai tiện ích calltiện ích apply đều có chung một đặc điểm là đòi hỏi đối số đầu tiên truyền vào bụng chúng phải là một giá trị ngữ cảnh từ khóa this; trong đại đa số các kịch bản thực tế thì đó sẽ là một đối tượng vật lý, thế nhưng xét về mặt luật pháp ngôn ngữ thì nó hoàn toàn có quyền là bất kỳ một kiểu giá trị nào khác trên đời như một con số hay một chuỗi ký tự. Sự phân ly giữa hai tiện ích này nằm ở chỗ tiện ích call sẽ yêu cầu bạn truyền các đối số tiếp theo theo kiểu nối đuôi nhau rời rạc và nó sẽ chuyển tiếp toàn bộ đống đó cho hàm được triệu hồi, trong khi đó tiện ích apply lại đưa ra một yêu sách là đối số thứ hai của nó bắt buộc phải là một cấu trúc mảng chứa đựng toàn bộ các giá trị để nó bung ra làm đối số truyền vào hàm.

Việc phải ngồi chiêm nghiệm và áp dụng cái phong cách triệu hồi hàm thông qua thao tác gán ngữ cảnh tường minh bằng tiện ích call hay tiện ích apply vào trong chương trình của bạn thoạt nhìn có vẻ mang lại một cảm giác khá gượng gạo và thô kệch về mặt cú pháp. Thế nhưng sức mạnh thực sự và độ hữu dụng của nó vượt xa những gì mà nhãn quan thông thường có thể đánh giá trong cái nhìn đầu tiên. Hãy lật lại cái đoạn mã ví dụ ban đầu, giả sử tôi khao khát muốn khai sinh ra một đối tượng điểm hoàn toàn mới, thế nhưng tôi lại cực kỳ lười biếng và không hề muốn phải nai lưng ra sao chép hay gõ lại từ đầu toàn bộ các định nghĩa phức tạp của những hàm khởi tạo, hàm xoay hay hàm chuyển đổi thành chuỗi vốn đã tồn tại sẵn trên đối tượng điểm ban đầu. Giải pháp kiến trúc của tôi là đi mượn một sợi dây tham chiếu trỏ đến hàm, cụ thể là hàm khởi tạo của đối tượng điểm cũ, và sau đó sử dụng quyền lực của tiện ích call để gán một cách tường minh cái đối tượng điểm mới – vốn đang trống rỗng – vào vị trí ngữ cảnh từ khóa this cho lần thực thi đó. Trong khoảnh khắc mà hàm khởi tạo đang chạy bục mặt, từ khóa this lẩn khuất bên trong nội tạng của nó sẽ ngoan ngoãn trỏ thẳng vào cái đối tượng điểm mới, và đó chính là nguồn cơn giải thích tại sao các thuộc tính tọa độ mới lại được hệ thống khởi tạo và gắn thành công lên cái đối tượng mới toanh đó. Bất kỳ một hàm nào có nhận thức về từ khóa this đều có thể bị đem ra mượn mọc theo cái phong cách vô lại nhưng cực kỳ hiệu quả này.

Can thiệp vòng đời đối tượng bằng từ khóa tạo mới

Vẫn tồn tại một con đường kiến trúc hoàn toàn khác để thực thi cái trò chia sẻ hành vi giữa đối tượng điểm cũ và đối tượng điểm mới, đó là chúng ta tiến hành đắp thêm các sợi dây tham chiếu dùng chung trỏ thẳng đến các hàm đó lên trên cơ thể của bất kỳ một đối tượng mục tiêu nào mà chúng ta nhắm tới. Cái vị trí triệu hồi lúc này sẽ khoác lên mình một hình hài tự nhiên và mang tính công thái học hơn rất nhiều, một thứ cú pháp hoàn toàn ỷ lại vào cơ chế gán ngữ cảnh ngầm định. Nhìn lướt qua thì cái phương pháp tiếp cận này dường như mang lại một cảm giác mã nguồn sạch sẽ và gọn gàng hơn đôi chút khi đặt lên bàn cân so sánh giữa hai cú pháp triệu hồi. Thế nhưng, cái yếu điểm chí mạng của phương pháp này là nó ép buộc chúng ta phải tự tay thi hành các thao tác phẫu thuật để sửa đổi nội tạng của bất kỳ một đối tượng mục tiêu nào bằng cách nhét thêm những sợi dây tham chiếu hàm dùng chung đó vào, một chuỗi các thao tác mang tính chất cực kỳ rườm rà, đậm chất thủ công mỹ nghệ, và mở toang cánh cửa rước vào vô vàn những sai sót logic tiềm ẩn. Trong một số giới hạn ngữ cảnh nhất định, cái phương pháp tiếp cận chắp vá này vẫn có thể được cộng đồng cắn răng chấp nhận, thế nhưng trong đại đa số các chiến trường phần mềm khác, việc áp dụng thiết quân luật gán ngữ cảnh tường minh thông qua tiện ích call hay tiện ích apply lại luôn luôn chiếm thế thượng phong và được giới tinh hoa ưu ái lựa chọn hơn hẳn.

Từ đầu chương đến giờ, chúng ta đã được vinh dự diện kiến ba phương thức hoàn toàn khác biệt để thi hành phép gán ngữ cảnh ngay tại vị trí triệu hồi hàm: mặc định, ngầm định,tường minh. Một con đường thứ tư để có thể kích nổ một hàm, và đồng thời định đoạt luôn số phận của từ khóa this cho riêng cái lần triệu hồi đó, là viện đến quyền lực tối cao của từ khóa tạo mới new. Có một tiểu tiết kiến trúc vô cùng tinh vi cần phải được làm sáng tỏ ở đây: để một hàm có đủ tư cách pháp lý để bị từ khóa tạo mới new triệu hồi một cách hợp lệ, thì cái hàm đó bắt buộc phải được khai báo dưới hình thái của một biểu thức hàm được gán vào một thuộc tính. Nếu chúng ta đối chiếu với các đoạn mã ví dụ trước đây, hình thái phương thức viết tắt gọn gàng sẽ đẻ ra một hàm bị hệ thống cấm tiệt không cho phép được gọi chung với từ khóa tạo mới new. Đa số các lập trình viên thường chỉ quen mắt với việc nhìn thấy từ khóa tạo mới new cặp kè với từ khóa lớp để làm cái trò khởi tạo bản sao. Thế nhưng, nếu xét trên bình diện là một cơ chế cốt lõi nằm sâu dưới đáy của ngôn ngữ JavaScript, thì từ khóa tạo mới new hoàn toàn không mang bản chất là một toán tử sinh ra chỉ để phục vụ độc quyền cho các lớp. Theo một khía cạnh học thuật, từ khóa tạo mới new thực chất là một tên không tặc, nó tiến hành không tặc một hàm điện toán và sử dụng vũ lực để bẻ lái hành vi của cái hàm đó sang một chế độ vận hành hoàn toàn dị biệt so với một lời gọi hàm thông thường.

Dưới đây là bốn bước quy trình đặc chủng mà cỗ máy JavaScript bắt buộc phải nai lưng ra thực thi mỗi khi có một hàm bị kích nổ bởi từ khóa tạo mới new. 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ông khai của cái hàm đang bị gọi. Thứ ba, nó ra lệnh kích nổ cái hàm đó và đồng thời cưỡng ép ngữ cảnh từ khóa this phải cắm chặt vào cái đối tượng mới toanh trống rỗng kia. Cuối cùng, nếu như bản thân cái hàm đó lại cứng đầu không chịu tự mình trả về một giá trị đối tượng tường minh thông qua câu lệnh trả về, thì cỗ máy sẽ tự động ngầm định rằng cái lời gọi hàm đó đáng lý ra phải nôn ra cái đối tượng mới đã được nhào nặn qua ba bước đầu tiên. Cái bước thứ tư này ẩn chứa một hàm ý đe dọa rằng nếu bạn lỡ dại dùng từ khóa tạo mới new để gọi một hàm mà bản thân nó lại đang cố tình nôn ra một đối tượng của riêng nó, thì cái đối tượng mới được hệ thống cất công nặn ra từ ba bước đầu tiên sẽ bị vứt thẳng vào sọt rác một cách không thương tiếc. Đó đích thị là một cái bẫy sập vô cùng xảo quyệt mà các kỹ sư phải cực kỳ cảnh giác, bởi vì xét về mặt hiệu quả, nó đã thẳng tay tiêu hủy cái đối tượng mới đó trước cả khi chương trình có cơ hội kịp chạm tay vào và lưu trữ lại một sợi dây tham chiếu trỏ đến nó. Về cơ bản, từ khóa tạo mới new vĩnh viễn không bao giờ nên được xài để triệu hồi một hàm có chứa chấp những câu lệnh trả về đối tượng tường minh bên trong nội tạng của nó.

Bản chất thực sự của hàm mũi tên

Toàn bộ những chân lý học thuật mà tôi đã mạnh miệng khẳng định từ đầu chương đến giờ về từ khóa this lẩn khuất bên trong các hàm điện toán, cũng như cái cơ chế mà hệ thống dùng để định đoạt nó dựa trên vị trí triệu hồi, tất cả đều được xây dựng trên một giả định khổng lồ: đó là bạn đang phải đối mặt và giao chiến với một hàm điện toán – hay phương thức – mang tính chất bình thường và tuân thủ luật pháp ngôn ngữ. Vậy thì một hàm điện toán mang tính chất bất thường thì sẽ khoác lên mình hình hài gớm ghiếc như thế nào?

Ảo giác về vị trí triệu hồi và giới hạn của hàm gọi lại

Nó mang một dáng vẻ cú pháp đặc trưng của cấu trúc hàm mũi tên. Phải thừa nhận rằng tôi đang có chút châm biếm và tỏ ra không công bằng khi dám gán mác bất thường cho một hàm mũi tên và sử dụng một ví dụ vô thưởng vô phạt để minh họa. Đó chỉ là một câu nói đùa học thuật thôi. Hãy cùng nhau soi xét một ví dụ thực chiến của hàm mũi tên trong môi trường tương tác với Mô hình đối tượng tài liệu. Để tạo ra một bức tranh tương phản rõ nét, tôi cũng sẽ phơi bày luôn cái phiên bản không sử dụng mũi tên tương đương của nó. Hoặc nếu chúng ta muốn quay ngược bánh xe lịch sử và hoài cổ một chút – phong cách yêu thích của tôi! – chúng ta hoàn toàn có thể thử nghiệm với hình thái khai báo hàm độc lập truyền thống. Hoặc nếu như cái hàm đó lại chễm chệ xuất hiện dưới thân phận là một phương thức nằm lọt thỏm bên trong một định nghĩa lớp, hoặc dưới dạng một phương thức viết tắt gọn gàng bên trong một cú pháp khai báo đối tượng trực tiếp, thì hình hài của nó sẽ biến hóa tương ứng. Trọng tâm học thuật tối thượng mà tôi thực sự muốn xoáy sâu vào là cách thức mà mỗi một hình thái hàm này sẽ phản ứng và hành xử ra sao đối với sợi dây tham chiếu từ khóa this của riêng chúng, và liệu cái hình thái hàm mũi tên đầu tiên kia có thực sự ẩn chứa một sự nổi loạn khác biệt nào so với những người anh em truyền thống của nó hay không – bật mí là nó hoàn toàn dị biệt!

Thế nhưng trước khi đi sâu vào giải phẫu, hãy cùng nhau khởi động bằng một bài kiểm tra kiến thức nho nhỏ để xem liệu bạn có đang thực sự tỉnh táo và theo kịp bài giảng hay không. Đối với mỗi một hình thái hàm vừa được phơi bày ra đó, làm thế quái nào mà chúng ta có thể tiên tri được chính xác mỗi một từ khóa this sẽ nhắm mũi tên trỏ vào cái thực thể nào? Tôi chân thành hy vọng rằng bạn sẽ dõng dạc phản hồi bằng một triết lý kiểu như: điều kiện tiên quyết là chúng ta phải cày xới xem các hàm đó đang được triệu hồi ra sao. Nghe có vẻ hoàn toàn hợp tình hợp lý. Giả sử như kiến trúc chương trình của chúng ta được phác họa như một hệ thống xử lý sự kiện giao diện. Chà, thú vị đây. Chắc hẳn một nửa số độc giả đang nghiền ngẫm cuốn sách này chưa từng có cơ hội được tận mắt chứng kiến những đoạn mã tương tác với Giao diện lập trình ứng dụng của Mô hình đối tượng tài liệu kiểu cổ điển bao giờ. Tôi dường như có thể nghe thấy tiếng còi báo động của sự hoang mang đang rú lên ầm ĩ ngay lúc này! Thành thật xin lỗi, tôi đang vô tình để lộ cái tuổi tác nghề nghiệp già cỗi của mình ở đây. Tôi đã lăn lộn trong cái ngành công nghiệp này đủ lâu để có thể hoài niệm về cái thời đại mà chúng ta phải cày cuốc viết những thể loại mã nguồn thủ công như thế này, rất lâu trước khi những thứ tiện ích mì ăn liền như thư viện jQuery xuất hiện và xả rác ký tự đô la lên khắp mọi mặt trận mã nguồn.

Và sau vô vàn những năm tháng tiến hóa không ngừng nghỉ của hệ sinh thái lập trình giao diện phía trước, dường như chúng ta đã hạ cánh an toàn xuống một vùng đất mang đậm tính hiện đại hơn rất nhiều – ít nhất thì đó cũng là cái định kiến đang thống trị tư duy hiện tại. Tôi mạnh dạn đoán rằng đại đa số các bạn trẻ ngày nay đã quá quen mắt với việc chiêm ngưỡng những đoạn mã thuộc về các bộ khung kiến trúc dựa trên thành phần – điển hình như React – với cú pháp lồng ghép mã nhãn dán vào trực tiếp trong logic. Dĩ nhiên, ngoài kia vẫn còn tồn tại hàng triệu cách thức kiến trúc khác nhau để định hình hình hài cho những đoạn mã đó, phụ thuộc hoàn toàn vào việc bạn đang tôn thờ bộ khung nào hay triết lý nào. Hoặc thậm chí có khả năng bạn đã vứt bỏ hoàn toàn việc xài các thành phần dựa trên hệ tư tưởng từ khóa lớp và từ khóa this, bởi vì bạn đã quyết định di cư toàn bộ tư tưởng của mình sang vương quốc của các hàm móc nối vòng đời và cơ chế bao đóng. Dù trong bất kỳ hoàn cảnh nào, thì đối với mục đích học thuật của cuộc thảo luận này, toàn bộ chương sách này sinh ra là để mổ xẻ về từ khóa this, vì vậy chúng ta bắt buộc phải trói buộc bản thân vào một phong cách viết mã có chứa đựng sự hiện diện của từ khóa this, để đảm bảo rằng chúng ta có những đoạn mã thực tế liên quan mật thiết đến chủ đề đang được mổ xẻ.

Cơ chế từ khóa này theo phạm vi từ vựng

Sự thật phũ phàng là cả hai đoạn mã ví dụ vừa rồi đều thất bại thảm hại trong việc phơi bày cho chúng ta thấy cái hàm xử lý nhấp chuột thực sự đang được định nghĩa ở cái xó xỉnh nào. Thế nhưng tôi đã mỏi miệng lặp đi lặp lại cái chân lý này từ đầu chương đến giờ, rằng cái việc nó được định nghĩa ở đâu hoàn toàn vô giá trị; thứ duy nhất mang ý nghĩa quyết định là… cái gì cơ? Hãy cùng đồng thanh hô to lên nào… thứ duy nhất mang ý nghĩa quyết định là hàm đó được triệu hồi bằng cách thức nào. Vậy thì rốt cuộc cái hàm xử lý nhấp chuột kia đang bị kích nổ ra sao? Cái vị trí triệu hồi của nó đang lẩn trốn ở đâu, và nó đang ngoan ngoãn tuân phục theo cái quy tắc gán ngữ cảnh nào? Nếu như bộ não của bạn đang bị kẹt cứng, thì xin đừng quá hoảng loạn. Tôi đang cực kỳ cố tình thiết kế ra một cái bẫy học thuật đầy thử thách này, cốt chỉ để lột trần và phơi bày ra ánh sáng một chân lý cực kỳ mang tính chất sống còn. Khi các hành động ràng buộc hàm xử lý sự kiện được thi hành, trong cả hai kịch bản, chúng ta đều đã chỉ định một sợi dây tham chiếu có dính dáng đến từ khóa this, điều này tung hỏa mù ám chỉ rằng dường như đang tồn tại một đối tượng ngữ cảnh từ khóa this ôm giữ một thuộc tính trên cơ thể nó, và cái thuộc tính đó lại đang nắm giữ cái định nghĩa hàm của chúng ta. Vậy thì, liệu cái cú pháp tham chiếu đó có nghiễm nhiên trở thành cái vị trí triệu hồi hay không? Nếu đúng là như vậy, thì đạo luật gán ngữ cảnh nào sẽ được hệ thống áp dụng? Có phải là quy luật ngữ cảnh ngầm định hay không?

Đáng buồn thay, câu trả lời là một chữ không phũ phàng. Vấn đề cốt lõi mang tính chất chí mạng ở đây là, chúng ta hoàn toàn bị mù và không có bất kỳ một khả năng nào để có thể tận mắt chứng kiến cái vị trí triệu hồi thực sự bên trong cái chương trình chết tiệt này. Ôi chao. Nếu như nhãn quan kiến trúc của chúng ta không thể chạm tới cái vị trí triệu hồi, thì làm thế quái nào mà chúng ta có thể tiên tri được bằng cách thức nào mà cái hàm đó sẽ bị cỗ máy đem ra kích nổ trên thực tế? Đó chính là cái trọng tâm học thuật tuyệt đối mà tôi đang muốn dồn ép bạn phải đối mặt. Cái việc chúng ta cất công truyền vào một sợi dây tham chiếu hàm hoàn toàn vô giá trị trong việc giải quyết bài toán. Đó chỉ đơn thuần là một thao tác truyền tay một sợi dây tham chiếu trỏ đến một giá trị đối tượng hàm mà thôi. Nó tuyệt đối không phải là một vị trí triệu hồi. Lẩn khuất sâu bên dưới những tầng kiến trúc trừu tượng, ở một cái xó xỉnh tối tăm nào đó bên trong nội tạng của bộ khung, thư viện, hay thậm chí là trong chính cái môi trường thực thi JavaScript, khi một gã người dùng nào đó nhấp chuột vào nút bấm, một sợi dây tham chiếu trỏ đến cái hàm xử lý nhấp chuột sẽ bị hệ thống tàn nhẫn lôi ra và bóp cò kích nổ.

Và như chúng ta đã từng bóng gió suy đoán, cái vị trí triệu hồi vô hình đó thậm chí sẽ còn bơm cả cái đối tượng sự kiện của Mô hình đối tượng tài liệu vào bụng hàm dưới thân phận là một đối số. Bởi vì cặp mắt trần tục của chúng ta không thể nhìn xuyên thấu để thấy được cái vị trí triệu hồi đó, nên chúng ta đành phải viện đến trí tưởng tượng phong phú của mình để mô phỏng nó. Trừ phi bạn rảnh rỗi đến mức ngồi mổ xẻ và nghiền ngẫm từng dòng mã nguồn của cái thư viện hay bộ khung đó, hoặc là một con mọt tài liệu chuyên cày cuốc các bản đặc tả kỹ thuật, bằng không bạn sẽ vĩnh viễn mù tịt và không biết được cỗ máy thực sự kỳ vọng cái điều quái quỷ gì ở cái vị trí triệu hồi đó. Điều đó dẫn đến một hệ lụy tàn khốc là việc cố gắng tiên tri xem, rốt cuộc thì từ khóa this sẽ nhắm mũi tên trỏ vào đâu bên trong cái hàm xử lý mà bạn tự tay nhào nặn ra, là một nhiệm vụ… nói một cách nhẹ nhàng nhất có thể… mang tính chất vặn xoắn não bộ và cực kỳ rắc rối. Để giải thoát bạn khỏi cái mớ bòng bong tra tấn tinh thần này, tôi sẽ đi thẳng vào vấn đề cốt lõi. Gần như toàn bộ mọi nỗ lực thiết kế nhằm triển khai một cơ chế xử lý nhấp chuột trên hành tinh này đều sẽ sử dụng một thủ thuật tà đạo nào đó giống với tiện ích call, và chúng sẽ tàn nhẫn thiết lập cái phần tử Mô hình đối tượng tài liệu – thứ mà cái bộ lắng nghe sự kiện đang bám rễ vào – trở thành cái ngữ cảnh tường minh cho lần triệu hồi đó.

Cạm bẫy của việc liên kết cứng và hiệu suất

Hãy vận động trí não để hồi tưởng lại xem cái hàm xử lý nhấp chuột của chúng ta vốn dĩ là một thực thể có nhận thức về từ khóa this, và cái sợi dây tham chiếu phần tử biểu mẫu bên trong nó đang ám chỉ một nỗ lực truy cập vào một đối tượng sở hữu thuộc tính phần tử biểu mẫu, thứ mà bản thân nó lại đang trỏ ngược về cái phần tử biểu mẫu cha. Các nút bấm của Mô hình đối tượng tài liệu, theo một quy luật mặc định phũ phàng của tự nhiên, hoàn toàn không hề được sinh ra với một thuộc tính phần tử biểu mẫu gắn trên cơ thể chúng. Nói một cách trần trụi, cái sợi dây tham chiếu từ khóa this mà hệ thống xử lý sự kiện của chúng ta sắp sửa bị hệ thống ép uổng gán cho gần như chắc chắn một trăm phần trăm là một cú lừa ngoạn mục và hoàn toàn sai lệch. Chết tiệt. Trừ phi chúng ta chấp nhận cắn răng đập đi xây lại toàn bộ cái hàm xử lý đó, bằng không chúng ta bắt buộc phải tìm ra một phương thuốc giải độc để khắc phục cái thảm họa gán nhầm này.

Một trong những phương pháp chắp vá cổ điển để xử lý vấn đề này là sử dụng một biến số lưu trữ ngữ cảnh từ vựng để vượt quyền cơ chế động. Rất nhiều những đoạn mã JavaScript đồ cổ xài cái thủ thuật tà đạo này sẽ thường xuyên sử dụng một cú pháp khai báo kiểu như biến tự thân bằng từ khóa this thay vì cái tên gọi ngữ cảnh đàng hoàng mà tôi đang cố tình gán cho nó ở đây. Tự thân là một từ ngữ ngắn gọn hơn, và nghe có vẻ ngầu hơn rất nhiều trong các diễn đàn lập trình. Thế nhưng nó lại chứa đựng một ý nghĩa ngữ nghĩa sai lệch đến mức thảm họa. Từ khóa this tuyệt đối không mang bản chất là một sợi dây tham chiếu tự thân trỏ ngược về chính bản thân cái hàm đó, mà thực chất nó là cái bối cảnh môi trường dành riêng cho cái lần triệu hồi hàm hiện tại đó. Nhìn lướt qua thì hai khái niệm đó có vẻ mang hình hài giống hệt nhau, thế nhưng xét về mặt bản chất triết học thì chúng lại là hai thế giới hoàn toàn tách biệt, khác biệt đến mức giống như việc đem táo ra so sánh với một bài hát của ban nhạc The Beatles vậy. Tôi đã sử dụng một kỹ thuật kiến trúc là định nghĩa một hàm bao bọc bên ngoài và sau đó sử dụng quyền lực để ép buộc cái vị trí triệu hồi phải khoác lên mình một hình hài tuân theo luật gán ngữ cảnh ngầm định. Bây giờ, thì cái việc cái vị trí triệu hồi nội tạng của thư viện hay môi trường trông có hình thù quái dị ra sao cũng hoàn toàn không còn mang bất kỳ một ý nghĩa nào nữa. Bởi vì chúng ta giờ đây đã thực sự nắm giữ quyền trượng cai trị cái vị trí triệu hồi. Dòng khai báo gán biến ngữ cảnh mang tính chất sinh tử đối với toàn bộ cái màn ảo thuật này. Nó tiến hành khai sinh ra một biến số từ vựng, tuyệt đối không phải là một từ khóa ma thuật nào cả, mà chức năng duy nhất của nó là ôm giữ một bức ảnh chụp nhanh của cái giá trị đang nằm trong từ khóa this của môi trường bên ngoài.

Cái tên gọi học thuật dành cho cái khuôn mẫu thiết kế ma lanh này là từ khóa this từ vựng, mang hàm ý mô tả một từ khóa this đã bị tha hóa và hành xử giống hệt như một biến số thuộc phạm vi từ vựng tĩnh thay vì phải làm đúng bổn phận là một sợi dây ràng buộc ngữ cảnh động. Thế nhưng, hóa ra cỗ máy ngôn ngữ JavaScript lại bí mật cất giấu một phương tiện vận chuyển tiện lợi hơn rất nhiều để có thể thi triển cái màn ảo thuật từ khóa this từ vựng này. Đó chính là cấu trúc hàm mũi tên! Trái ngược hoàn toàn với phần còn lại của thế giới hàm, hàm mũi tên là một sinh vật đặc biệt ở chỗ nó… chẳng có cái quái gì đặc biệt cả. Hay nói một cách chính xác hơn là nó hoàn toàn từ chối việc định nghĩa bất kỳ một hành vi đặc biệt nào dành cho từ khóa this một cách tuyệt đối. Nằm lọt thỏm bên trong nội tạng của một hàm mũi tên, từ khóa this… đã bị tước đoạt hoàn toàn thân phận là một từ khóa… Nó bị giáng cấp và trở nên hoàn toàn không có bất kỳ một sự khác biệt nào so với bất kỳ một biến số mộc mạc nào khác. Khi một từ khóa this xui xẻo bị hệ thống chạm trán ở bên trong một hàm mũi tên, nó sẽ bị cỗ máy đối xử tệ bạc y hệt như một biến số từ vựng thông thường, chứ không còn là một từ khóa mang siêu năng lực nữa. Và bởi vì bản thân cái hàm đó hoàn toàn bị vô sinh và không hề sở hữu bất kỳ một biến số this nào, nên cỗ máy JavaScript lại tiếp tục giở cái thói quen ngàn đời mà nó vẫn luôn làm đối với các biến số từ vựng: nó xách vali leo ngược lên một tầng của tháp phạm vi từ vựng – và nó sẽ quét mắt để lùng sục xem liệu có bất kỳ một từ khóa this nào đã được đăng ký hộ khẩu trong cái phạm vi đó hay không. Hàm mũi tên hoàn toàn không phải là một hình thái đường cú pháp sinh ra để thay thế cho cái trò liên kết cứng từ khóa this.

Những biến thể triệu hồi ngoại lệ

Trước khi chúng ta đặt dấu chấm hết cho cuộc hành trình cày xới đầy mệt mỏi và dông dài về từ khóa this, hệ sinh thái ngôn ngữ vẫn còn giấu giếm một vài biến thể triệu hồi hàm cực kỳ dị hợm và mang tính chất ngoại lệ mà chúng ta bắt buộc phải lôi ra mổ xẻ để tránh những cái bẫy sập bất ngờ.

Cú pháp triệu hồi hàm gián tiếp

Hãy vắt óc để hồi tưởng lại cái ví dụ thảm họa từ giai đoạn đầu của chương này, nơi mà lời gọi hàm khởi tạo trơ trọi đã hoàn toàn bất lực trong việc cung cấp tín hiệu gán ngữ cảnh từ khóa this cần thiết để hệ thống hoạt động bình thường. Thế nhưng, thế giới mã nguồn vẫn còn đầy rẫy những con đường tà đạo khác để có thể tận mắt chứng kiến một sự sụp đổ tương tự. Lấy ví dụ, một thứ cú pháp mang hình hài kỳ dị bao gồm một cặp ngoặc đơn ôm lấy một con số và một tham chiếu hàm được phân cách bởi dấu phẩy, theo sau là một cặp ngoặc đơn triệu hồi chứa đối số. Cái thứ cú pháp quái thai này thực chất đang thi hành thao tác đánh giá một biểu thức chuỗi dấu phẩy. Hệ quả cuối cùng của một biểu thức kiểu như vậy chính là cái giá trị được đánh giá ở vị trí chốt sổ cuối cùng, trong trường hợp này nó chính là cái sợi dây tham chiếu hàm trần trụi. Động thái này tàn nhẫn bứt cái sợi dây tham chiếu hàm đó ra khỏi bến đỗ đối tượng và ném nó chỏng chơ lên trên ngăn xếp biểu thức, để rồi sau đó mới tiến hành triệu hồi cái giá trị cô đơn đó bằng một cặp đối số. Đây đích thị là một hành vi triệu hồi gián tiếp của một hàm điện toán. Và cái kết cục bi thảm nào sẽ xảy ra? Nó thực chất lại ăn khớp một cách hoàn hảo với quy luật gán ngữ cảnh mặc định mà chúng ta đã từng soi xét dưới kính hiển vi ở phần trước của chương này.

Hệ lụy là, nếu chúng ta đang dạo chơi trong chế độ không nghiêm ngặt, thì cái từ khóa this dành cho cái lời gọi hàm gián tiếp đó sẽ được hệ thống ngầm định gán bằng đối tượng toàn cục. Nếu như chúng ta đang bị cùm kẹp trong chế độ nghiêm ngặt, thì nó sẽ bị ném cho một cái giá trị không xác định, và thao tác gán thuộc tính bên trong hàm lúc này sẽ biến thành một hành vi vi phạm pháp luật và hệ thống sẽ ném ra một ngoại lệ lỗi để trừng phạt cái tội dám cả gan truy cập thuộc tính trên một cái xác không hồn mang giá trị không xác định. Tồn tại vô vàn những phương thức biến thái khác nhau để có thể thi triển một đòn triệu hồi hàm gián tiếp. Một ví dụ kinh điển khác của nghệ thuật triệu hồi hàm gián tiếp chính là khuôn mẫu Biểu thức hàm được triệu hồi ngay lập tức, một thứ vũ khí từng thống trị giới lập trình trước thời kỳ của các mô đun hiện đại. Như bạn có thể dễ dàng phân tích bằng nhãn quan kiến trúc, cái giá trị biểu thức hàm đó bị hệ thống đẩy thẳng lên ngăn xếp biểu thức, và sau đó nó bị kích nổ một cách phũ phàng bằng cặp ngoặc đơn nằm chễm chệ ở đoạn cuối.

Thế nhưng, số phận của đoạn mã gọi hàm mang cấu trúc ngoặc đơn bọc lấy tham chiếu hàm rồi theo sau là ngoặc đơn đối số thì sẽ ra sao? Cái kết quả đầu ra của đoạn mã đó sẽ là gì? Dựa trên những lý luận triết học mà chúng ta đã cùng nhau đúc kết trong các ví dụ vừa qua, có vẻ như một logic hoàn toàn tự nhiên là cái biểu thức tham chiếu bên trong ngoặc đơn cũng sẽ tống khứ cái giá trị hàm lên ngăn xếp biểu thức, để rồi sau đó bị kích nổ một cách gián tiếp. Đừng vội đắc ý, sự thật không hề đơn giản như vậy! Hệ thống ngữ pháp của ngôn ngữ JavaScript bí mật cất giấu một luật lệ đặc chủng để thao túng cái hình thái triệu hồi này, nó sẽ đối xử với cái cấu trúc đó y hệt như thể nó là một lời gọi hàm thông thường mà không hề có sự tồn tại của cái lớp vỏ bọc ngoặc đơn chết tiệt bao quanh tên định danh. Chắc hẳn bạn đang vò đầu bứt tai tự hỏi tại sao có kẻ lại mang trong mình một khát khao bệnh hoạn muốn sử dụng vũ lực để ép buộc hệ thống phải gán ngữ cảnh mặc định cho từ khóa this thông qua một cái trò triệu hồi hàm gián tiếp rườm rà như vậy?

Kỹ thuật truy cập đối tượng toàn cục

Trước khi chúng ta tiến hành giải mã cái câu hỏi hóc búa đó, hãy cùng nhau chào đón một con đường tà đạo khác để có thể thi triển thuật gán từ khóa this của hàm gián tiếp. Cho đến tận thời điểm này, những khuôn mẫu triệu hồi hàm gián tiếp mà chúng ta vừa vạch trần đều mắc phải một điểm yếu chí mạng là chúng cực kỳ nhạy cảm và dễ bị tổn thương bởi chế độ nghiêm ngặt. Thế nhưng, kịch bản sẽ rẽ nhánh ra sao nếu như chúng ta khát khao một kỹ thuật gán từ khóa this của hàm gián tiếp có khả năng đạp lên dư luận và hoàn toàn không thèm đếm xỉa gì đến cái chế độ nghiêm ngặt kia? Cấu trúc hàm tạo của hàm điện toán có khả năng nuốt chửng một chuỗi ký tự chứa mã nguồn và sau đó tiến hành hô biến để định nghĩa ra một hàm điện toán tương đương ngay tại thời điểm chạy. Đáng sợ hơn, nó luôn luôn thi hành cái thứ ma thuật đó với một thái độ ngông cuồng y hệt như thể cái hàm đó vừa mới được sinh ra từ trong cốt lõi của phạm vi toàn cục vậy. Và để đẩy sự ngông cuồng lên đến đỉnh điểm, nó còn đảm bảo chắc nịch rằng cái hàm đó tuyệt đối không bao giờ bị ép buộc phải chạy trong chế độ nghiêm ngặt, bất chấp cái tình trạng chế độ nghiêm ngặt của toàn bộ chương trình đang bao bọc nó có ra sao đi chăng nữa. Cái kết cục đầu ra đó hoàn toàn đồng nhất với việc bạn xách một cái hàm gián tiếp ra chạy rông ngoài đường vậy.

Một trong những ứng dụng mang tính chất ngách và độc dị bậc nhất của cái kỹ thuật gán từ khóa this của hàm gián tiếp bất chấp chế độ nghiêm ngặt này là để đánh cắp một sợi dây tham chiếu mang độ tin cậy tuyệt đối trỏ thẳng về cái đối tượng toàn cục đích thực, và cái chiêu trò này cực kỳ thịnh hành trong cái kỷ nguyên tăm tối trước khi bản đặc tả kỹ thuật của ngôn ngữ JavaScript rủ lòng thương và chính thức khai sinh ra cái tên định danh đối tượng toàn cục globalThis. Trên thực tế, một kết quả đầu ra mang đậm sự tương đồng, thông qua việc lạm dụng cái thủ thuật toán tử dấu phẩy kết hợp với hàm đánh giá mã nguồn động. Lời gọi hàm đánh giá mã nguồn động truyền chuỗi ký tự chứa từ khóa this thông thường sẽ bị chế độ nghiêm ngặt trói chặt tay chân, thế nhưng khi nó bị kẹp vào giữa cái cấu trúc toán tử dấu phẩy, nó lại trở thành một con thú thoát cương và do đó nó cung cấp cho chúng ta một con đường cực kỳ đáng tin cậy để moi ra được cái đối tượng toàn cục bên trong bất kỳ một hệ thống chương trình nào.

Đáng buồn thay, cả hai cái phương pháp tiếp cận thông qua hàm tạo và toán tử dấu phẩy kết hợp đánh giá mã nguồn động đều bị mắc nghẹn bởi một rào cản kỹ thuật mang tính chất sống còn: toàn bộ mớ mã nguồn đó sẽ bị hệ thống an ninh của các đoạn mã JavaScript chạy trên trình duyệt web chặn đứng một cách không thương tiếc nếu như cái ứng dụng đó bị trói buộc bởi những điều khoản hạn chế của Chính sách bảo mật nội dung, một thứ chính sách thi hành lệnh cấm tuyệt đối đối với việc đánh giá mã nguồn động nhằm mục đích phòng chống các thảm họa an ninh mạng. Liệu chúng ta có thể giở trò luồn lách để qua mặt cái rào cản chết người này không? Câu trả lời là có, trong đại đa số các kịch bản. Bản đặc tả kỹ thuật tối cao của ngôn ngữ JavaScript ban hành một đạo luật rằng một hàm lấy giá trị khi được định nghĩa chễm chệ trên cơ thể của đối tượng toàn cục, hoặc trên bất kỳ một đối tượng nào nằm ở tuyến dưới và nhận thừa kế từ nó, sẽ bị hệ thống ép buộc phải chạy với cái ngữ cảnh từ khóa this bị cắm chặt vào đối tượng toàn cục, và nó hoàn toàn phớt lờ mọi cái chế độ nghiêm ngặt của chương trình. Chà, đó quả thực là một cái mánh khóe bẩn thỉu và cực kỳ nhức nách. Thế nhưng, đó chính là cái bản chất ma quỷ của từ khóa this trong ngôn ngữ JavaScript dành tặng cho bạn đấy!

Đánh giá hàm mẫu nội suy

Vẫn còn sót lại một biến thể triệu hồi hàm mang đậm tính chất ngoại lai cuối cùng mà chúng ta bắt buộc phải đưa lên thớt mổ xẻ để hoàn thiện bức tranh toàn cảnh: các hàm được gắn nhãn cho chuỗi mẫu. Chuỗi mẫu – thứ mà tôi có xu hướng ưu ái gọi bằng một cái tên mang tính học thuật hơn là các giá trị nguyên bản được nội suy – hoàn toàn có khả năng bị gắn nhãn bằng một hàm tiền tố đứng trước, và cái hàm này sẽ bị cỗ máy đánh thức và kích nổ cùng với toàn bộ nội tạng đã được phân tích cú pháp của cái chuỗi mẫu nguyên bản đó. Như bạn có thể dễ dàng soi xét thông qua cú pháp, sự vắng mặt hoàn toàn của cấu trúc ngoặc đơn triệu hồi là một điểm nhấn dị biệt, thay vào đó chỉ có duy nhất cái hàm gắn nhãn xuất hiện lù lù ngay trước cái chuỗi mẫu nguyên bản; việc chèn thêm khoảng trắng giữa hai thực thể này mặc dù được luật pháp ngôn ngữ cho phép nhưng lại là một hành vi cực kỳ dị hợm và hiếm khi xuất hiện trong thế giới thực.

Bất chấp cái ngoại hình kỳ quái và đe dọa đó, thì cái hàm gắn nhãn rốt cuộc vẫn sẽ bị hệ thống kích nổ không thương tiếc. Nó sẽ bị nhét vào bụng một danh sách chứa đựng một hoặc hàng tá những giá trị chuỗi nguyên bản vốn dĩ đã được hệ thống hì hục phân tích cú pháp và bóc tách ra khỏi cái chuỗi mẫu nguyên bản ban đầu, song hành cùng với toàn bộ những giá trị biểu thức nội suy nào xui xẻo bị cỗ máy bắt gặp trên đường phân tích. Chúng ta sẽ không đi sâu vào việc mổ xẻ và lột trần toàn bộ những bí mật thâm cung bí sử của các hàm chuỗi mẫu được gắn nhãn – mặc dù khách quan mà nói thì chúng đích thị là một trong những tính năng mang tính chất cách mạng, quyền lực và kích thích trí tò mò bậc nhất từng được giới thiệu vào hệ sinh thái ngôn ngữ JavaScript – thế nhưng bởi vì cái trọng tâm học thuật mà chúng ta đang cày xới ở đây là việc gán từ khóa this trong các lần triệu hồi hàm, nên để đảm bảo tính toàn vẹn và độ phủ sóng của kiến thức, chúng ta bắt buộc phải đem cái chủ đề về cách thức mà từ khóa this sẽ được phân bổ ra làm mồi nhậu. Hình thái thứ hai của các hàm gắn nhãn mà bạn hoàn toàn có nguy cơ bị chạm trán trong tự nhiên là lời gọi thông qua một đối tượng.

Dưới đây là một lời giải thích gọn gàng và dễ tiêu hóa nhất cho cái vấn đề đau đầu này: lời gọi hàm gắn nhãn trơ trọi và lời gọi hàm gắn nhãn thông qua đối tượng sẽ lần lượt thể hiện những hành vi gán từ khóa this tương ứng và đồng nhất hoàn toàn với các vị trí triệu hồi của một lời gọi hàm trơ trọi và một lời gọi hàm phương thức. Diễn giải một cách hàn lâm hơn, lời gọi hàm gắn nhãn trơ trọi sẽ ngoan ngoãn cúi đầu tuân phục theo cái đạo luật gán ngữ cảnh mặc định, và lời gọi hàm gắn nhãn thông qua đối tượng sẽ bị cai trị bởi cái quy luật gán ngữ cảnh ngầm định. Thật may mắn cho những bộ não đang quá tải của chúng ta, chúng ta hoàn toàn có thể quăng cái nỗi lo âu về các quy tắc gán của từ khóa tạo mới new hay gán tường minh thông qua tiện ích vào sọt rác, bởi vì những hình thái đó đã bị hệ thống cấm tiệt và hoàn toàn bất khả thi khi kết hợp với các hàm gắn nhãn. Cần phải thẳng thắn vạch trần một sự thật rằng, cái kịch bản mà một hàm giá trị nguyên bản mẫu được gắn nhãn lại được tác giả cất công định nghĩa dưới thân phận là một thực thể có nhận thức về từ khóa this là một điều cực kỳ, cực kỳ hiếm hoi trong thế giới thực, vì vậy cái xác suất mà bạn phải thực sự nai lưng ra để áp dụng mớ quy tắc hỗn độn này là cực kỳ thấp. Thế nhưng, phòng bệnh hơn chữa bệnh, giờ đây bạn đã được vũ trang đầy đủ kiến thức và nằm trong nhóm những kẻ am hiểu tận tường về nó.

Kết luận

Vậy là chúng ta đã kết thúc chặng đường mổ xẻ từ khóa this. Tôi sẵn sàng cá cược rằng đối với phần đông độc giả, cái chủ đề này đã chứng minh bản thân nó phức tạp và… nói sao cho lịch sự nhỉ, xoắn não hơn rất nhiều so với những gì mà các bạn từng mường tượng trước đây. Tin tốt lành, có lẽ vậy, là trong môi trường chiến đấu thực tế ngoài kia, bạn sẽ cực kỳ hiếm khi bị vấp ngã bởi toàn bộ mớ bòng bong phức tạp đan xen này cùng một lúc. Thế nhưng, có một chân lý không thể lảng tránh: bạn càng lạm dụng từ khóa this bao nhiêu, thì nó càng đưa ra một yêu cầu khắt khe bấy nhiêu, đòi hỏi không chỉ bản thân bạn, mà còn cả những thế hệ lập trình viên đi sau sẽ kế thừa đoạn mã của bạn, bắt buộc phải thấu hiểu một cách trọn vẹn và sâu sắc về cơ chế vận hành chính xác ở tầng vi mạch của nó. Bài học đắt giá nhất được đúc kết từ chương này là bạn phải luôn luôn nuôi dưỡng một sự chủ động và một nhận thức cực kỳ toàn diện về mọi ngóc ngách của từ khóa this trước khi bạn cả gan rải nó rải rác khắp nơi trên mã nguồn của mình. Hãy liên tục tự vấn và đảm bảo một cách chắc chắn rằng bạn đang thao túng nó với một hiệu suất tối đa và đang bòn rút được toàn bộ những tinh hoa sức mạnh từ cái trụ cột kiến trúc mang tính chất sống còn này của ngôn ngữ JavaScript.

Đọ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.4 934 – 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.4.

Chuyên mục triet-ly-lap-trinh

Chuyên mục giai-ma-ban-chat-coi-loi-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ẻ