Lấy tên lớp của cá thể lớp ES6


156

Có cách nào 'hài hòa' để lấy tên lớp từ thể hiện của lớp ES6 không? Khác với

someClassInstance.constructor.name

Hiện tại tôi đang tính đến việc thực hiện Traceur. Và có vẻ như Babel có một polyfill Function.nametrong khi Traceur thì không.

Tóm lại: không có cách nào khác trong ES6 / ES2015 / Harmony và ATM không có gì được mong đợi trong ES.Next.

Nó có thể cung cấp các mẫu hữu ích cho các ứng dụng phía máy chủ chưa được tối ưu hóa nhưng không mong muốn trong các ứng dụng dành cho trình duyệt / máy tính để bàn / thiết bị di động.

Babel sử dụngcore-js để polyfill Function.name, nó nên được tải thủ công cho các ứng dụng Traceur và TypeScript khi thích hợp.


2
Tôi đã gặp vấn đề tương tự; đối với Traceur, giải pháp duy nhất là phân tích mã lớp thực tế để trích xuất tên mà tôi không nghĩ là đủ điều kiện là hài hòa. Tôi vừa nuốt viên thuốc vừa chuyển sang Babel; Sự phát triển của Traceur dường như bị đình trệ và nhiều tính năng ES6 được triển khai kém. Như bạn đã đề cập, instance.constructor.nameclass.nametrả lại tên lớp trong ES6 thích hợp.
Andrew Odri

Có vẻ là cách duy nhất.
Frederik Krautwald

Đây có phải là trong tiêu chuẩn ES6?
drudru

12
Điều đáng nói là someClassInstance.constructor.namesẽ bị sai lệch nếu bạn làm xấu mã của mình.
JamesB

stackoverflow.com/questions/2648293/ Mạnh Có thể muốn xem xét điều này, nên hoạt động w / .constructor.
Florrie

Câu trả lời:


204

someClassInstance.constructor.namechính xác là cách chính xác để làm điều này. Transpilers có thể không hỗ trợ điều này, nhưng đó là cách tiêu chuẩn theo thông số kỹ thuật. ( nameThuộc tính của các hàm được khai báo thông qua các sản phẩm ClassDeclaration được đặt trong 14,5.15 , bước 6.)


2
Đó là những gì tôi sợ. Bạn có biết bất kỳ polyfill hợp lý cho điều đó? Tôi đã cố gắng tìm ra cách Babel làm điều đó nhưng có rất ít thành công.
Estus Flask

Tôi thực sự không biết ý của bạn đối với một polyfill cho một tính năng ngôn ngữ (các lớp).
Domenic

Tôi có nghĩa là polyfill cho constructor.name. Có vẻ như Babel đã thực hiện nó, nhưng tôi đã không thành công trong việc hiểu làm thế nào nó làm điều đó.
Bình Estus

2
@estus someClassInstance.constructorlà một Hàm. Tất cả các chức năng có một thuộc nametính được đặt theo tên của nó. Đó là lý do tại sao babel không cần phải làm gì cả. Vui lòng xem developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
mẹo

2
@Esteban Dường như Babel liên tục quảng bá các polyfill core-js (bao gồm một polyfill cho Function.name), đó là lý do tại sao một số bản dựng Babel có thể hoạt động tốt trong tất cả các trình duyệt.
Bình Estus 22/2/2016

52

Như @Domenic nói, hãy sử dụng someClassInstance.constructor.name. @Esteban đề cập đến trong các ý kiến ​​rằng

someClassInstance.constructorlà một chức năng. Tất cả các chức năng có một nametài sản ...

Do đó, để truy cập tên lớp một cách tĩnh, hãy làm như sau (cách này hoạt động với phiên bản Babel BTW của tôi. Theo nhận xét trên @Domenic số dặm của bạn có thể thay đổi).

class SomeClass {
  constructor() {}
}

var someClassInstance = new SomeClass();
someClassInstance.constructor.name;      // === 'SomeClass'
SomeClass.name                           // === 'SomeClass'

Cập nhật

Babel vẫn ổn, nhưng uglify / minifying đã khiến tôi gặp vấn đề. Tôi đang tạo một trò chơi và đang tạo ra một hàm băm tài nguyên Sprite gộp (trong đó khóa là tên hàm). Sau khi thu nhỏ, mọi hàm / lớp được đặt tên t. Điều này giết chết băm. Tôi đang sử dụng Gulptrong dự án này và sau khi đọc các tài liệu gulp-uglify tôi đã phát hiện ra có một tham số để ngăn chặn việc xáo trộn tên biến / tên hàm cục bộ này xảy ra. Vì vậy, trong gulpfile của tôi, tôi đã thay đổi

.pipe($.uglify()) đến .pipe($.uglify({ mangle: false }))

Có một sự đánh đổi giữa hiệu suất và khả năng đọc ở đây. Không xáo trộn các tên sẽ dẫn đến một tệp xây dựng lớn hơn (một chút) (nhiều tài nguyên mạng hơn) và có khả năng thực thi mã chậm hơn (cần dẫn nguồn - có thể là BS). Mặt khác, nếu tôi giữ nguyên như vậy, tôi sẽ phải xác định thủ công getClassNametrên mọi lớp ES6 - ở cấp độ tĩnh và thể hiện. Không, cám ơn!

Cập nhật

Sau khi thảo luận trong các ý kiến, có vẻ như tránh .namequy ước có lợi cho việc xác định các chức năng đó là một mô hình tốt. Nó chỉ mất một vài dòng mã và sẽ cho phép thu nhỏ và tổng quát mã của bạn (nếu được sử dụng trong thư viện). Vì vậy, tôi đoán rằng tôi đã thay đổi ý định và sẽ tự xác định getClassNamecác lớp học của mình. Cảm ơn @estus! . Getter / Setters thường là một ý tưởng tốt so với dù sao truy cập biến trực tiếp, đặc biệt là trong một ứng dụng dựa trên máy khách.

class SomeClass {
  constructor() {}
  static getClassName(){ return 'SomeClass'; }
  getClassName(){ return SomeClass.getClassName(); }
}
var someClassInstance = new SomeClass();
someClassInstance.constructor.getClassName();      // === 'SomeClass' (static fn)
someClassInstance.getClassName();                  // === 'SomeClass' (instance fn)
SomeClass.getClassName()                           // === 'SomeClass' (static fn)

3
Vô hiệu hóa hoàn toàn xáo trộn không phải là một ý tưởng rất tốt bởi vì xáo trộn góp phần giảm thiểu rất nhiều. Việc sử dụng chủ đề trong mã phía máy khách ở vị trí đầu tiên không phải là ý tưởng tốt, nhưng các lớp có thể được bảo vệ khỏi việc xáo trộn với reservedtùy chọn Uglify (có thể lấy danh sách các lớp bằng biểu thức chính quy hoặc bất cứ thứ gì).
Bình Estus

Rất đúng. Có sự đánh đổi khi có kích thước tệp lớn hơn. Có vẻ như đây là cách bạn có thể sử dụng RegEx để ngăn chặn việc xáo trộn chỉ các mục được chọn . Ý bạn là gì "không phải là một ý tưởng hay để sử dụng chủ đề trong mã phía máy khách"? Nó sẽ có một rủi ro bảo mật trong một số tình huống?
James L.

1
Không, chỉ là những gì đã nói. Việc khai thác JS phía máy khách là bình thường, vì vậy người ta đã biết rằng mẫu này sẽ gây ra sự cố cho ứng dụng. Thêm một dòng mã cho mã định danh chuỗi lớp thay vì namemẫu gọn gàng có thể chỉ là một chiến thắng. Điều tương tự có thể được áp dụng cho Node, nhưng ở mức độ thấp hơn (ví dụ: ứng dụng Electron bị che khuất). Theo nguyên tắc thông thường, tôi sẽ dựa vào namemã máy chủ, nhưng không phải trong mã trình duyệt và không phải trong thư viện chung.
Bình Estus

OK, do đó, bạn khuyên bạn nên xác định thủ công 2 hàm getClassName (tĩnh & thể hiện) để giải quyết vấn đề xáo trộn & cho phép thu nhỏ hoàn toàn (không có RegEx gây phiền nhiễu). Điểm đó về thư viện có rất nhiều ý nghĩa. Hãy làm cho nó thêm ý nghĩa hơn. Đối với tôi, dự án của tôi là một ứng dụng Cordova nhỏ tự tạo, vì vậy những thứ đó không thực sự có vấn đề. Bất cứ điều gì ngoài đó tôi có thể thấy những vấn đề này tăng lên. Cảm ơn đã thảo luận! Nếu bạn có thể nghĩ ra bất kỳ cải tiến nào cho bài viết, hãy thoải mái chỉnh sửa nó.
James L.

1
Có, ban đầu tôi đã sử dụng namemã DRYer để trích xuất tên của thực thể sử dụng lớp (dịch vụ, plugin, v.v.) từ tên lớp, nhưng cuối cùng tôi đã thấy rằng nó sao chép nó một cách rõ ràng với prop tĩnh ( id, _name) chỉ là cách tiếp cận vững chắc nhất. Một cách khác là không quan tâm đến tên lớp, sử dụng chính lớp đó (đối tượng hàm) làm định danh cho một thực thể được ánh xạ tới lớp này và importbất cứ khi nào cần (một cách tiếp cận được sử dụng bởi Angular 2 DI).
Bình Estus

8

Lấy tên lớp trực tiếp từ lớp

Các câu trả lời trước đã giải thích rằng nó someClassInstance.constructor.namehoạt động tốt, nhưng nếu bạn cần lập trình chuyển đổi tên lớp thành một chuỗi và không muốn tạo một cá thể chỉ cho điều đó, hãy nhớ rằng:

typeof YourClass === "function"

Và, vì mọi hàm đều có một thuộc nametính, một cách hay khác để có được một chuỗi với tên lớp của bạn là chỉ cần làm:

YourClass.name

Điều gì sau đây là một ví dụ tốt về lý do tại sao điều này là hữu ích.

Đang tải các thành phần web

Như tài liệu MDN dạy chúng tôi, đây là cách bạn tải một thành phần web:

customElements.define("your-component", YourComponent);

Trong trường hợp YourComponentđược một lớp học kéo dài từ HTMLElement. Vì nó được coi là cách thực hành tốt để đặt tên lớp của thành phần của bạn theo chính thẻ thành phần, nên sẽ rất tốt khi viết một hàm trợ giúp mà tất cả các thành phần của bạn có thể sử dụng để tự đăng ký. Vì vậy, đây là chức năng:

function registerComponent(componentClass) {
    const componentName = upperCamelCaseToSnakeCase(componentClass.name);
    customElements.define(componentName, componentClass);
}

Vì vậy, tất cả những gì bạn cần làm là:

registerComponent(YourComponent);

Điều này là tốt vì nó ít bị lỗi hơn là tự viết thẻ thành phần. Để gói nó, đây là upperCamelCaseToSnakeCase()chức năng:

// converts `YourString` into `your-string`
function upperCamelCaseToSnakeCase(value) {
    return value
        // first char to lower case
        .replace(/^([A-Z])/, $1 => $1.toLowerCase())
        // following upper chars get preceded with a dash
        .replace(/([A-Z])/g, $1 => "-" + $1.toLowerCase());
}

Cảm ơn. Ví dụ là phía khách hàng. Như đã được đề cập, có một số vấn đề với việc sử dụng tên hàm trong trình duyệt. Hầu như mọi đoạn mã trình duyệt dự kiến ​​sẽ được thu nhỏ nhưng điều này sẽ làm hỏng mã dựa trên tên hàm.
Bình Estus

Vâng, bạn hoàn toàn đúng. Công cụ khai thác sẽ phải được cấu hình để không chạm vào tên lớp để phương pháp này hoạt động.
Lucio Paiva

1

Đối với dịch chuyển babel (trước khi thu nhỏ)

Nếu bạn đang sử dụng Babel @babel/preset-env, có thể giữ các định nghĩa lớp mà không chuyển đổi chúng thành các hàm (loại bỏ thuộc constructortính)

Bạn có thể bỏ một số khả năng tương thích trình duyệt cũ với cấu hình này trong babel.config / babelrc:

{
  "presets": [
    ["@babel/preset-env", {"targets": {"browsers": ["> 2%"]}}]
  ]
}

Thông tin thêm về targets: https://babeljs.io/docs/en/babel-preset-env#t Target

Đối với việc thu nhỏ babel (sau khi dịch mã)

Có vẻ như không có giải pháp dễ dàng nào ngay bây giờ ... Chúng ta cần xem xét các loại trừ xáo trộn.


Bạn có thể giải thích thêm làm thế nào điều này được cho là để giúp giảm thiểu? class Foo {}sẽ được thu nhỏ thành một cái gì đó giống như class a{}với bất kỳ mục tiêu nào. Sẽ không có Footừ nào trong mã nguồn được rút gọn.
Bình Estus

Thành thật mà nói tôi đã không đào sâu hơn các tài liệu và thực tế nó đã giúp tôi sử dụng cấu hình này ... Tôi đang sử dụng ECSY vào dự án được dịch mã babel và nó yêu cầu thông số này để có được tên lớp hợp lệ: github.com/MozillaReality/ecsy/issues/ 119
Ifnot

Tôi hiểu rồi. Điều này rất cụ thể đối với mã bạn đã xử lý. Ví dụ, tên có thể được bảo tồn cho các mô-đun ES và ES6 vì export class Foo{}không thể bị làm mờ hiệu quả hơn nữa nhưng điều này có thể khác ở những nơi khác, chúng tôi không thể biết chính xác như thế nào mà không có ý tưởng hay xảy ra với các đoạn mã cụ thể trong thời gian xây dựng. Dù sao đi nữa, điều này đã không thay đổi kể từ năm 2015. Luôn luôn có thể đối với một số cấu hình biên dịch và mã. Và tôi muốn nói rằng khả năng này vẫn còn quá mong manh để sử dụng tên lớp cho logic ứng dụng. Tên lớp mà bạn dựa vào có thể trở thành rác sau một thay đổi ngẫu nhiên trong mã nguồn
Estus Flask

1
Ok tôi đã tìm thấy những gì đang xảy ra bằng cách nhìn vào mã. Giải pháp của tôi sửa chữa sự dịch chuyển của các lớp thành các hàm. Vì vậy, nó giúp trước khi giảm thiểu. Nhưng không phải với vấn đề giảm thiểu. Tôi phải tiếp tục đào bởi vì, tôi không thể hiểu làm thế nào tất cả mã của tôi sử dụng constructor.namevẫn hoạt động trong phiên bản rút gọn ... phi logic: /
Ifnot
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.