先使用构造函数创建一个对象:

 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()  

原型链访问:原型链是沿着隐式原型来查找的 原型链作用:查找对象的属性(方法)

访问一个对象属性的时候:

  1. 先在自身属性中查找,找到了就返回
  2. 如果没有找到,再沿着 __proto__ ​隐式原型链向上查找,找到了就返回
  3. 如果最终没有找到,返回 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()  

个人笔记记录 2021 ~ 2025