先使用构造函数创建一个对象:
1function Person() {}
2let person = new Person()
3person.name = 'Janny'
在上述例子中,我们使用构造函数 Person
,通过 new
创建了一个实例对象 person
。
原型 prototype
上的方法是给实例对象使用的。
每个函数(只有函数才会有)都有一个 prototype
属性,其指向一个对象,这个对象就是该构造函数创建的实例对象的原型(通过 p.__proto__
指向,后续会详细讲解)。
1function Person() {}
2
3Person.prototype.name = 'Janny'
4let person1 = new Person()
5let person2 = new Person()
6console.log(person1.name)
7console.log(person2.name)
8
9
10Person.prototype === person1.__proto__
11Person.prototype === person2.__proto__
prototype
指向的通常是一个 Object
空对象,我们可以手动为其添加属性和方法,例如:
1Person.prototype.name = 'Danny'
2Person.prototype.doing = function() {}
构造函数的 prototype
属性和实例对象的原型的关系为:
prototype
构造函数 Person
实例对象person 的原型
每个 JavaScript 对象(除了 null)都具有 __proto__
属性,这个属性会指向该对象的原型。
1function Person() {}
2
3
4let person = new Person()
5
6
7person.__proto__ === Person.prototype
关系如下:
prototype
通过 new 创建了
_proto_
构造函数 Person
实例对象person 的原型
实例对象person
实例对象的原型上有一个 constructor
属性,它指向关联的构造函数本身。
1function Person() {}
2
3Person.prototype.constructor === Person
4
5let person = new Person()
6
7person.__proto__.constructor = Person
8
9
10Object.getPrototypeOf(person) === Person.prototype
关系如下:
prototype
constructor
通过 new 创建了
_proto_
构造函数 Person
实例对象person 的原型
实例对象person
每个构造函数都有一个 prototype
,即显式原型(属性) 每个实例对象都有一个 __proto__
,可称为隐式原型(属性) 显式原型属性与隐式原型属性都是一个地址值,二者均指向了同一个对象,就是上面说的那个实例对象的原型。
1function Person() {}
2console.log(Person.prototype)
3
4let person = new Person()
5console.log(person.__proto__)
6
7console.log(Person.prototype === person.__proto__)
原型属性都是 JS 引擎自动添加的
显式原型的创建过程:
- 当构造函数创建的时候会自动添加一个
prototype
属性 - 在内存中创建一个空对象(实例对象的原型)
- 让
prototype
属性指向这个(原型)空对象
隐式原型的创建过程:
- 当通过构造函数创建实例对象的时候自动添加了一个
__proto__
属性 - 将
prototype
中存的地址值赋值给__proto__
,此时二者指向了同一个对象
prototype
_proto_
构造函数 Person
一个JS对象
该对象为实例的原型
实例对象person
1function Person() {
2 this.doing = function() {
3 console.log('方法doing')
4 }
5}
6Person.prototype.todo = function() {
7 console.log('方法todo')
8}
9
10let person = new Person()
11person.ownFun = function() {
12 console.log('自身方法ownFun')
13}
14
15person.ownFun()
16
17
18
19person.doing()
20person.todo()
21
22
23
24person.toString()
25
26
27person.done()
原型链访问:原型链是沿着隐式原型来查找的 原型链作用:查找对象的属性(方法)
访问一个对象属性的时候:
- 先在自身属性中查找,找到了就返回
- 如果没有找到,再沿着
__proto__
隐式原型链向上查找,找到了就返回 - 如果最终没有找到,返回
undefined
_proto_
prototype
_proto_
_proto_
实例对象person
方法ownFun
实例对象的原型
方法todo 方法doing
Object构造函数
JS中所有对象的原型
Object相关的实例方法
原型链的尽头 null
所有对象都有一个 __proto__
隐式原型属性,指向了构造函数 Function
的 prototype
显式原型属性,即==实例对象的隐式原型等于其构造函数的显式原型==。
由于 JavaScript 中一切皆对象,所以 Function
既是构造函数又是实例对象,也就是说,Function 也是通过 new Function()
创建而来的。
所有构造函数(构造函数也是对象)的 __proto__
属性都是一样的,因为所有构造函数都是 Funciton
通过 new 创建而来的,所以都指向了 Function
的显式原型 prototype
属性。
1function Person() {}
2function Car() {}
3
4Person.__proto__ === Car.__proto__
5
6Person.__proto__ === Function.prototype
7Car.__proto__ === Function.prototype
因为构造函数 Object
是 Function
通过 new Function()
创建而来的,所以 Object
相当于实例对象,Function
是该实例对象的构造函数。
所以:
1Object.__proto__ === Function.prototype
这和原型链的尽头是不一样的
1Object.prototype.__proto__ === null
构造函数的显示原型 prototype
指向的对象默认是空的 Object的实例对象
,但是构造函数 Object
的显示原型 prototype
并不满足这一点,通过下述代码对比可以发现:普通构造函数的 prototype
属性指向的对象和 Object
的 prototype
属性指向的对象是不一样的。
1function Person() {}
2
3Person.prototype instanceof Object
4
5null instanceof Object
6
7Object.prototype instanceof Object
所有的函数都是 Function
的实例(包括 Function
自身)
1Function instanceof Function
所有的函数都是 Function
的实例,而 Function
的实例的原型都是构造函数 Object
的实例。
1
2Object instanceof Object
下面四条语句均是正确的,通过上面的原型链示意图就可以很轻松地理解了。
1Function instanceof Function
2Function instanceof Object
3Object instanceof Object
4Object instanceof Function
instanceof
是如何判断的?
- 表达式:
obj instanceof Fun
其中obj
是实例对象,Fun
是构造函数 - 如果
Fun
构造函数的显式原型对象在obj
实例对象的隐式原型链上,返回true
,否则返回false
即Fun
只走到Fun.prototype
就不走了,而obj
要沿着隐式原型链obj.__proto__.__proto__......
一直走下去,直到终点。
实例对象的隐式原型属性对象等于构造函数的显式原型属性对象。
Function
是通过 new
自身产生的实例。
1function Person(){}
2let person = new Person()
3
4
5person instanceof Person
6
7
8person instanceof Object
1Object instanceof Function
2
3
4
5Object instanceof Object
6
7
8
9Function instanceof Function
10
11
12
13Function instanceof Object
14
15
16function Foo(){}
17Object instanceof Foo
例题一
1function A(){}
2A.prototype.n = 1
3let b = new A()
4
5A.prototype = {
6 n:2,
7 m:3
8}
9
10let c = new A()
11
12b.n
13b.m
14c.n
15c.m
实例一旦创建,其原型就已经确定了,后续再修改原型不会影响到已经创建的实例。
因为当创建 b 之后,b.__proto__
已经指向了 A.prototype
这个实例对象,即此时这个实例对象有两个指针指向着,分别是 b.__proto__
和 A.prototype
,后续进行了 A.prototype={n:2,m:3}
操作,让 A.prototype
指向了一个新的实例对象。但是这个操作并没有修改原来的实例对象,此时原来的实例对象还有一个指针指着呢,那就是 b.__proto__
。所以实例对象 b 的原型还是原来的。 而新创建的实例对象 c ,其在创建前原型已经进行了修改,故创建 c 的时候,c.__proto__
指向的是修改后的原型对象。
例题二
1let Foo = function(){}
2Object.prototype.a = function(){
3 console.log('obj_a()')
4}
5Function.prototype.b = function(){
6 console.log('fun_b()')
7}
8let f = new Foo()
9
10f.a()
11f.b()
12
13Foo.a()
14Foo.b()