JS构造函数、实例对象、原型对象的关系与原型链的详细解释

一、构造函数

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值。在JS中,构造函数和普通函数的区别主要有两点:

1、构造函数首字母要大写,用于创建某一类对象;

2、构造函数是通过new操作符来创建实例对象的,而普通函数则不需要。

var Angry_IT_Man = function Angry_IT_Man(){
    this.say = function(){console.log('欢迎关注微信公众号:愤怒的it男')};
};

var angry_it_man = new Angry_IT_Man;

在new一个实例对象时会做四件事情:

1、在内存中创建一个新的空对象;

2、让this指向这个新的对象;

3、执行构造函数里面的代码,给这个新对象添加属性和方法;

4、返回这个新对象(所以构造函数里面不需要return)。

二、原型对象

每次通过构造函数new一个实例对象时,都需要开辟一个内存空间来存放同一个方法,浪费内存。

var Angry_IT_Man = function Angry_IT_Man(){
    this.say = function(){console.log('欢迎关注微信公众号:愤怒的it男')};
};

var angry_it_man1 = new Angry_IT_Man;
var angry_it_man2 = new Angry_IT_Man;

如上图,通过构造函数创建angry_it_man1和angry_it_man2实例对象时,是各自开辟不同的内存空间存放say()方法的。如果只开辟一个内存空间存放say()方法,而这个say()方法可以被所有的实例对象共享,则可以大大节省内存空间。

于是,原型对象登场!我们可以把那些不变的方法,直接定义在原型对象上,这样所有实例对象就可以共享这些方法。

JavaScript 规定,每一个构造函数都有一个prototype属性指向原型对象,而每一个原型对象都有一个constructor属性指向构造函数。

因此,对于上述的代码,我们可以改为:

var Angry_IT_Man = function Angry_IT_Man(){

};

Angry_IT_Man.prototype.say = function(){console.log('欢迎关注微信公众号:愤怒的it男')};

var angry_it_man1 = new Angry_IT_Man;
var angry_it_man2 = new Angry_IT_Man;

从这个例子可以看出,通过给Angry_IT_Man.prototype设置了一个函数方法,由Angry_IT_Man创建出来的实例对象(angry_it_man1和angry_it_man2)就继承了这个函数方法。

三、实例对象

JS 在创建实例对象的时候,每个实例对象都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。

var Angry_IT_Man = function Angry_IT_Man(){

};

Angry_IT_Man.prototype.say = function(){console.log('欢迎关注微信公众号:愤怒的it男')};

var angry_it_man = new Angry_IT_Man;

console.log(angry_it_man.__proto__ === Angry_IT_Man.prototype);

实例对象angry_it_man有一个 __proto__属性指向构造函数的原型对象,创建它的构造函数是Angry_IT_Man,构造函数的原型对象是 Angry_IT_Man.prototype ,所以:angry_it_man.proto == Angry_IT_Man.prototype

四、原型链

重温一下构造函数、原型对象、实例对象之间的关系。每个构造函数都有一个原型对象,构造函数有一个属性(prototype)指向原型对象,原型对象有一个属性(constructor)指回构造函数,而被构造函数new出来的实例对象有一个属性(__proto__)指向原型对象。

那如果当原型对象的原型对象是另一个构造函数的原型对象呢?那意味着这个原型对象本身也有一个属性(__proto__)指向另一个原型对象,相应地另一个原型对象也有一个属性(constructor)指向另一个构造函数。这样就在实例对象和原型对象之间构造了一条原型链。原型链是靠__proto__形成的(而非prototype),是JS实现继承的一种模型。

举个例子:定义三个构造函数:Man、IT_Man和Angry_IT_Man。这三个构造函数的原型对象分别定义了一些属性和方法。然后Angry_IT_Man的原型对象的原型对象赋值为IT_Man的原型对象,IT_Man的原型对象的原型对象赋值为Man的原型对象,从而实现一条原型链,Angry_IT_Man继承IT_Man,IT_Man继承Man。

var Man = function Man(){

};

Man.prototype.name = 'Noah';
Man.prototype.age = 24;
Man.prototype.say = function(){console.log('欢迎关注微信公众号:愤怒的it男')};

var IT_Man = function IT_Man(){

};

IT_Man.prototype.company = 'Tencent';
IT_Man.prototype.skill = 'javascript';

var Angry_IT_Man = function Angry_IT_Man(){

};

Angry_IT_Man.prototype.emotion = 'angry';

Angry_IT_Man.prototype.__proto__ = IT_Man.prototype;
IT_Man.prototype.__proto__ = Man.prototype;

var angry_it_man1 = new Angry_IT_Man;
var angry_it_man2 = new Angry_IT_Man;

1、当访问一个实例对象的属性(包括方法)时,首先查找这个实例对象自身有没有该属性;

2、如果没有就查找它的原型,也就是 __proto__指向的原型对象;

3、如果还没有就查找原型对象的原型对象;

4、依此类推一直找到Object为止。

 欢迎关注作者微信公众号【愤怒的it男】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值