什么是面相对象?
程序中,都是用对象来集中描述现实中一个具体事物的属性和功能。
为什么要使用面向对象?
为了便于大量数据和功能的维护和使用
何时使用面向对象?
只要使用面相对象编程,都要先创建对象
如何使用?
事物的属性会成为对象的属性
事物的功能会变成对象的方法
成员=属性+方法
创建对象的3中方法:
1、对象直接量方式:
var obj = {
属性名:值,
属性名:值,
方法名:function(){
....this.属性名...
},
方法名:function(){
...this.属性名 ...
}
}
访问对象的成员:
访问对象的属性:对象名.属性名
本质:属性其实是保存在对象中的变量
访问对象的方法:对象名.方法名()
本质:方法其实是要保存在对象内的函数
问题:对象在自己的方法中使用自己的属性
错误1:在方法中直接使用属性名
原因:默认,没有加任何前缀的普通变量,只能在作用域链中查找,无权进入任何对象查找
错误2:在方法中使用对象名.属性名
原因:对象名只是一个普通变量,随时可能发生改变
正确做法:只要方法想访问自己的属性,必须用this.属性名
this->自动获得正在调用方法的"."前的对象名
2、用new创建
var obj = new Object();
obj.属性名=值;
obj.方法名=function(){
...this.属性名...
}
本质:js中一切对象底层都是关联数组:
与关联数组的区别:
相同:
1.可用["下标"],也可用.下标方式访问成员
如何选择:
1、如果属性名是固定的,首选
2、如果属性名是动态获得的,应用[]
2.随时可在任意位置添加新成员
3.访问不存在的成员不会报错,返回undefined
问题:一次只能创建一个对象,如果反复创建多个对象,代码会很冗余
解决:用构造函数反复创建多个就够功能相同的对象
用构造函数创建
定义构造函数:
function 类型名(属性参数,..){
this.属性名=属性参数;
...=...;
this.方法名=function(){
...this.属性名 ...
}
}
用构造函数创建对象:
var obj = new 类型名(属性值,...)
其中,new共做了4件事:
1、创建一个空对象
2、让新的子对象继承构造函数的原型对象
3、调用构造函数
1.this=>new
2.this.属性名=值:通过强行赋值的方式,为对象添加新的属性,并保存属性值
4、返回新对象的地址,保存在变量中
问题:每个对象都有一个重复的相同的方法定义,浪费内存
解决方案:继承
继承
什么是继承?
父对象的成员,子对象无需重复创建,即可直接使用
为什么要使用继承?
代码重用,节约内存
什么时候使用?
只要多个子对象,拥有相同的属性值和方法时
如何使用?
通过原型对象
什么是原型对象?
集中保存子对象共有成员的父对象
如何使用原型对象?
创建:不需要创建
定义构造函数时自动附赠一个原型对象
创建子对象时:new的第2步,让子对象自动继承构造函数的原型对象
添加共有成员:
构造函数中不再包含共有方法的定义
所有共有方法都应该添加到构造函数的原型对象中
构造函数.prototype.方法名=function(){
...this.属性名 ...
}
例如:
<script>
//描述所有学生的统一结构
function Student(sname,sage){
this.sname=sname;
this.sage=sage;
}
Student.prototype.intr=function(){ //使用原型对象定义(利用继承)
console.log(
"I'm "+this.sname+", I'm "+this.sage);
}
Student.prototype.className="初一2班";
//创建lilei
var lilei=new Student("Li Lei",11);
var hmm=new Student("Han Meimei",12);
console.log(lilei);
console.log(hmm);
lilei.intr(); //调用对象中的方法
hmm.intr(); //调用对象中的方法
</script>
这个例子中,由于每个对象都有一个相同的方法,故利用继承性定义对象的方法,所以构造函数Student中的子对象都可以直接使用方法
自有属性和共有属性:
自有属性:保存在子对象本地,归子对象独有的属性
共有属性:保存在父对象中,所有子对象共享的属性
获取属性指:子对象.属性名
修改属性值的方法:自有属性只能用子对象修改
共有属性只能用原型对象修改
内置类型的原型对象:
笔试题:ES中内置类型/对象共有多少个,分别是?
String Number Boolean --包装类型
Array Date RegExp Math
Error
Function Object
Global(在浏览器中被window代替)
他们的本质:
所有能new的都是构造函数
所有的API都保存在构造函数的原型对象中:
Array.prototype、Date.prototype...
问题:旧浏览器无法使用新的API
如何解决:
向该类型的原型对象中添加自定义的API
开发时:在MDN中查找:MDS 类型名.prototype.函数名->找到Polyfill
笔试:解释包装类型
什么是包装类型?
包装类型是专门保存一个原始类型的值,并提供操作原始类型值得API
为什么要使用包装类型?
原始类型的一个值本身没有任何功能
什么时候使用?
只要试图对原始类型的值调用API时,都会自动实用该原始类型对应的包装类型对象
例:
"hello".toUpperCase();//由于hello是原始类型string 本身没有任何功能,所以他会自动调用
//new String.toUpperCase("hello");将字符串变为大写
如何使用包装类型?
创建:不需要自己创建,当试图对原始类型的值调用API时,自动创建对应的包装类型对象。
调用的API其实是包装类型对象提前定义好的API
问题:包装类型的API也有浏览器兼容问题
如何解决?
同内置类型的API解决方式相同
例如:
//如果想在数组中查找一个数据,使用indexOf是很好的方法,但是在IE8中不支持,如何解决兼容问题呢?
if(typeof Array.prototype.indexOf !== "funciton")
Array.prototype.indexOf=function(val,starti){ //val 想找的字符 starti 开始的位置
starti = stari||0; //使用短路逻辑创建备用值,当用户没有传入starti时为0
for(var i=0;i<this.length;i++){
if(this[i]===val){ //判断传入的想要查找的值是否能找到
return i; //如果能,返回查找到的值
}
return -1; ///如果不能,返回-1
}
}
var arr = [1,2,3,4,3,2,1];
document.write(
arr.indexOf(3)+"<br>"+//2
arr.indexOf(3,3)+"<br>"//4
);
原型链
什么是原型链?
由多级父元素逐级继承,形成的链式结构
包括:所有对象的属性和方法
控制:属性和方法的使用顺序,以及共享范围
笔试题: 判断一个对象是不是数组类型,有几种方法:
问题: typeof只能区分五种原始类型和function
无法进一步细致区分引用类型对象的类型
例如:
var n=1,s="a",b=true,nu=null,un; //这些都是原始类型
var obj1={},obj2=[],obj3=new Date(),obj4=function(){}; //非原始类型
console.log(
typeof s, //string
typeof n, //number
typeof b, //boolean
typeof nu, //object
typeof un, //undefined
typeof obj1, //object
typeof obj2, //object
typeof obj3, //function
typeof obj4 //object
);//可以看出typeof只能区分原始类型与function 无法细分引用类型对象
1. 判断原型对象:
varbool=Array.prototype.isPrototypeOf(obj)
判断Array.prototype是不是obj的父级原型对象
例如:
var obj1={},obj2=[],obj3=new Date();
console.log(
Array.prototype.isPrototypeOf(obj1),//false
Array.prototype.isPrototypeOf(obj2),//true
Array.prototype.isPrototypeOf(obj3),//false
);
2. 判断构造函数:
//obj.constructor===Array
varbool= obj instanceof Array
问题: 不仅检查直接父级,且检查整个原型链
检查不够严格
原因:
<script>
var obj1={},obj2=[1,2,3],obj3=new Date();
var obj4={};
obj4.__proto__=obj2; //当给obj4的继承关系强行赋值为Array时
//1.
console.log(
Array.prototype.isPrototypeOf(obj1),//false
Array.prototype.isPrototypeOf(obj2),//true
Array.prototype.isPrototypeOf(obj3),//false
Array.prototype.isPrototypeOf(obj4) //true //无法检测
);
//2.
console.log(
//obj1.constructor===Array,
obj1 instanceof Array,//false
obj2 instanceof Array,//true
obj3 instanceof Array,//false
obj4 instanceof Array //true //无法检测
);
</script>
3. 判断每个对象内部的隐藏属性class:
class属性在创建对象时保存对象的类型
不随继承关系改变而改变
如何: 唯一的办法: Object.prototype.toString
问题: 多数父对象都重写了toString方法,将顶级的toString覆盖了,无法直接调用到
解决: call: 让任何一个对象抢到任何一个想调用的函数执行
要调用的函数.call(obj)
使用了.call让他们都使用Object的toString
console.log(
Object.prototype.toString.call(obj1)==="[object Array]" //false
,
Object.prototype.toString.call(obj2)==="[object Array]" //true
,
Object.prototype.toString.call(obj3)==="[object Array]" //false
,
Object.prototype.toString.call(obj4)==="[object Array]" //false 可以检测
);
在这里运用了多态
什么是多态?
同一个函数,在不同情况下表现出不同的状态
包括哪2种方式?
1. 重载:
2. 重写(override):
什么是重写?
子对象中定义了和父对象中的API同名的成员
为什么重写?
子对象觉得父对象中的成员不好用
何时需要重写?
只要子对象觉得父对象中的成员不好用,就可以在子对象本地重写父对象成员
如何重写?
只要在子对象本地定义和父对象中同名的API
方法:
var arr = [1,2,3];
toString.call[arr];