Trong JavaScript, hàm có thể có thuộc tính. Tất cả các hàm đều có một thuộc tính đặc biệt gọi là prototype
.
function doSomething() {}
console.log(doSomething.prototype);
// Hàm trong JavaScript luôn có thuộc tính prototype mặc định
// Ngoại lệ: hàm mũi tên không có thuộc tính prototype mặc định
const doSomethingFromArrowFunction = () => {};
console.log(doSomethingFromArrowFunction.prototype);
doSomething()
có thuộc tính prototype
mặc định. Sau khi chạy đoạn mã trên, console sẽ hiển thị một đối tượng tương tự như sau:
{ constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Lưu ý: Chrome console sử dụng [[Prototype]]
để biểu thị prototype của đối tượng, theo thuật ngữ của đặc tả; Firefox sử dụng .prototype
. Để thống nhất, chúng ta sẽ sử dụng [[Prototype]]
.
Chúng ta có thể thêm thuộc tính vào prototype của doSomething()
:
function doSomething() {}
doSomething.prototype.foo = "bar";
console.log(doSomething.prototype);
Kết quả:
{ foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } }
Sử dụng toán tử new
để tạo một instance của doSomething()
dựa trên prototype này. Gọi hàm với toán tử new
sẽ trả về một đối tượng là instance của hàm. Các thuộc tính sau đó có thể được thêm vào đối tượng này.
function doSomething() {}
doSomething.prototype.foo = "bar"; // thêm thuộc tính vào prototype
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // thêm thuộc tính vào đối tượng
console.log(doSomeInstancing);
Kết quả:
{ prop: "some value", [[Prototype]]: { foo: "bar", constructor: ƒ doSomething(), [[Prototype]]: { constructor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() } } }
[[Prototype]]
của doSomeInstancing
là doSomething.prototype
. Khi truy cập một thuộc tính của doSomeInstancing
, runtime đầu tiên sẽ kiểm tra xem doSomeInstancing
có thuộc tính đó hay không.
Nếu không, runtime sẽ tìm kiếm thuộc tính trong doSomeInstancing.[[Prototype]]
(tức là doSomething.prototype
). Nếu doSomeInstancing.[[Prototype]]
có thuộc tính đang tìm, thì thuộc tính đó sẽ được sử dụng.
Nếu không, doSomeInstancing.[[Prototype]].[[Prototype]]
sẽ được kiểm tra. Theo mặc định, [[Prototype]]
của thuộc tính prototype
của bất kỳ hàm nào là Object.prototype
. Vậy nên, doSomeInstancing.[[Prototype]].[[Prototype]]
(tức là doSomething.prototype.[[Prototype]]
hoặc Object.prototype
) sẽ được tìm kiếm. Quá trình này gọi là prototype chain.
Nếu thuộc tính không được tìm thấy trong doSomeInstancing.[[Prototype]].[[Prototype]]
, thì doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
sẽ được tìm kiếm. Tuy nhiên, doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]
không tồn tại, vì Object.prototype.[[Prototype]]
là null
. Sau khi toàn bộ chuỗi prototype được tìm kiếm, runtime sẽ khẳng định rằng thuộc tính không tồn tại và giá trị của thuộc tính là undefined
.
function doSomething() {}
doSomething.prototype.foo = "bar";
const doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: ", doSomeInstancing.prop);
console.log("doSomeInstancing.foo: ", doSomeInstancing.foo);
console.log("doSomething.prop: ", doSomething.prop);
console.log("doSomething.foo: ", doSomething.foo);
console.log("doSomething.prototype.prop:", doSomething.prototype.prop);
console.log("doSomething.prototype.foo: ", doSomething.prototype.foo);
Kết quả:
doSomeInstancing.prop: some value
doSomeInstancing.foo: bar
doSomething.prop: undefined
doSomething.foo: undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo: bar