原型与原型链
原型与原型链
原型:被用于复制现有实例来生成新实例的函数
原型链:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。
构造函数:用new来调用,就是为了创建一个自定义类,构造函数和普通函数本质上没什么区别,只不过使用了new关键字创建对象的函数,被叫做了构造函数。
实例:是类在实例化之后一个一个具体的对象
原型
JS中每一个函数都有一个prototype属性,这个属性指向函数的原型对象,每一个由原型对象派生的子对象,都有相同的属性。子对象就叫构造函数,从实例原型中获取相同的属性。
我们所创建的每一格函数,解析器都会向函数中添加一个属性prototype
这个属性对应着一个对象,这个对象就是我们所谓的原型对象
打比方说:
1 | function Person(age) { |
其中,函数的prototype指向了一个对象,而这个对象正是调用构造函数时创建的实例的原型,也就是person1和person2的原型。
_Proto _
_Proto _是每一个子对象(除null外)都会有的一个属性,指向该对象的原型
当函数以构造函数的形式调用时,它所创造的对象中都会有一个隐含的属性指向该构造函数的原型对象,我们可以通过_proto_来访问属性
1 | function Person() { |
此属性可以访问原型,但并不存在于Person.prototype中,来自于Object.prototype,类似getter/setter,即使用obj._proto_时,可以理解为返回Object.getPrototypeOf(obj)
构造函数constructor
每个原型都有一个constructor属性,指向该关联的构造函数
如果函数作为普通函数调用prototype没有任何作用
1 | function Person() { |
1 | function Person() { |
当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:person.constructor === Person.prototype.constructor
实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层(也就是Object)为止。
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中
1 | function Person() { |
在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。
但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.proto ,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。
原型链
原型对象也是对象,所以它也有原型,
当我们使用一个对象的属性或方法时,会按照以下步骤:
- 先在自身中寻找
- 自身中如果有,则直接使用
- 如果自身中没有则去原型对象中寻找,如果原型对象中有,则使用
- 如果原型对象中没有,则去原型对象的原型对象中寻找,直到找到Object对象的原型
- Object有原型,但Object的原型没有原型,如果在Object的原型里没有找到,则返回undefined
整个查找过程都是顺着__proto__属性,一步一步往上查找,形成了像链条一样的结构,这个结构,就是原型链。所以,原型链也叫作隐式原型链。
检查属性函数
- 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
1
console.log("name" in person) //true
- 使用对象的hasOwnProperty()来检查对象自身中是否含有该属性,只有当对象自身中含有属性时,才会返回true
1
console.log("person.hasOwnProperty()") //false