一、构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值。在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男】