JS中的原型与原型链
JS中的原型与原型链
JunsJS 中的原型与原型链
这也是 JS 中的一个重要的概念,我个人一直理解他们的作用是为了更好的复用函数。他包含了__proto__
和 prototype
,这两个东西经常让人搞混,这里来看看。
为什么需要他们
看个例子:
function Person(name, age) { |
这里我们可以看到,通过new
生成的实例,都开了一块新的内存,所以他们的eat
函数是不一样的,这就导致了内存的浪费,所以说我们可以用一个共享的地方,让实例可以去这里找到这个函数,这就是原型链的作用。
原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
function Person(name, age) { |
这里他们的 eat 函数就相同了。
三者 🤓
理解原型对象还有原型链,就需要搞懂这三者之间的关系:prototype
、__proto__
和 constructor
这三者依附在不同的引用对象类型上
- 对象:
__proto__
、constructor
是对象独有的 - 函数:
prototype
是函数独有的,用来存放实例共享的属性和方法- 因为函数也是对象,所以函数也有
__proto__
、constructor
prototype
他是函数独有的,从一个函数指向一个对象,这个对象就是用来存放实例共享的属性和方法的。他的含义就是函数的原型对象,也就是函数创建的实例的原型对象,也叫显示原型对象。
因此可知:
function Fn() {} |
其中 f.__proto__ === Foo.prototype
既然prototype
是存放公用东西的地方,那么其实他就是可以让实例对象来访问公用东西的,通过__proto__
来访问。
任何函数在创建的时候,其实会默认同时创建该函数的 prototype
对象
__proto__
对象都有这个属性,从一个对象指向另一个对象,这个被指向的对象就是他的原型对象,也称为隐式原型对象。
他的作用就是,当我访问一个对象的属性/方法时,如果这个对象里面没有,就可以从他的__proto__
指向的原型对象中寻找。如果还是找不到,就沿着原型对象的__proto__
继续找,知道找到要的东西,或者找到顶层原型对象null
,返回 undefined。
这个查找的过程,从当前对象出发,沿着原型对象构成的链接寻找,就是原型链。
constructor
他也叫构造函数,是对象所独有的,从一个对象指向一个函数,含义是对象的构造函数。
每个对象都有构造函数(本身拥有/继承),这个构造函数就是用来创建这个对象的函数。
其中可以看到Function
对象比较特殊,他的构造函数是自己,因为他可以看成函数/对象,所有的函数/对象最终都是由Function
构造函数得来,所以说constructor
属性的重点就是Function
。
原型对象
在上面我们其实有俩种原型对象,一种是函数的原型对象,一种是对象的原型对象,分别叫做显式原型对象和隐式原型对象。
显式原型对象 | 隐式原型对象 |
---|---|
属性 prototype |
属性 __proto__ |
函数独有 | 对象独有(函数也是对象,所以函数也有) |
定义函数时自动复制,默认为{} |
创建实例对象时自动添加,赋值为构造函数的prototype 值 |
实现原型的继承和属性的共享 | 构成原型链,实现原型的继承 |
原型对象的指向
- 字面量方式
这时候原型就是Object.prototype
const { log } = console |
- 构造器方式
这时候原型就是构造函数的prototype
const { log } = console |
Object.create()
会把传入的对象作为原型
const { log } = console |
看点题目
1
const A = function () {} |
当创建 A 时,已经创建了A.prototype
对象为{ }
,创建 b 的时候,把A.prototype
对象的引用赋值给了b.__proto__
,所以b
的__proto__
指向A.prototype
,所以b
的n
属性就是1
。
然后修改了A.prototype
引用的地址,所以c
的__proto__
指向了新的A.prototype
对象,所以c
的n
属性就是2
,m
属性就是3
。
2
const F = function () {} |
首先我们看f
,他是通过构造函数F
创建的实例,所以他的__proto__
指向F.prototype
,而F.prototype
的__proto__
指向Object.prototype
,所以可以写出这样的原型链f -> F.prototype -> Object.prototype
。他并没有涉及到 Function
,所以第二个 log 报错。
再看F
,他是函数,由Function
构造,所以F.__proto__
指向Function.prototype
,而Function.prototype
的__proto__
指向Object.prototype
。所以可以写出这样的原型链F -> Function.prototype -> Object.prototype
。所以第一个 log 输出a
,第二个 log 输出b
。
3
function Person(name) { |
p.__proto__
等于什么?Person.__proto__
等于什么?
1: p
是 Person
构造出来的,所以p.__proto__
等于Person.prototype
2: Person 是一个函数,所以Person.__proto__
等于Function.prototype
4
const foo = {} |
直接看上文吧,这个简单