JavaScript的原型和原型链
原型
全貌
User
构造函数实例化了一个User
实例,所以User
构造函数的prototype
原型(对象)==
User
实例的__proto__
隐式原型,它们构成了这样的三角关系。User
构造函数本质上是new Function()
而来的一个实例,所以User
构造函数、Function
构造函数和Function
原型(对象)之间也有这样的三角关系。Function
原型(对象)也是个对象呀,所以是new Object()
而来,那么Function
原型(对象)是Object
构造函数的一个实例,则Function
原型(对象)、Object
构造函数和Object
原型(对象)也构成了这样的三角关系。特殊的点:
Object
原型(对象)的隐式原型是null
,也就是说Object.prototype.__proto__
是null
Object
构造函数本质上也是new Function()
而来的一个实例,那么,其原型对象就是Function
原型(对象),即Object.prototype == Function.prototype
- 在JS中,万物皆对象,
Function
构造函数本质上也是对象,但它比较特殊,它的隐式原型是它的原型(对象),也就是Function.__proto__ == Function.prototype
构造函数
构造函数和普通函数本质上没什么区别,只不过使用了
new
关键字创建对象的函数,被叫做了构造函数。构造函数的首字母一般是大写,用以区分普通函数,当然不大写也不会有什么错误。等价于类class
1 | function Person(name, age) { |
1 | class Person { |
以上两种写法都可以用来创建对象,效果等价。
使用new
操作符调用构造函数,做了如下几步操作:
- 创建一个新的对象;
- 将新对象的隐式原型(
_proto_
)指向构造函数的原型(对象)(prototype
); - 将构造函数的
this
指向新对象; - 执行构造函数的代码;
- 如果构造函数返回非空对象,则返回此对象,否则,则返回新建的对象;
构造函数和普通函数在本质上并没有区别,构造函数首字母大写只是一个约定成俗的规范而已。
使用 new
操作符调用普通函数也可以创建实例,所以,正确的说,使用 new
操作符调用函数,可使该函数成为构造函数。直接调用 person
函数的话,由于是全局环境下调用,所以 person
的 this
绑定的是 window
。
原型(对象)(prototype
)
在js中,每一个函数类型的数据(构造函数) ,都有一个叫做
prototype
的属性,这个属性指向的是一个对象,就是所谓的原型(对象),它是一个对象。对于原型(对象) 来说,它有个constructor
属性,指向它的构造函数。
二者互相指
原型(对象)最主要的作用就是用来存放实例对象的 公有属性 和 公有方法。
把这些公有的属性和方法放在原型对象里共享,避免重复创建相同的属性和方法造成浪费:
1 | function Person(name, age) { |
获取对象的(隐式)原型
- Object 对象的一个方法
isPrototypeOf
可以确定对象和(隐式)原型是否有关系,console.log(Person.prototype.isPrototypeOf(person1)); // true
- Object 对象的一个方法
getPrototypeOf
可以获取对象的(隐式)原型,console.log(Object.getPrototypeOf(person1) === Person.prototype); // true
重写对象的(隐式)原型
- Object 对象提供一个方法
setPrototypeOf
重写对象(隐式)原型。
1 | let father = { |
- Object 对象提供方法
create
可以在创建对象的时候指定对象的(隐式)原型。
1 | let father = { |
遮蔽原型
实例不能重写原型的属性,但是实例可以定义一个属性来遮蔽原型上的同名属性。
Object 的原型提供了一个方法
hasOwnPrototype
用于判断属性是在(隐式)原型还是实例上,在实例上,返回true
,在原型上,返回false
。
1 | let Person = function () {}; |
隐式原型(__proto___
)
隐式原型是利用
__proto__
属性查找原型,这个属性 指向当前对象的构造函数的原型对象,这个属性是对象类型数据的属性,所以 实例对象可以使用
1 | console.log(per1.__proto__ === Person.prototype); // true |
形象的比喻
- 构造函数$\longrightarrow$雕刻师,其
prototype
指向原型(对象) - 原型(对象) $\longrightarrow$设计图/设计原型。它也有
__proto__
$\longrightarrow$次稿设计图/设计原型,初稿设计图/设计原型。 最最早的初稿是一张白纸,也就是说Object.prototype.__proto__
是null
- 通过构造函数
new
出来的实例对象$\longrightarrow$一个个雕塑作品,其__proto__
指向指向原型(对象)
原型链
简介
原型链的核心就是依赖对象的
__proto__
的指向,当自身不存在的属性时,就一层层的扒出创建对象的构造函数,直至到Object时,就没有__proto__
指向了。
不仅实例有一个__proto__
属性,原型对象也有一个__proto__
属性指向它的原型,从中继承方法和属性,一层一层,以此类推,直到Object.prototype = null
,s通俗来讲:原型链就是多个对象通过__proto__
的方式连接了起来。
instanceof
instanceof
就是基于 原型链 判断变量的类型
1 | function myInstanceof(left, right) { |
总结
Object
是所有对象的爸爸或者祖先,所有对象都可以通过_proto_找到它;Function
是所有函数的爸爸,所有的函数都可以通过_proto_找到它;- 函数的
prototype
是一个对象,叫原型(对象),包含一些属性和方法; - 对象的隐式原型
__proto__
属性指向其构造函数的原型(对象),__proto__
将多个对象和隐式原型连接起来,叫做原型链;