什么是面向对象: 程序中都是用对象结构描述现实中一个具体事物
什么是对象: 程序中专门描述现实中一个具体事物的程序结构
为什么: 现实中,一个数据,必须属于某个具体对象中,才有意义。
何时: 今后,只要描述一个事物,都要将事物的属性和功能集中定义在一个对象中
如何: 面向对象三大特点:
封装,继承,多态
封装:
什么是: 创建一个对象,存储一个事物的属性和功能
其中: 事物的属性,会成为对象的属性
属性其实就是存在对象中的变量
事物的功能,会成为对象的方法
方法其实就是存在对象中的函数
功能和属性,统称为成员
为什么: 便于大量维护数据
何时: 只要使用面向对象的方式编程,都必须先将事物的属性和功能封装在对象中,再按需使用对象的属性和方法。
如何: 3种:
- 直接量:
var obj={
属性名:值,
... : ... ,
方法名(){
... this.属性名 ...
}
}
问题: 对象自己的方法,要使用自己的属性值,缺找不到!
原因: 不加.访问的变量,只能在作用域链中查找,无法自动进入对象中查找
解决: 错误: 在属性前加"对象."
在对象内,禁止写死对象名
因为对象名其实仅是一个普通变量名,很有可能发生变化
正确: 用this.属性名:
this: 在函数执行时,自动创建的一个关键词
自动指向正在调用当前函数的.前的对象
为什么: 不受对象名的影响
总结: 今后,只要对象自己的方法,想访问自己的属性,必须用this.属性名
var lilei=/*new */Object();
lilei.sname="Li Lei";
lilei.age=11;
lilei.intr=function(){
console.log(
"I'm "+this.sname
+", I'm "+this.age
);
}
console.dir(lilei);//显示一个对象的存储结构
console.log(lilei.sname);
console.log(lilei.age);
lilei.intr();//this->lilei
lilei.age++;
console.log(lilei.age);
lilei.intr();
lilei.sname="Li Xiaolei";
lilei.intr();
- 用new: 2步:
- 先创建一个空对象
var obj=new Object(); //new可省略,()也可省略,但不能同时省 - 向空对象中添加新成员:
obj.属性名=值;
obj.方法名=function(){ ... this.属性名 ... }
说明: js中的对象,可随时,通过强行赋值的方式,添加新成员
何时: 如果在创建对象时,暂时不知道对象的成员
揭示: js中的对象,底层其实就是关联数组
相同: 1. 都可用.或[]随时添加/访问成员
2. 访问不存在的成员,不报错,返回undefined
3. 都可用for in遍历每个成员
不同: 对象比关联数组用法简单!
今后,都是用对象来代替关联数组使用!
console.dir():xianshi
问题: 一次只能创建一个对象
如果反复创建相同结构的多个对象,重复代码会很多
解决:构造函数
//DRY
//1. 定义构造函数,描述学生类型的统一结构
function Student(sname,age){
this.sname=sname;
this.age=age;
this.intr=function(){
console.log(
"I'm "+this.sname
+", I'm "+this.age);
}
}
//2. 用new调用构造函数反复创建学生类型的具体对象
var lilei=new Student("Li Lei",11);
var hmm=new Student("Han Meimei",12);
console.dir(lilei);
console.dir(hmm);
lilei.intr();
hmm.intr();
- 用构造函数(constructor):
什么是: 描述同一类型的多个对象,相同成员结构的函数
第二个作用: 将一个空对象构造为拥有属性和功能的完整对象
何时: 只要反复创建同一类型的多个对象时,都要先用构造函数描述统一的结构,再用构造函数创建对象
如何: 2步
1. 定义构造函数:
function 类型名(属性参数,....){
this.属性名=属性参数;
... = ... ;
this.方法名=function(){
... this.属性名 ...
}
}
2. 使用构造函数反复创建对象:
var obj= new类型名(属性值,...);
调用时的参数值,应和定义构造函数时的属性参数保持一致。
new: 4件事:
1. 创建一个新的空对象
2. 自动让新的子对象继承构造函数的原型对象
3. 调用构造函数,将构造函数中的this执行正在创建的新对象。向新的空对象中强行添加新成员
4. 将新对象地址返回给变量保存
优: 重用结构定义
缺: 浪费内存
构造函数ex:
function Student(sname,age){
this.sname=sname;
this.age=age;
this.intr=function(){
console.log("我是"+this.sname+",我今年"+this.age);
}
}
var lilei=new Student("Li lei","18");
console.log(lilei);
lilei.intr();
var Hanmeimei=new Student("Hanmeimei","21");
Hanmeimei.intr();
继承:
什么是: 父对象的成员,子对象无需创建即可直接使用!
为什么: 代码重用!节约内存!
何时: 所有子对象都拥有相同的属性值和方法定义时,都要用继承来实现
如何: js中所有继承,都是继承原型对象
什么是原型对象: 集中存储所有子对象共有成员的父对象
为什么: 实现继承
何时: 只要实现继承,都要继承原型对象
如何:
创建: 不用手动创建,买一赠一
其实创建构造函数同时,都附赠一个空的原型对象
继承: 不用手动设置
用new创建新的子对象时,会自动设置新对象继承构造函数的原型对象
添加共有成员:
构造函数.prototype.成员=值;
总结: 只要所有子对象共用的成员,都必须集中存储在原型对象中
自有属性和共有属性:
自有属性: 直接保存在对象本地的属性
共有属性: 保存在原型对象中,所有子对象共有的属性
读取: 两者完全一样: 对象.属性名
修改: 自有属性: 只能用子对象改: 子对象.自有属性名=值
自有、共有属性修改时的差别:!!!
lilei.sname="li xiaolei"
Student.prototype.className="初二";
共有属性: 只能用原型对象修改:
构造函数.prototype.共有属性名=值
原型链: prototype chain
什么是: 由多级父对象,逐级继承,形成的链式结构
为什么: 为了更高级,更大范围的重用
如何:
所有对象,都有__proto__属性
原型对象的__proto__指向更上级的父对象
所有对象最终都继承自Object.prototype——顶级父对象
内置对象的原型链:
其实每种内置类型都有对应的构造函数和原型对象,也最终都继承自Object.prototype
其中: 内置类型的构造函数负责创建该类型的子对象
内置类型的原型对象负责保存该类型所有子对象共有的API
问题: 旧浏览器不支持新的API
解决: 向旧浏览器中的原型对象中手动添加一个函数
鄙视: 判断一个对象是不是数组类型,有几种方法:
typeof不行!
- 判断原型对象:
Object.getPrototypeOf(obj)==Array.prototype
判断obj是数组类型的子对象
问题: __proto__是内部属性,本不应该被访问到
解决: 用Object.getPrototypeOf(obj)
代替proto
- 判断构造函数:
实例
obj instanceof Array
判断obj是不是被构造函数Array创造出来的
instanceof 不仅判断直接父类型,而是所有在原型链上的类型,都返回true!
- 判断对象的内部class属性
每个对象内部,都有一个隐藏的class属性,记录该对象创建时的数据类型
class属性不会随继承关系的改变而改变
问题1: class是内部属性
解决: 只有最顶层的toString()才能输出对象的class属性值
[object class名]
问题2: 内置类型的原型对象中几乎都重写了新的toString()
解决: 用call强行调用:
call: 让一个对象,调用一个本来无法调用到的函数
何时: 只要希望调用一个本无法调用到的函数
如何: 要调用的函数.call(对象)
Object.prototype.toString.call(obj)=="[object Array]"
说明obj的内部属性class的值为"Array"
- 多态:
什么是: 同一个函数在不同情况下表现出不同的状态
重写: 如果子对象觉得父对象的成员不好用,可在本地定义同名成员,覆盖父对象中继承来的成员
为什么: 体现子对象和父对象之间的差异
何时: 只要子对象觉得父对象的成员不好用,就可以重写!