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


87

Tôi đã nhìn thấy một sơ khai trống trên MDN một thời gian cho Reflectđối tượng trong javascript nhưng tôi không thể tìm thấy bất kỳ thứ gì trên Google. Hôm nay tôi đã tìm thấy http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object này và nó có vẻ giống với đối tượng Proxy ngoại trừ chức năng cảnh giới và trình tải.

Về cơ bản, tôi không biết liệu trang này tôi tìm thấy chỉ giải thích cách triển khai Reflect hay tôi không thể hiểu từ ngữ của nó. Ai đó có thể vui lòng giải thích cho tôi nói chung những gì các phương pháp Reflectlàm?

Ví dụ: trên trang mà tôi tìm thấy nói rằng việc gọi Reflect.apply ( target, thisArgument, argumentsList ) sẽ "Trả về kết quả của việc gọi phương thức nội bộ [[Call]] của đích với các đối số thisArgument và args." nhưng điều đó có khác gì so với chỉ gọi target.apply(thisArgument, argumentsList)không?

Cập nhật:

Cảm ơn @Blue, tôi đã tìm thấy trang này trên wiki http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect mà theo hiểu biết của tôi thì đối tượng phản chiếu cung cấp các phiên bản phương thức của tất cả các hành động có thể bị chặn bởi proxy để chuyển tiếp dễ dàng hơn. Nhưng điều đó có vẻ hơi kỳ lạ với tôi vì tôi không thấy nó hoàn toàn cần thiết như thế nào. Nhưng nó có vẻ làm được nhiều hơn thế một chút, đặc biệt là mệnh giá nói double-liftingnhưng điều đó chỉ đến thông số proxy cũ /


1
Thông số cho biết "Đối tượng Reflect là một đối tượng bình thường duy nhất.", Theo hiểu biết của tôi Reflectchỉ là một vật chứa RealmLoadercác đối tượng, nhưng tôi cũng không biết cái sau làm gì.
simonzack

Cảm ơn :), tôi có vẻ như từ trang mà tôi đã liên kết đến (không biết nó hợp pháp đến mức nào) rằng mỗi Vương quốc là "ngữ cảnh của tập lệnh java" của riêng nó và trình tải nạp các Vương quốc như mô-đun hoặc thứ gì đó, dựa trên sự tương đồng giữa các phản và proxy và thực tế là loại proxy "quá tải" được tích hợp sẵn trong chức năng có thể Reflect.LoaderReflect.Realmliên quan đến chức năng quá tải của mô-đun?
Jim Jones

1
Trông giống như đó là một 'lớp tĩnh' (như JSON) với các phương pháp tĩnh: isExtensible, ownKeysvv ES 6, với các lớp học thực tế, điều này rất hữu ích để tìm hiểu thêm về một lớp học ( targettrong 16.1.2 Tôi nghĩ).
Rudie

Câu trả lời:


129

CẬP NHẬT 2015: Như đã chỉ ra trong câu trả lời của 7th , bây giờ ES6 (ECMAScript 2015) đã được hoàn thiện, tài liệu thích hợp hơn hiện đã có:


Câu trả lời gốc (để hiểu (lịch sử) và các ví dụ bổ sung) :

Các Reflection proposaldường như đã tiến triển đến Dự thảo ECMAScript 6 Đặc điểm kỹ thuật . Tài liệu này hiện phác thảo các Reflectphương thức của -object và chỉ nêu những điều sau về Reflect-object chính nó:

Đối tượng Reflect là một đối tượng bình thường duy nhất.

Giá trị của khe bên trong [[Prototype]] của đối tượng Reflect là đối tượng nguyên mẫu Object được tích hợp sẵn tiêu chuẩn (19.1.3).

Đối tượng Reflect không phải là một đối tượng chức năng. Nó không có phương thức bên trong [[Construct]]; không thể sử dụng đối tượng Reflect làm hàm tạo với toán tử mới . Đối tượng Reflect cũng không có phương thức bên trong [[Gọi]]; không thể gọi đối tượng Reflect dưới dạng một hàm.

Tuy nhiên, có một lời giải thích ngắn gọn về mục đích của nó trong ES Harmony :

Mô-đun “@reflect” phục vụ nhiều mục đích:
  • Bây giờ chúng ta đã có các mô-đun, mô-đun “@reflect” là một nơi tự nhiên hơn cho nhiều phương thức phản chiếu được định nghĩa trước đó trên Đối tượng. Đối với mục đích tương thích ngược, không có khả năng các phương thức tĩnh trên Đối tượng sẽ biến mất. Tuy nhiên, các phương thức mới có thể sẽ được thêm vào mô-đun “@reflect” hơn là vào phương thức khởi tạo Đối tượng.
  • Ngôi nhà tự nhiên dành cho proxy, tránh sự cần thiết phải ràng buộc Proxy toàn cầu.
  • Hầu hết các phương pháp trong mô-đun này ánh xạ 1-1 vào các bẫy Proxy. Trình xử lý proxy cần các phương pháp này để chuyển tiếp các thao tác một cách thuận tiện, như được hiển thị bên dưới.



Vì vậy, Reflectđối tượng cung cấp một số hàm tiện ích, nhiều hàm có vẻ trùng lặp với các phương thức ES5 được định nghĩa trên Đối tượng toàn cục.

Tuy nhiên, điều đó không thực sự giải thích những vấn đề hiện có mà điều này dự định giải quyết hoặc chức năng nào được thêm vào. Tôi nghi ngờ điều này có thể được lung linh và thực sự, sự hài hòa-đặc điểm kỹ thuật ở trên liên kết với một 'triển khai gần đúng, không quy chuẩn của các phương pháp này' .

Việc kiểm tra mã đó có thể cung cấp (thêm) ý tưởng về việc sử dụng nó, nhưng may mắn thay, cũng có một wiki nêu ra một số lý do tại sao đối tượng Reflect lại hữu ích :
(Tôi đã sao chép (và định dạng) văn bản sau để tham khảo trong tương lai từ đó nguồn vì chúng là những ví dụ duy nhất mà tôi có thể tìm thấy. Ngoài ra, chúng có ý nghĩa, đã có lời giải thích tốt và chạm vào applyví dụ của câu hỏi .)


Giá trị trả lại hữu ích hơn

Nhiều hoạt động trong Reflecttương tự như các hoạt động ES5 được định nghĩa trên Object, chẳng hạn như Reflect.getOwnPropertyDescriptorReflect.defineProperty. Tuy nhiên, ngược lại Object.defineProperty(obj, name, desc)sẽ trả về objkhi thuộc tính được xác định thành công, hoặc ném ra một TypeErrorcách khác, Reflect.defineProperty(obj, name, desc)được chỉ định đơn giản là trả về một boolean cho biết thuộc tính có được xác định thành công hay không. Điều này cho phép bạn cấu trúc lại mã này:

try {
  Object.defineProperty(obj, name, desc);
  // property defined successfully
} catch (e) {
  // possible failure (and might accidentally catch the wrong exception)
}

Về điều này:

if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
  // failure
}

Các phương thức khác trả về trạng thái thành công boolean như vậy là Reflect.set(để cập nhật một thuộc tính), Reflect.deleteProperty(để xóa một thuộc tính), Reflect.preventExtensions(để làm cho một đối tượng không thể mở rộng được) và Reflect.setPrototypeOf(để cập nhật liên kết nguyên mẫu của một đối tượng).


Hoạt động hạng nhất

Trong ES5, cách để phát hiện một đối tượng có objđịnh nghĩa hay kế thừa một tên thuộc tính nhất định hay không là viết (name in obj). Tương tự, để xóa một thuộc tính, người ta sử dụng delete obj[name]. Mặc dù cú pháp dành riêng rất hay và ngắn gọn, nhưng điều đó cũng có nghĩa là bạn phải gói rõ ràng các hoạt động này trong các hàm khi bạn muốn chuyển hoạt động xung quanh dưới dạng giá trị hạng nhất.

Với Reflect, các phép toán này dễ dàng được định nghĩa là các hàm hạng nhất:
Reflect.has(obj, name)là hàm tương đương với (name in obj)Reflect.deleteProperty(obj, name)là một hàm hoạt động giống nhưdelete obj[name].


Ứng dụng chức năng đáng tin cậy hơn

Trong ES5, khi người ta muốn gọi một hàm fvới một số lượng biến đối số được đóng gói dưới dạng một mảng argsvà liên kết thisgiá trị với obj, người ta có thể viết:

f.apply(obj, args)

Tuy nhiên, fcó thể là một đối tượng cố ý hoặc vô ý xác định applyphương thức riêng của nó . Khi bạn thực sự muốn đảm bảo rằng applyhàm tích hợp được gọi, người ta thường viết:

Function.prototype.apply.call(f, obj, args)

Không chỉ dài dòng, nó nhanh chóng trở nên khó hiểu. Với Reflect, giờ đây bạn có thể thực hiện một lệnh gọi hàm đáng tin cậy theo cách ngắn gọn và dễ hiểu hơn:

Reflect.apply(f, obj, args)


Các hàm tạo đối số biến

Hãy tưởng tượng bạn muốn gọi một hàm khởi tạo với một số đối số thay đổi. Trong ES6, nhờ cú pháp lây lan mới, có thể viết mã như:

var obj = new F(...args)

Trong ES5, điều này khó viết hơn, bởi vì người ta chỉ có thể sử dụng F.applyhoặc F.callgọi một hàm với số lượng đối số thay đổi, nhưng không có F.constructhàm nào đối newvới hàm có số đối số thay đổi. Với Reflect, bây giờ người ta có thể viết, trong ES5:

var obj = Reflect.construct(F, args)


Hành vi chuyển tiếp mặc định cho bẫy Proxy

Khi sử dụng Proxycác đối tượng để bọc các đối tượng hiện có, việc chặn một thao tác, thực hiện một cái gì đó và sau đó là "làm điều mặc định", thường là áp dụng thao tác bị chặn cho đối tượng được bọc. Ví dụ: giả sử tôi chỉ muốn ghi lại tất cả các quyền truy cập thuộc tính vào một đối tượng obj:

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    // now do the default thing
  }
});

Các ReflectProxyAPI được thiết kế song song , chẳng hạn rằng đối với mỗi Proxycái bẫy, có tồn tại một phương pháp tương ứng trên Reflectrằng "làm điều mặc định". Do đó, bất cứ khi nào bạn thấy mình muốn "làm điều mặc định" bên trong trình xử lý Proxy, điều chính xác cần làm là luôn gọi phương thức tương ứng trong Reflectđối tượng:

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    return Reflect.get(target, name);
  }
});

Kiểu trả về của các Reflectphương thức được đảm bảo tương thích với kiểu trả về của Proxybẫy.


Kiểm soát ràng buộc này của những người truy cập

Trong ES5, khá dễ dàng để thực hiện truy cập thuộc tính chung hoặc cập nhật thuộc tính. Ví dụ:

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property update

Các Reflect.getReflect.setphương thức cho phép bạn làm điều tương tự, nhưng cũng chấp nhận như một đối số tùy chọn cuối cùng, một receivertham số cho phép bạn đặt this-binding một cách rõ ràng khi thuộc tính mà bạn lấy / đặt là một trình truy cập:

var name = ... // get property name as a string
Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
Reflect.set(obj, name, value, wrapper)

Điều này đôi khi hữu ích khi bạn đang gói objvà bạn muốn mọi tự gửi trong trình truy cập được định tuyến lại đến trình bao bọc của bạn, ví dụ: nếu objđược định nghĩa là:

var obj = {
  get foo() { return this.bar(); },
  bar: function() { ... }
}

Cuộc gọi Reflect.get(obj, "foo", wrapper)sẽ khiến this.bar()cuộc gọi được định tuyến lại wrapper.


Tránh di sản __proto__

Trên một số trình duyệt, __proto__được định nghĩa là một thuộc tính đặc biệt cho phép truy cập vào nguyên mẫu của một đối tượng. ES5 đã chuẩn hóa một phương pháp mới Object.getPrototypeOf(obj)để truy vấn nguyên mẫu. Reflect.getPrototypeOf(obj)làm chính xác như vậy, ngoại trừ điều đó Reflectcũng xác định một tương ứng Reflect.setPrototypeOf(obj, newProto)để thiết lập nguyên mẫu của đối tượng. Đây là cách mới tuân thủ ES6 để cập nhật nguyên mẫu của đối tượng.
Lưu ý rằng: setPrototypeOf cũng tồn tại trênObject (như được chỉ một cách chính xác hiện bởi KNU 's bình luận )!


CHỈNH SỬA:
Ghi chú bên lề (giải quyết các nhận xét cho câu hỏi Q): Có một câu trả lời ngắn gọn và đơn giản về 'Q: Mô-đun ES6 so với Nhập khẩu HTML' giải thích RealmsLoaderđối tượng.

Một lời giải thích khác được cung cấp bởi liên kết này :

Một đối tượng cảnh giới trừu tượng hóa khái niệm về một môi trường toàn cầu riêng biệt, với đối tượng toàn cục của riêng nó, bản sao của thư viện chuẩn và "bản chất" (các đối tượng chuẩn không bị ràng buộc với các biến toàn cục, như giá trị ban đầu của Object.prototype).

Web có thể mở rộng : Đây là tương đương động của cùng một nguồn gốc <iframe>không có DOM.

Đáng nói hơn là: tất cả điều này vẫn còn trong bản nháp, đây không phải là một thông số kỹ thuật được khắc trên đá! Đó là ES6, vì vậy hãy ghi nhớ khả năng tương thích của trình duyệt!

Hi vọng điêu nay co ich!


@Spencer Killen: Chà .. câu trả lời này có hướng suy nghĩ của bạn đi đúng hướng và giải thích điều này liên quan đến sự khác biệt giữa Reflect.applytarget.apply? Hoặc tôi nên thêm gì trước khi tiền thưởng kết thúc?
GitaarLAB

2
setPrototypeOf cũng tồn tại trên Object.
Knu

1
Lưu ý rằng việc sử dụng Reflect.getlàm triển khai mặc định cho proxy get không hoạt động tốt nếu bạn đang ủy quyền một đối tượng có thuộc tính nguyên mẫu. Nó chỉ phàn nàn rằng nó không hoạt động. Tuy nhiên, nếu thay vào đó bạn sử dụng Reflect.get(target, property)mà không chuyển qua receiver, thì nó vẫn hoạt động.
CMCDragonkai

Các thử nghiệm của tôi trong đó bạn luôn truy cập các thuộc tính thông qua proxy, dẫn đến tình huống trong đó targetmục tiêu ban đầu là proxy kết thúc, trong khi đó receiverlà chính proxy. Nhưng một lần nữa, điều này có thể khác nếu bạn quản lý để truy cập các thuộc tính theo cách khác.
CMCDragonkai

5

Đi theo tài liệu nháp tìm thấy trên wiki,

http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts

Chúng tôi nhận được dòng về "vật thể thông thường duy nhất" mà nó làm rõ trong bản nháp. Nó cũng có các định nghĩa chức năng.

Wiki phải đáng tin cậy vì bạn có thể tìm thấy liên kết đến nó từ trang web emcascript

http://www.ecmascript.org/dev.php

Mặc dù vậy, tôi đã tìm thấy liên kết đầu tiên bằng google và không may mắn tìm thấy nó bằng cách tìm kiếm trực tiếp trên wiki.


Cảm ơn, các bước được thực hiện khi mỗi phương thức được gọi được liệt kê trong thông số kỹ thuật chính thức có vẻ khá giống với những phương thức trong liên kết của tôi nhưng tôi vẫn không thể tìm ra những gì mỗi phương thức làm khi nó được gọi, chẳng hạn như trong Reflect. Áp dụng các bước được liệt kê 4. Thực hiện thao tác tóm tắt Chuẩn bị cho cuộc gọi. 5. Trả về kết quả của việc gọi phương thức bên trong [[Call]] của target với các đối số thisArgument và args ------- điều đó có nghĩa là gì?
Jim Jones

Dự đoán tốt nhất của tôi khi nhìn vào nó là nó đang tham chiếu đến đệ quy đuôi ở bước 4 và bước 5 là tham chiếu đến hàm nguyên mẫu. Có vẻ như ý tưởng chung là xác minh phương thức áp dụng có thể chạy trên những gì nó được áp dụng cho (bước 1-2), xử lý lỗi (bước 3) và sau đó gọi hàm áp dụng đang được chạy đối với (bước 4-5). Dự đoán tốt nhất của tôi khi quét qua tài liệu là điểm của mô-đun Reflect là khi chức năng của bạn đang thực hiện đòi hỏi một số hình thức xem xét nội tâm đối tượng. Việc sử dụng lời gọi cũng có thể là lý do tại sao nó "không có phương thức bên trong [[Gọi]]".
Blue
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.