携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情
前言
大家好呀,我是L同学。在上篇文章面向对象(一)中,我们学习了面向对象的相关知识点,包括什么是面向对象、什么是对象、JavaScript 中的面向对象、构造函数的作用、解析构造函数代码的执行等相关知识点。在本篇文章中,我们将继续学习面向对象的相关知识点,包括构造函数和实例对象的关系、构造函数存在的问题、什么是原型等相关知识点。
构造函数和实例对象的关系
构造函数是根据具体的事物抽象出来的抽象模板,实例对象是根据抽象的构造函数模板得到的具体实例对象。实例对象由构造函数而来,一个构造函数可以生成很多具体的实例对象,而每个实例对象都是独一无二的。每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数。反推出来,每一个对象都有其构造函数。因此,我们可以通过实例对象的 constructor 属性判断实例和构造函数之间的关系。
console.log(p1.constructor === Person) // => true
console.log(p2.constructor === Person) // => true
console.log(p1.constructor === p2.constructor) // => true
构造函数存在的问题
以构造函数为模板,创建对象,对象的属性和方法都可以在构造函数内部定义。那构造函数存在什么问题呢?接下来,我们来看下面的例子。
function Cat(name, color) {
this.name = name;
this.color = color;
this.say = function () {
console.log('hello'+this.name,this.color);
};
}
var cat1 = new Cat('猫', '白色');
var cat2 = new Cat('猫', '黑色');
cat1.say();
cat2.say();
在以上例子中,从表面上看好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象, name 和 say 都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存,如果实例对象很多,会造成极大的内存浪费。那么,能不能将相同的内容,放到公共部分,节约计算机资源呢?
原型
JavaScript 的每个对象都会继承一个父级对象,父级对象称为 原型 (prototype) 对象。原型也是一个对象,原型对象上的所有属性和方法,都能被子对象 (派生对象) 共享,通过构造函数生成实例对象时,会自动为实例对象分配原型对象。 而每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。需要注意的是,null 没有自己的原型对象。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在构造函数的 prototype 属性上,也就是实例对象的原型对象上。
function Cat(color) {
this.color = color;
}
Cat.prototype.name = "猫";
Cat.prototype.sayhello = function(){
console.log('hello'+this.name,this.color);
}
Cat.prototype.saycolor = function (){
console.log('hello'+this.color);
}
var cat1 = new Cat('白色');
var cat2 = new Cat('黑色');
cat1.sayhello();
cat2.saycolor();
这时所有实例对象的 name 属性和 sayhello() 、saycolor 方法,其实都是在同一个内存地址的对象中,也就是构造函数的 prototype 属性上,因此就提高了运行效率节省了内存空间。