听了燕十八老师的《JavaScript高级》课程,看了阮一峰老师的JavaScript面向对象的笔记,再针对自己的一点理解,做了如下这篇笔记!
先有人还是先有人类?
如果从构物主的角度讲,脑子中要先构想人类的形象,然后再造人。先有类,再有对象。在编程语言中java、php、c++等语言,都是遵循这一原则,先把类构建好,再通过类造的对象,所有对象都拥有同一个模板,如果类改变了,所有对象也会跟着改变。
如果从生物演化的角度讲,应该先有个体对象,然后针对一类个体对象总结他们所具有的特征形成了类。类基于演化而来。在编程语言中,JavaScript的对象不依赖于类的存在,这是js第一个灵活的地方。
js中的对象只是一个"属性字典",因此我们可以直接造对象,不要类。
假如我们要创造一个猫的对象,它有“名字”和“颜色”两个属性,则可以直接这样写:
var cat1 = {}; // 创建一个空对象
cat1.name = "大毛"; // 按照原型对象的属性赋值
cat1.color = "黄色";
如果多个“猫”对象都有这两个属性,那么就可以归纳成一个“猫”的类,类的封装有以下几种方式:
1.工厂模式(原始模式)
function Cat(name,color) { return { name:name, color:color } }
然后生成实例对象,就等于是在调用函数:
var cat1 = Cat("大毛","黄色"); var cat2 = Cat("二毛","黑色");
这种方法的问题是,cat1
和cat2
之间没有内在的联系,不能反映出它们是同一个原型对象的实例。即没有解决对象识别的问题(即怎样知道一个对象的类型问题)。
2.构造函数模式
为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。
对构造函数使用new
运算符,就能生成实例,并且this
变量会绑定在实例对象上。
猫的构造函数可以写为:
function Cat(){ this.name=name; this.color=color; }
我们现在就可以生成实例对象了。
var cat1 = new Cat("大毛","黄色"); var cat2 = new Cat("二毛","黑色"); alert(cat1.name); // 大毛 alert(cat1.color); // 黄色
new操作符在这里主要做了一下4件事:
(1).创建一个新对象,该对象为空对象,该对象的constructor指向构造函数。
(2).将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
(3).执行构造函数中的代码(为这个新对象添加属性)。
(4).返回新对象(隐式返回this)。
构造函数方法很好用,但是存在一个浪费内存的问题。
请看,我们现在为Cat
对象添加一个不变的属性type
(种类),再添加一个方法eat
(吃)。那么,原型对象Cat
就变成了下面这样:
function Cat(name,color){ this.name = name; this.color = color; this.type = "猫科动物"; this.eat = function(){alert("吃老鼠");}; }
生成实例:
var cat1 = new Cat("大毛","黄色"); var cat2 = new Cat ("二毛","黑色"); alert(cat1.type); // 猫科动物 cat1.eat(); // 吃老鼠
表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type
属性和eat()
方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。
alert(cat1.eat == cat2.eat); //false
能不能让type
属性和eat()
方法在内存中只生成一次,然后所有实例都指向那个内存地址呢?回答是可以的。
3.原型模式
Javascript规定,每一个构造函数都有一个prototype
属性,指向一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变的属性和方法,直接定义在prototype
对象上。这里的原型对象可以理解为是对多个多个对象特征的提炼形成的一个共有特征对象。
function Cat(name,color){ this.name = name; this.color = color; } Cat.prototype.type = "猫科动物"; Cat.prototype.eat = function(){alert("吃老鼠")};
然后,生成实例。
var cat1 = new Cat("大毛","黄色"); var cat2 = new Cat("二毛","黑色"); alert(cat1.type); // 猫科动物 cat1.eat(); // 吃老鼠
这时所有实例的type
属性和eat()
方法,其实都是同一个内存地址,指向prototype
对象,因此就提高了运行效率。
alert(cat1.eat == cat2.eat); //true