js-面向对象
先上一张图,可以对面向对象有一个大致的了解,然而什么是面向对象呢,用java中的一句经典语句来说就是:万事万物皆对象。面向对象的思想主要是以对象为主,将一个问题抽象出具体的对象,并且将抽象出来的对象和对象的属性和方法封装成一个类.
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
封装
面向对象有三大特性,封装、继承和多态。对于ES5来说,没有class的概念,并且由于js的函数级作用域(在函数内部的变量在函数外访问不到),所以我们就可以模拟 class的概念,在es5中,类其实就是保存了一个函数的变量,这个函数有自己的属性和方法。将属性和方法组成一个类的过程就是封装。
封装:把客观事物封装成抽象的类,隐藏属性和方法的实现细节,仅对外公开接口。
那么,如果我们要把”属性”(property)和”方法”(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?
一、 生成实例对象的原始模式
假定我们把猫看成一个对象,它有”名字”和”颜色”两个属性。
1 | var Cat = { |
现在,我们需要根据这个原型对象的规格(schema),生成两个实例对象。
1 | var cat1 = {}; // 创建一个空对象 |
好了,这就是最简单的封装了,把两个属性封装在一个对象里面。但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。
二、 原始模式的改进
我们可以写一个函数,解决代码重复的问题。
1 | function Cat(name,color) { |
然后生成实例对象,就等于是在调用函数:1
2 var cat1 = Cat("大毛","黄色");
var cat2 = Cat("二毛","黑色");
这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。
三、 构造函数模式
为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。用来在创建对象时初始化对象。
构造函数其实就是普通的函数,只不过有以下的特点
1.首字母大写(建议构造函数首字母大写,即使用大驼峰命名,非构造函数首字母小写)
2.内部使用this
3.使用 new生成实例
通过构造函数添加属性和方法实际上也就是通过this添加的属性和方法。因为this总是指向当前对象的,所以通过this添加的属性和方法只在当前对象上添加,是该对象自身拥有的。所以我们实例化一个新对象的时候,this指向的属性和方法都会得到相应的创建,也就是会在内存中复制一份,这样就造成了内存的浪费。
1 | fuction Cat (name,color) { |
生成实例:
1 | var cat1 = new Cat('tom','orange'); |
通过this定义的属性和方法,我们实例化对象的时候都会重新复制一份.
在类上通过 this的方式添加属性和对象会导致内存浪费的问题,我们就考虑,有什么方法可以让实例化的类所使用的方法直接使用指针指向同一个方法。于是,就想到了原型的方式
通过原型prototype
js规定,每一个构造函数都有一个prototype属性,指向另一个对象(这个对象就是我们俗称的原型对象).这个对象的所有属性和方法,都会被构造函数的实例继承.
也就是说,对于那些不变的属性方法,我们可以直接将其添加在类的prototype对象上.
1 | function Cat (name,color) { |
然后同样生成实例
1 | var cat1 = new Cat('大宝','黑白色'); |
这时所有实例的type
属性和eat()
方法,其实都是同一个内存地址,指向prototype
对象,因此就提高了运行效率。
在类的外部通过.语法添加
我们还可以在类的外部通过. 语法进行添加,因为在实例化对象的时候,并不会执行到在类外部通过. 语法添加的属性,所以实例化之后的对象是不能访问到. 语法所添加的对象和属性的,只能通过该类访问。
三者的区别
通过构造函数、原型和. 语法三者都可以在类上添加属性和方法。但是三者是有一定的区别的。
1.构造函数:通过this添加的属性和方法总是指向当前对象的,所以在实例化的时候,通过this添加的属性和方法都会在内存中复制一份,这样就会造成内存的浪费。但是这样创建的好处是即使改变了某一个对象的属性或方法,不会影响其他的对象(因为每一个对象都是复制的一份)。
2.原型:通过原型继承的方法并不是自身的,我们要在原型链上一层一层的查找,这样创建的好处是只在内存中创建一次,实例化的对象都会指向这个prototype 对象,但是这样做也有弊端,因为实例化的对象的原型都是指向同一内存地址,改动其中的一个对象的属性可能会影响到其他的对象
3..
语法:在类的外部通过. 语法创建的属性和方法只会创建一次,但是这样创建的实例化的对象是访问不到的,只能通过类的自身访问.
javascript也有private public protected
对于java
或者Oc
程序员来说private
public
protected
这三个关键字应该是很熟悉的哈,但是在js中,并没有类似于private
public
protected
这样的关键字,但是我们又希望我们定义的属性和方法有一定的访问限制,于是我们就可以模拟private
public
protected
这些访问权限。
public:public表明该数据成员、成员函数是对所有用户开放的,所有用户都可以直接进行调用
private:private表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。
protected:protected对于子女、朋友来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。
new的实质
虽然很多人都已经了解了new的实质,那么我还是要再说一下new 的实质var o = new Object()
- 新建一个对象o
o. __proto__ = Object.prototype
将新创建的对象的__proto__
属性指向构造函数的prototype- 将this指向新创建的对象
- 返回新对象,但是这里需要看构造函数有没有返回值,如果构造函数的返回值为基本数据类型
string
,boolean
,number
,null
,undefined
,那么就返回新对象,如果构造函数的返回值为对象类型,那么就返回这个对象类型
举个栗子
1 | var Book = function (id, name, price) { |
继承
篇幅稍长,飞机票
多态
JS的函数重载
这个是多态的基础,在之前的Javascript入门已经说过了,JS函数不支持多态,但是事实上JS函数是无态的,支持任意长度,类型的参数列表。如果同时定义了多个同名函数,则以最后一个函数为准。
在函数里根据传参的不同,经过函数体里面的条件语句之后,最后返回的结果不同.