Trước hết, hãy nhớ rằng JavaScript chủ yếu là ngôn ngữ nguyên mẫu , thay vì ngôn ngữ dựa trên lớp 1 . Foo
không phải là một lớp, nó là một hàm, là một đối tượng. Bạn có thể khởi tạo một đối tượng từ hàm đó bằng cách sử dụng new
từ khóa cho phép bạn tạo một cái gì đó tương tự như một lớp trong ngôn ngữ OOP tiêu chuẩn.
Tôi khuyên bạn nên bỏ qua __proto__
hầu hết thời gian vì nó có hỗ trợ trình duyệt chéo kém, và thay vào đó tập trung vào tìm hiểu về cách thức prototype
hoạt động.
Nếu bạn có một thể hiện của một đối tượng được tạo từ hàm 2 và bạn truy cập một trong các thành viên của nó (phương thức, thuộc tính, thuộc tính, hằng số, v.v.), truy cập sẽ chảy xuống cấu trúc phân cấp nguyên mẫu cho đến khi (a) tìm thấy thành viên, hoặc (b) không tìm thấy nguyên mẫu khác.
Hệ thống phân cấp bắt đầu trên đối tượng được gọi, và sau đó tìm kiếm đối tượng nguyên mẫu của nó. Nếu đối tượng nguyên mẫu có một nguyên mẫu, nó lặp lại, nếu không có nguyên mẫu nào tồn tại, undefined
được trả về.
Ví dụ:
foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"
foo = {};
console.log(foo.bar); // logs undefined
function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set
Có vẻ như tôi ít nhất đã hiểu được những phần "cơ bản" này, nhưng tôi cần phải làm cho chúng rõ ràng chỉ để chắc chắn.
Trong JavaScript, mọi thứ đều là đối tượng 3 .
tất cả mọi thứ là một đối tượng.
function Foo(){}
không chỉ định nghĩa một hàm mới, nó định nghĩa một đối tượng hàm mới có thể được truy cập bằng cách sử dụng Foo
.
Đây là lý do tại sao bạn có thể truy cập Foo
nguyên mẫu của Foo.prototype
.
Những gì bạn cũng có thể làm là thiết lập nhiều chức năng hơn trên Foo
:
Foo.talk = function () {
alert('hello world!');
};
Chức năng mới này có thể được truy cập bằng cách sử dụng:
Foo.talk();
Tôi hy vọng bây giờ bạn nhận thấy sự tương đồng giữa các hàm trên một đối tượng hàm và một phương thức tĩnh.
Hãy nghĩ về f = new Foo();
việc tạo một thể hiện của lớp, Foo.prototype.bar = function(){...}
như định nghĩa một phương thức chia sẻ cho lớp và Foo.baz = function(){...}
như định nghĩa một phương thức tĩnh công khai cho lớp.
ECMAScript 2015 đã giới thiệu nhiều loại đường cú pháp cho các loại khai báo này để làm cho chúng đơn giản hơn để thực hiện trong khi cũng dễ đọc hơn. Ví dụ trước có thể được viết là:
class Foo {
bar() {...}
static baz() {...}
}
cho phép bar
được gọi là:
const f = new Foo()
f.bar()
và baz
được gọi là:
Foo.baz()
1: class
là "Từ dành riêng trong tương lai" trong đặc tả ECMAScript 5 , nhưng ES6 giới thiệu khả năng xác định các lớp bằng class
từ khóa.
2: về cơ bản là một thể hiện lớp được tạo bởi một nhà xây dựng, nhưng có nhiều khác biệt về sắc thái mà tôi không muốn đánh lừa bạn
3: các giá trị nguyên thủyundefined
, bao gồm , null
booleans, số và chuỗi, không phải là đối tượng kỹ thuật vì chúng là các triển khai ngôn ngữ cấp thấp. Booleans, số và chuỗi vẫn tương tác với chuỗi nguyên mẫu như thể chúng là các đối tượng, vì vậy với mục đích của câu trả lời này, việc xem chúng là "đối tượng" dễ dàng hơn mặc dù chúng không hoàn toàn.
Foo.talk = function ...