在学习函数的时候就很好奇函数作为构造器创建对象内部机制是如何实现的。实例,构造器和原形对象到底是什么关系呢,原形对象怎样去影响实例呢?今天就来分享我对这部分的理解。
对象构造器
对象分为函数对象和普通对象,函数对象只能由js预定义对象function创建,普通对象由函数对象创建,故函数对象又被称为对象构造器。
构造器作为function的实例对象它会有一个__proto__属性,指向它本身的原形对象,也就是function构造器的prototype属性所指向的原形对象。它也会有一个继承自原型对象的prototype属性。prototype属性用作构造器创建实例对象的原型对象。
综上所述构造器的__proto__属性和prototype指向的原形对象并不是同一个原形对象。
用一张图来表示他们的关系如下:
构造器创建对象的内部机制
我们先来看上一篇博客中的一段代码:
function Animal(name, age, color ,legnum ) {
this.name = name;
this.age= age;
this.color = color ;
this.legnum = legnum ;
}
var dog = new Animal("dog",3,"black",4);
var cat = new Animal("cat",1,"red",4);
在这段代码中,dog,cat都是用构造器Animal创建出来的实例。上文已经告诉我们对象实例的原形对象也就是构造器的prototype属性结构与构造器结构没有任何关系,那么dog 和cat 是如何继承到Animal构造器的属性的呢?这需要从构造器创建对象的内部机制说起。
在js内部机制中,用构造器创建一个对象需要经过以下几步,以dog实例为例:
//创建一个空的新对象
var newobj = {}
//将构造器的prototype属性中的属性和方法拷贝到newobj 中,并将newobj 的__proto__属性指向构造器的prototype属性
for(var value in Animal.prototype){
newobj[value]=Animal.prototype[value];
}
newobj.__proto__ = Animal.prototype;
//到这一步newobj 中并没有任何属性,因为我们没有给Animal构造器的prototype属性所指的原形对象添加任何属性
//调用Animal的call方法
Animal.call(newobj ,"dog",3,"black",4);
//执行完这步我们就得到了dog实例,在call调用中,我们执行了构造器函数Animal,也就是往新的对象实例中添加了,name,age,color ,legnum 属性。
这里补充说明一下函数的call方法,函数的call方法作用是将函数中的this替换成call方法的第一个参数,来执行函数。
以上文中的Animal.call(newobj ,“dog”,3,“black”,4);为例,call方法的功能就是将Animal函数中this.name = name; this.age= age; this.color = color ;this.legnum = legnum ;语句中的this替换成newobj 来执行Animal函数。也就是Animal.call(newobj ,“dog”,3,“black”,4);等同于:
function Animal(name, age, color ,legnum ) {
newobj .name = name;
newobj .age= age;
newobj .color = color ;
newobj .legnum = legnum ;
}
Animal("dog",3,"black",4);
综上所述当遇到这样的问题:
function Animal(name, age, color ,legnum ) {
this.name = name;
this.age= age;
this.color = color ;
this.legnum = legnum ;
this.hashair = true;
}
Animal.prototype.hashair = false;
var dog = new Animal("dog",3,"black",4);
var doghashair = dog.hashair
问doghashair 的值时,我们就可以很清晰地理解doghashair 的值为true。因为虽然dog 的属性拷贝自Animal.prototype,但是最后执行Animal的call方法时属性值会被重置。
获取属性值的内部机制
js获取一个对象的属性值的内部机制总结起来就是先找对象实例本身,如果找不到就去其原型对象找,再找不到就去原型对象的原型对象找直至原型对象为空,这查找的过程就形成了原型链。以上文中的dog实例为例,用图表示如下:
注意:dog.__proto__和Animal.prototype指向的是同一个原型对象,Animal.prototype.__proto__和Animal.__proto__指向的不是同一个原型对象。
由上文获取对象实例的属性实现过程,我们就可以很容易理解原型对象添加属性可以影响实例对象,但是删除属性却没有影响,更改属性值影响部分自身没有同名属性的实例。