Appearance
要深入了解 JavaScript 对象,必须掌握三个关键属性:prototype
、 __proto__
和 constructor
prototype:
- 是函数的独有属性。
- 每个函数都有一个 prototype 属性,该属性是一个对象,包含由该函数创建的所有实例所共享的属性和方法。
- 该属性在构造函数调用时会作为新对象的原型。
js
function Person(name) {
this.name = name;
}
console.log(Person === Person.prototype.constructor) // true
console.dir(Person)
proto:
- 是每个对象都有的属性,指向创建该对象的构造函数的 prototype 属性。
- 它形成了对象的原型链,使得对象可以从其原型继承属性和方法。
- 当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作,首先检查对象本身是否有对应的属,如果对象没有该属性,则访问对象的
[[Prototype]]
(即__proto__
)所指向的原型对象,继续查找,直到找到相应的属性或到达原型链的顶层(null)。
js
function Person(name) {
this.name = name;
}
const p = new Person
console.log(p)
constructor:
- 是每个对象所具有的属性,指向创建该对象的构造函数。
- 当创建一个对象时,这个属性被自动设置。
- 对象具有
[[Prototype]]
(即__proto__
)属性。当访问一个对象的constructor
属性时,如果对象本身没有constructor
属性,那么会沿着原型链查找,直到找到constructor
属性。
js
function Person() {}
var PersonPrototype = Person.prototype
console.log(PersonPrototype.constructor === Person) // true
var p = new Person()
console.log(p.__proto__.constructor === Person) // true
// true 因为当查找对象上的某个key 的时候本身没有就会顺着 __proto__ 往上查找因此可以直接其实本质p.__proto__.constructor
console.log(p.constructor === Person)
console.log(p.constructor.name === Person.name) // true
三者关系
- 每个函数都有一个
prototype
属性,它包含由该函数创建的所有实例对象共享的属性和方法。 - 每个对象都有一个
__proto__
属性,它指向创建该对象的构造函数的prototype
属性,从而形成对象的原型链。 - 每个对象都有一个
constructor
属性,指向创建该对象的构造函数。 - 因为函数也是对象,所以函数也具有
__proto__
和constructor
属性,并且函数独有prototype
属性(箭头函数没有自己的prototype
属性) - 大部分 函数数据类型 的值都具备
prototype
(原型/显式原型)属性,属性值本身是一个对象「浏览器会默认为其开辟一个堆内存,用来存储实例可调用的公共的属性和方法」,在浏览器默认开辟的这个堆内存中「原型对象」有一个默认的属性constructor
(构造函数/构造器),属性值是当前函数/类本身
js
function Person(name) {
this.name = name;
}
// 函数的 prototype 属性
console.log(Person.prototype); // {constructor: Person}
console.log(Person.prototype.constructor); // [Function: Person]
// 实例的 __proto__ 属性
const alice = new Person("Alice");
console.log(alice.__proto__); // {constructor: Person}
console.log(alice.__proto__ === Person.prototype); // true
// 实例的 constructor 属性
console.log(alice.constructor); // [Function: Person]
console.log(alice.constructor === Person); // true
plaintext
Person (函数)
|
├── prototype(显式原型)
↓
Person.prototype
|
├── constructor -> Points back to Person
↓
实例 (alice)
|
├── __proto__(隐式原型)
↓
Person.prototype
不要去做的事 -- 重写原型对象
每创建一个函数, 就会同时创建它的prototype对象, 这个对象也会自动获取constructor属性,但重新赋值一个对象相当于丢失了指向自身constructor属性,而constructor属性还变成当前赋值对象的
js
function Person() {
}
console.log(Person.prototype)
// 直接赋值一个新的原型对象
Person.prototype = {
message: "Hello Person",
info: { name: "哈哈哈", age: 30 },
running: function() {},
eating: function() {},
// constructor: Person
}
// 非要覆盖 可以这么写
Object.defineProperty(Person.prototype, "constructor", {
enumerable: false,
configurable: true,
writable: true,
value: Person
})
图解 function 和 Object
构造函数 Foo
、实例 f1
以及它们与全局对象 Function
的关系。
js
// 定义构造函数 Foo
function Foo() {}
// 创建实例 f1
var f1 = new Foo();
图解说明
- 每一个构造函数(比如
Foo
)都是Function
的实例。 - 每一个实例对象(比如
f1
)的__proto__
属性指向其构造函数的prototype
属性。 - 每一个构造函数(比如
Foo
)本身也是一个对象,所以它的__proto__
会指向Function.prototype
。 Function
本身也是一个函数,所以Function
的__proto__
是Function.prototype
。Function.prototype
的__proto__
是Object.prototype
,最终所有对象的__proto__
指向null
。
图解
(全局对象) Function
|
├── prototype
|
Function.prototype
|
⬇
+-------------------+
| |
Foo (构造函数) +----------------------+
| | |
├── prototype | |
| | ⬇
Foo.prototype f1 (实例) Function (全局对象)
| |
└── constructor ←-┘
|
|
⬇
f1.__proto__
|
⬇
Foo.prototype
|
⟶ Object.prototype
⟶ null
代码示例
为了演示上述关系,以下是实际的代码示例及相关注释:
javascript
// 定义构造函数 Foo
function Foo() {}
// 创建实例 f1
var f1 = new Foo();
console.log(Foo.__proto__); // Function.prototype
console.log(Foo.prototype); // Foo.prototype 是一个对象,包含 constructor 属性
console.log(Foo.prototype.constructor); // Foo
console.log(f1.__proto__); // Foo.prototype
console.log(f1.constructor); // Foo
console.log(Function.__proto__); // Function.prototype
console.log(Function.prototype.constructor); // Function
// 原型链的终点是 Object.prototype
console.log(Foo.__proto__.__proto__); // Object.prototype
console.log(Function.prototype.__proto__); // Object.prototype
console.log(Foo.prototype.__proto__); // Object.prototype
console.log(Object.prototype.__proto__); // null
详细解释
Foo
是Function
的一个实例:Foo.__proto__ === Function.prototype
(true)- 这是因为
Foo
作为一个构造函数,实际上是由Function
构造出来的。
f1
的原型链:f1.__proto__ === Foo.prototype
(true)- 这是因为
f1
是通过new Foo()
创建的实例,所以它的__proto__
属性指向Foo.prototype
。
构造函数
constructor
:Foo.prototype.constructor === Foo
(true)f1.constructor === Foo
(true)- 这是因为在定义构造函数
Foo
时会自动创建Foo.prototype
对象,其中包含constructor
属性,指向Foo
函数本身。
最终的原型链:
Foo.__proto__
指向Function.prototype
f1.__proto__
指向Foo.prototype
Function.__proto__
指向Function.prototype
Function.prototype.__proto__
最终指向Object.prototype
Object.prototype.__proto__
为null
,表示该原型链的终点。
其他案例
js
function DoSomething(){}
console.log( DoSomething.prototype );
DoSomething.prototype.name = 'wang'
// {
// name: "wang", ------------> name 属性是DoSomething自己的所以不是从他的copy原型来的,因此不再__proto__
// constructor: ƒ DoSomething(),
// __proto__: { -----------------> 这里的原型链指向的是Object,这证明最开始说的'Object'就是所有对象的原型'
// constructor: ƒ Object(),
// hasOwnProperty: ƒ hasOwnProperty(),
// isPrototypeOf: ƒ isPrototypeOf(),
// propertyIsEnumerable: ƒ propertyIsEnumerable(),
// toLocaleString: ƒ toLocaleString(),
// toString: ƒ toString(),
// valueOf: ƒ valueOf()
// }
// }
const doSomething = new DoSomething()
doSomething.age = 17
console.log( doSomething)
// {
// age: 17, ---------》 age 属性是doSomething 的因此不再__proto__
// __proto__: { ---------》doSomething 是从DoSomething克隆来的因此一层原型链指向是DoSomething
// name: "wang",
// constructor: ƒ DoSomething(),
// __proto__: { -----------------》DoSomething 是从Object 来的因此第二层是在Object
// constructor: ƒ Object(),
// hasOwnProperty: ƒ hasOwnProperty(),
// isPrototypeOf: ƒ isPrototypeOf(),
// propertyIsEnumerable: ƒ propertyIsEnumerable(),
// toLocaleString: ƒ toLocaleString(),
// toString: ƒ toString(),
// valueOf: ƒ valueOf()
// }
// }
// }
总结
1. Object 作为一个类(函数),是 Function 类的实例
Object instanceof Function
=>true
Object.__proto__ === Function.prototype
2. Function 作为一个类(函数),是 Function 类的实例
Function instanceof Function
=>true
Function.__proto__ === Function.prototype
解释:只有这样我们才可保证所有函数都可调用 call
、apply
、bind
等方法。
3. 函数也是对象,Function 作为一个普通对象,它是 Object 类的实例
Function instanceof Object
=>true
Function.__proto__.__proto__ === Object.prototype
4. Object 作为一个普通对象,它是 Object 类的实例
Object instanceof Object
=>true
Object.__proto__.__proto__ === Object.prototype
冷门知识
不具备prototype的函数
- 箭头函数
- 基于ES6给对象某个成员赋值函数值的快捷操作