要理清楚JS中的原型与原型链的问题,需要我们先了解三个属性
1、__proto__(在ES标准定义中的名字应该是[[prototype]],具体实现是由浏览器代理自己实现);
__proto__,在Javascript中的所有的对象都具有一个__proto__属性,这个属性指向该对象的原型
2、prototype(原型对象,在JS中,每一个函数都有一个属性prototype,我们叫他原型对象
3、constructor(构造器),是原型对象中的一个属性,它指向构造函数
有如下代码:
function Person(){console.log('吼吼吼')};//创建构造函数
const per= new Person();//实例化对象
想要理清楚上面的代码发生了什么,必须要知道new关键字做了什么:首先第一行代码是创建了一个构造函数,关键的第二行代码来了。new一个新对象的过程,发生了什么呢?
(1)创建一个空对象 per
(2)让per对象的 __proto__属性指向构造函数的prototype,也即是-->per.__proto__ = Person.prototype
(3)绑定this,使构造函数的this指向新对象pro
(4)执行代码,返回this,此时的新对象就拥有了构造函数的方法和属性了
除了上面的几点,我们还需要了解:
(1). 新对象都有一个属性(JS中的函数也是一个对象) 叫__proto__
(2). 构造函数都有一个属性 叫prototype
(3). 构造函数的原型对象上有一个属性 叫constructor 指向构造函数
如上两图所示,实例化出来的 pro对象 有一个属性是指向了构造函数的 prototype对象 ,构造函数中有一个属性叫 prototype ,我们把它叫做原型对象,他有一个属性叫做 constructor 指向的是构造函数。通过上面的信息相信对JS中的原型链有了一定的了解了,我们可以把该构造函数的原型对象(prototype)当作是一个公共空间,里面存放在各种方法等 ,原则上来说是只有该构造函数的实例化对象才可以访问的空间。
在JS中,所有的原型对象都是Object函数构造出来的(除Object本身的原型对象之外),意思就是 说原型对象的最终指向都是Object.prototype,Object.prototype也是一个对象,但他的__proto__指向的就是null了,所以也有 JS中一切皆对象而万物初始皆空!让我们根据上面的描述再把原型链图完善一下吧!
JS中 所有的函数都是Function函数对象构造的,Function顶层没有人构造 就是自己构造了自己。
咋就是说根据第一句话,咋们的原型链图又可以再完善一下了哟。
咋们在做关于原型链的练习时要明白,在查找某个方法或变量时都是应该先在自己本身对象上查找,如果没找到就去构造函数原型对象prototype身上去查找。再没有找到就去Object原型对象prototype身上去查找,如果再没有,则会报错。
举个🌰
<script>
function Fun() {
}
Fun.prototype.num = 100;//为构造函数的原型对象中添加num=100
const n = new Fun();//通过new关键字实例化对象,让对象的__proto__指向构造函数中的原型对象
console.log(n.num);//在自身找不到属性num,就去原型链上找,找到num=100;
</script>
再来一个
function A() {
//this.v = 10;
}
function B(v) {
this.v = v;
}
function C(v) {
if (v) {
this.v = v;
}
}
A.prototype.v = 1;
B.prototype.v = 1;
C.prototype.v = 1;
console.log(new A().v);
console.log(new B().v);
console.log(new C(2).v);
首先这道题的输出是1,undefined,2
执行代码new A().v 会先去查找A的构造函数里边有没有 v 变量,看上面的代码可知找不到变量v,找不到就会沿着原型链往上找,找到了构造函数的原型对象上有一个变量v=1,输出1。如果把注释的那行代码取消注释则可以找到v=10。
执行代码new B().v ,在自己身上找到了v,发现构造函数里面有值,就会直接返回this.v的值,同时因为没有给v赋值,所以返回undefined。
执行代码new C().v ,在自己身上找到了v,发现构造函数里面有值,就会直接返回this.v的值,并且有赋值,输出结果为2