什么是面向对象:先将事物的属性和功能,集中存储在对象结构中,再按需调用对象中的功能,访问对象中的数据,这种编程方法,就叫面向对象编程
面向对象三大特点:封装、继承、多态
1 封装:创建一个对象,并在对象中集中保存一个事物的属性和功能。
创建对象的方法: 3种:
(1). 用{}创建一个对象:
a. var 对象名={ 属性名:属性值, ... : ... , 方法名: function(){ ... this.属性名 ... } }
对象的方法中想使用当前对象内的属性,也不能直接使用。需要用this: this是每个函数中都自动包含的,自动获得正在调用当前函数的.前的对象的 关键词
(2). 用new创建一个对象:
2步:
1). 用new创建一个空对象: var 对象名=new Object(); //等效于{}
2). 为空对象添加新属性: 不管有没有这个新属性,都给这个新属性强行赋值:
对象名.新属性名=值
或对象名.新方法名=function(){ ... }
揭示了整个js的本质: js中所有对象底层都是关联数组
(3). 用构造函数来创建对象:
构造函数:专门描述一类对象的统一结构的函数。构造函数还可以和new配合来创建一个新对象,并添加规定的属性和值。
如何使用构造函数: 2步:
1). 定义一个构造函数来描述一类对象的统一属性结构
//构造函数也是函数
//但是,因为构造函数描述的是一类对象的统一结构,所以函数名应该是这类对象的统一的类型名。比如: 如果一个构造函数专门描述所有学生对象的统一结构,则构造函数名应该命名为"Student"。再比如,如果一个构造函数专门描述所有商品对象的统一结构,则构造函数名应该命名为"Product"。
//外部的值 属性值1 属性值2
↓ ↓
function 类型名( 形参1, 形参2 ){
//固定套路:
//所有属性都要用this.属性名=xxx的方式定义
//问题: 将来每个对象的每个属性值都是无法提前预知的,所以不能将属性值写死在构造函数里。
//解决: 只要函数中包含不确定的因素,一律定义为形参!将来谁调用这个函数,谁就要按要求传入实参值!
this.属性名=形参1;
this.属性名=形参2;
... = ...;
//所有的方法也要用this.方法名=function(){ ... }方式来定义
this.方法名=function(){
... this.属性名...
}
}
2). 用new来调用构造函数反复创建多个相同结构的新对象
var 对象名=new 构造函数名/类型名( 实参值1, 实参值2,... )
new具体做了几件事:
① new创建了一个空对象;
② 自动设置当前新创建的空对象继承构造函数的原型对象;
③ 调用构造函数,并将构造函数中的this统一指向正在创建的这个新对象。(让实参值强行添加到新对象中的对应的属性上);
④ 返回新对象的地址保存到变量中
2 继承
继承:父对象中的成员,子对象无需重复创建,就可直接使用
父对象就是原型对象: 在一个类型中,为所有子对象集中保存所有共有成员的父对象。
原型对象: 构造函数中有一个默认自带的属性prototype,指向自己对应的原型对象:构造函数.prototype
如何向原型对象中添加共有的方法: 强行赋值
构造函数.prototype.共有方法=function(){ ... }
原型链:
(1). 什么是原型链: 由多级父对象逐级继承,形成的链式结构
(2). 保存着一个对象可用的所有成员!
(3). 控制着对象使用成员的顺序:
a. 如果自己有这个属性或方法,就优先使用自有的
b. 除非自己没有,才会沿着_ _proto_ _属性指向的父级对象,继续查找可用的属性或方法
c. 如果父对象也没有子对象想要的成员,会继续沿着父对象的_ _proto_ _,继续向更父级对象查找
自定义继承:只修改一个对象的父对象: 修改整个对象的__proto__指向一个新的父对象
子对象._ _proto_ _=新父对象
修改构造函数的原型对象:
构造函数.prototype=新原型对象
3 多态:
只要一个函数,在不同情况下表现出不同的状态,就称为多态
包括两种情况:
(1). 重载(overload): 一个函数,根据传入的实参值不同,选择执行不同的逻辑
(2). 重写(override): 子对象中定义了和父对象中同名的成员,导致使用这个成员时,永远使用子对象的这个成员,而再无可能使用父对象中同名成员。
如:顶级父对象object对象有一个toString方法,但是Array和Data对象都改写了,成为了适合自己用的方法