原型和原型链
原型:每个对象都有原型对象。
原型链:原型对象也可能拥有原型,一层接着一层,并从其中继承方法和属性。
此外:
1⃣️:每个 实例对象
都有 __proto__
来访问原型对象。
2⃣️:每个 构造函数
都有 prototype
来访问原型对象。
3⃣️:并且该 实例对象
的 __proto__
== 该 构造函数
的 prototype
。
4⃣️:查找属性时,实例对象
会依次查找继承的原型对象属性。
5⃣️:大部分 js 对象都是位于原型链顶端的 Object
的实例。
听着比较抽象:
function a(){
}
let b = new a();
这里的 a 就代表 构造函数
。
这里的 b 就代表 实例对象
。
原型对象
let ad = {
name: "hello world"
};
ad.toString();
想象一下,我们并没有为对象 ad 设置 toString() 方法,那么它从哪里获取该方法并调用它的呢?
调用对象 ad 的 __proto__
方法:
console.log(ad.__proto__);
输出:
{
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
valueOf: ƒ valueOf()
__defineGetter__: ƒ __defineGetter__()
__defineSetter__: ƒ __defineSetter__()
__lookupGetter__: ƒ __lookupGetter__()
__lookupSetter__: ƒ __lookupSetter__()
get __proto__: ƒ __proto__()
set __proto__: ƒ __proto__()
}
可以看到对象 ad 的原型对象中包含了 toString() 方法。
首先,浏览器将在当前对象中查找 toString() 方法。如果找不到,它将调用ad .__ proto__
方法来检查是否包含该方法。如果没有,就继续调用ad .__ proto__ .__ proto__
方法搜索,依此类推,直到查询的 .__ proto__
为空为止。
属性查找
创建构造函数 aa,并为其原型对象设置 age=20。
function aa(){
this.name = "anny"
}
aa.prototype.age = 20;
let bb = new aa();
console.log(bb.name);
console.log(bb.age);
输出:
anny
20
首先,浏览器查找实例对象 bb 中是否含有 name 属性,有,则显示出来。接着查找实例对象 bb 中是否含有 age 属性,没有,则调用 bb.__proto__
查找是否含有 age 属性,有,age=20,显示出来。
打印看一下 bb 原型对象的结构(console.log(bb.__proto__)
):
{
age: 20
constructor: ƒ aa()
__proto__: Object
}
属性遮蔽
为构造函数 aa 设置 age = 23,
function aa(){
this.age = 23
}
aa.prototype.age = 20;
let bb = new aa();
console.log(bb.age);
输出:
23
可以发现原型age的值未展示出来,当在自身属性查找到该属性后,就不会再去原型对象中搜索了,这种情况称之为“属性遮蔽”。
prototype 和 proto
实例对象的 __proto__
总是指向构造函数的 prototype
bb.__proto__ == aa.prototype
onsole.log(bb.__proto__ == aa.prototype)
输出:
true
当前使用的是通过实例对象来自动寻找构造函数中不存在且搜索继承原型对象中的属性。
如果是构造函数呢。
构造函数查找原型对象
为构造函数创建 name 属性,原型对象设置属性 age。
function aa(){
this.name = "anny";
}
aa.prototype.age = 20;
console.log(aa.name);
console.log(aa.age);
输出:
aa
undefined
为什么 aa.name 没有输出 “anny”?
直接调用构造函数的 name 属性,会返回 构造函数的 函数名称。
另外:
aa.prototype.constructor.name == aa.name
其中:
aa.prototype.constructor
返回的是该构造函数
ƒ aa(){
this.name = "anny";
}
那如果非要获取 aa 函数中的 name 属性呢:
通过 new 创建函数 aa 的实例对象 bb,直接访问即可,赋值当然也是如此,bb.name = “XXXX” 。
let bb = new aa();
console.log(bb.name);
输出:anny
为什么 aa.age 没有输出 20 呢?
不是说当前对象中没有该 属性,就自动通过 .__proto__
去寻找吗?
实例对象确实会这么做,但构造函数却不会自动调用其 prototype 方法往上层寻找。
如果想要实现自动寻找原型对象中的属性,就通过 new 创建实例对象来实现。
除了使用 实例对象 的 .__proto__
方法,也可以使用 Object.getPrototypeOf() 来查找原型对象。
console.log(Object.getPrototypeOf(bb))
或:
console.log(Object.getPrototypeOf(new aa()))
注意前后的关系,传入的参数为构造函数的实例对象。
总结:
创建构造函数 aa 和实例对象 bb:
function aa(){
this.name = "anny";
}
let bb = new aa();
创建原型对象属性(使用prototype):
aa.prototype.age = 20;
aa.prototype.speak = function(){
alert("hello world");
}
查找原型对象(两种方法指向同一原型对象且相等):
console.log(aa.prototype);
------------or-------------
console.log(bb.__proto__);
查找属性值:
console.log(bb.name);
console.log(bb.age);
bb.speak();
-----------or--------------
console.log(aa.prototype.age)
aa.prototype.speak()