JS 面向对象及组件开发
JS的面向对象:
1、什么是面向对象编程
用对象的思想去写代码,就是面向对象编程
过程式写法
面向对象写法
我们一直都在使用对象
数组 Array 时间 Date
var date = new Date();
var arr = new Array(); //[] //我们把系统自带的对象,叫做系统对象
面向对象编程(OOP)的特点
抽象:抓住核心问题
封装:只能通过对象来访问方法
继承:从已有对象上继承出新的对象
多态:多对象的不同形态
2、对象的组成
方法(行为、操作)——函数:过程、动态的
属性——变量:状态、静态的
var arr = [];
arr.number = 10; //对象下面的变量:叫做对象的属性
arr.test = function(){ //对象下面的函数 : 叫做对象的方法
alert(123);
};
arr.test(); //123
3、创建第一个面向对象程序
为对象添加属性和方法
Object对象
this指向
创建两个对象 : 重复代码过多
//var obj = {};
var obj = new Object(); //创建了一个空的对象
obj.name = '小明'; //属性
obj.showName = function(){ //方法
//alert(obj.name);
alert(this.name); // this指向的就是obj
};
obj.showName(); //小明
4、工厂方式
//工厂方式 : 面向对象中的封装函数
function createPerson(name){
}
var p1 = createPerson('小明');
p1.showName();
var p2 = createPerson('小强');
p2.showName();
5、对象的引用
var a = 5;
var b = a;
b += 3;
alert(b); //8
alert(a); //5 基本类型 : 赋值的时候只是值的复制
var a = [1,2,3];
var b = a;
b.push(4);
alert(b); //[1,2,3,4]
alert(a); //[1,2,3,4] 对象类型 : 赋值不仅是值的复制,而且也是引用的传递
var a = [1,2,3];
var b = a;
b = [1,2,3,4]; // b又重新占了一个内存地址,和原来的无关
alert(b); //[1,2,3,4]
alert(a); //[1,2,3]
var a = 5;
var b = 5;
alert(a == b); //基本类型 : 值相同就可以
var a = [1,2,3];
var b = [1,2,3];
alert( a == b ); //false //对象类型 : 值和引用都相同才行
var a = [1,2,3];
var b = a;
alert( a==b ); //true
6、工厂方式,构造函数的写法:
首字母大写、New 关键字提取、This指向为新创建的对象
当new去调用一个函数 : 这个时候函数中的this就是创建出来的对象,而且函数的的返回值直接就是this(隐式返回)
构造函数:new后面调用的函数,即用来创建对象的函数,叫做构造函数
// CreatePerson()构造函数:
function CreatePerson(name){
//省略new Object和最后的return obj
this.name = name;
this.showName = function(){
alert( this.name );
};
}
var p1 = new CreatePerson('小明');
p1.showName();
var p2 = new CreatePerson('小强');
p2.showName();
alert( p1.showName == p2.showName ); //false ,值虽然相同,但是引用的 内存地址不同
存在的问题:对象的引用、浪费内存
7、原型-prototype
原型 : 重写对象方法,让相同方法在内存中存在一份。就是去改写对象下面公用的方法或者属性 , 让公用的方法或者属性在内存中存在一份 ( 提高性能 )
原型 : CSS中的class,可以重用,但优先级没style高
普通方法 : CSS中的style,不可以重用,优先级高
即原型可以重用,但优先级没有普通方法高。
var arr = [];
arr.number = 10;
Array.prototype.number = 20;
alert(arr.number); //10
普通方法:
var arr = [1,2,3,4,5];
var arr2 = [2,2,2,2,2];
arr.sum = function(){
var result = 0;
for(var i=0;i<this.length;i++){
result += this[i];
}
return result;
};
arr2.sum = function(){
var result = 0;
for(var i=0;i<this.length;i++){
result += this[i];
}
return result;
};
alert( arr.sum() ); //15
alert( arr2.sum() ); //10
原型方法 :
var arr = [1,2,3,4,5];
var arr2 = [2,2,2,2,2];
Array.prototype.sum = function(){
var result = 0;
for(var i=0;i<this.length;i++){
result += this[i];
}
return result;
};
alert( arr.sum() ); //15
alert( arr2.sum() ); //10
8、工厂方式之原型:面向对象的写法:
原型prototype : 要写在构造函数的下面
function CreatePerson(name){
this.name = name;
}
CreatePerson.prototype.showName = function(){
alert( this.name );
};
var p1 = new CreatePerson('小明');
p1.showName();
var p2 = new CreatePerson('小强');
p2.showName();
alert( p1.showName == p2.showName ); //true
总结面向对象写法:构造函数加属性,原型加方法
function 构造函数(参数){
this.属性 = 参数;
}
构造函数.prototype.方法 = function(){ ... };
调用写法:
var 对象1 = new 构造函数();
对象1.方法();
9、面向对象的例子:
原则:
1)先写出普通的写法,然后改成面向对象写法
2)普通方法变型
3)尽量不要出现函数嵌套函数
4)可以有全局变量
5)把onload中不是赋值的语句放到单独函数中
改成面向对象:
1)全局变量就是属性
2)函数就是方法
3)Onload中创建对象
4)改this指向问题
5)事件ev要放到事件函数中:
var ev = ev || window.event;
return false;
例子:面向对象的选项卡:
第一步:普通方法:
<script>
window.onload = function(){
}
};
</script>
第二步:
先变型:
尽量不要出现函数嵌套函数
可以有全局变量
把onload中不是赋值的语句放到单独函数中
<script>
var oParent = null;
var aInput = null;
var aDiv = null;
window.onload = function(){
oParent = document.getElementById('div1');
aInput = oParent.getElementsByTagName('input');
aDiv = oParent.getElementsByTagName('div');
init();
};
function init(){
for(var i=0;i<aInput.length;i++){
aInput[i].index = i;
aInput[i].onclick = change;
}
}
function change(){
for(var i=0;i<aInput.length;i++){
aInput[i].className = '';
aDiv[i].style.display = 'none';
}
this.className = 'active';
aDiv[this.index].style.display = 'block';
}
</script>
第三步:
全局变量就是属性
函数就是方法
Onload中创建对象
改this指向问题
<script>
window.onload = function(){
var t1 = new Tab('div1');
t1.init();
t1.autoPlay();
var t2 = new Tab('div2');
t2.init();
t2.autoPlay();
};
function Tab(id){
this.oParent = document.getElementById(id);
this.aInput = this.oParent.getElementsByTagName('input');
this.aDiv = this.oParent.getElementsByTagName('div');
this.iNow = 0;
}
Tab.prototype.init = function(){
var This = this;
for(var i=0;i<this.aInput.length;i++){
this.aInput[i].index = i;
this.aInput[i].onclick = function(){
This.change(this);
};
}
};
Tab.prototype.change = function(obj){
for(var i=0;i<this.aInput.length;i++){
this.aInput[i].className = '';
this.aDiv[i].style.display = 'none';
}
obj.className = 'active';
this.aDiv[obj.index].style.display = 'block';
};
Tab.prototype.autoPlay = function(){
var This = this;
setInterval(function(){
if(This.iNow == This.aInput.length-1){
This.iNow = 0;
}
else{
This.iNow++;
}
for(var i=0;i<This.aInput.length;i++){
This.aInput[i].className = '';
This.aDiv[i].style.display = 'none';
}
This.aInput[This.iNow].className = 'active';
This.aDiv[This.iNow].style.display = 'block';
},2000);
};
</script>
10、包装对象 : JS基于原型的程序、系统对象也是基于原型的程序
尽量不要去修改或者添加系统对象下面的方法和属性
基本类型都有自己对应的包装对象 : String Number Boolean
基本类型会找到对应的包装对象类型,然后包装对象把所有的属性和方法给了基本类型,然后包装对象消失
var str = 'hello';
String.prototype.lastValue = function(){
return this.charAt(this.length-1);
};
alert( str.lastValue() ); // o
var str = 'hello';
str.number = 10; //创建对象后,包装对象立即消失
alert( str.number ); //undefined 创建的新对象
11、原型链
原型链 : 实例对象与原型之间的连接,叫做原型链
原型链的最外层 : Object.prototype
对象调用的属性在构造函数中没有的话,会沿着原型链到该对象的prototype原型中查找,若还是没有,则沿着最外层原型链到Object.prototype中查找
function Aaa(){
this.num = 10; // 优先级最高
}
Aaa.prototype.num = 20; // 优先级第二
Object.prototype.num = 30; //优先级最后
var a1 = new Aaa();
alert(a1.num);
12、hasOwnProperty : 看是不是对象自身下面的属性
var arr = [];
arr.num = 10;
Array.prototype.num2 = 20;
alert( arr.hasOwnProperty('num') ); //true //只是arr中的属性,其余的数组对象没有
alert( arr.hasOwnProperty('num2') ); //false //数组对象原型中的属性,所有数组都有这个属性
alert( arr.hasOwnProperty == Object.prototype.hasOwnProperty ); //true // 这个属性是在最外层的Object原型上
13、constructor : 查看对象的构造函数
function Aaa(){
}
var a1 = new Aaa();
alert( a1.constructor ); //Aaa
var arr = [];
alert( arr.constructor == Array ); //true
function Aaa(){
}
Aaa.prototype.constructor = Aaa; //每一个函数都会有的,都是自动生成的
Aaa.prototype.constructor = Array; // 修改了对象的构造函数,避免这样做
function Aaa(){
}
Aaa.prototype.name = '小明';
Aaa.prototype.age = 20;
var a1 = new Aaa();
alert( a1.constructor );
//若用JSON形式写Aaa的属性,那么a1的constructor不是Aaa了,而是JSON对象,即Object
Aaa.prototype = {
constructor : Aaa,
name : '小明',
age : 20
};
function Aaa(){
}
Aaa.prototype.name = 10;
Aaa.prototype.constructor = Aaa;
for( var attr in Aaa.prototype ){ //遍历不到JS内置属性,只能遍历到name
alert(attr);
}
14、instanceof : 对象与构造函数在原型链上是否有关系
//自定义对象
function Aaa(){
}
var a1 = new Aaa();
alert( a1 instanceof Object ); //true
//系统内置对象
var arr = [];
alert( arr instanceof Array ); //true
15、toString() :
toString() : 系统对象下面都是自带的,不需要经过原型链寻。自己写的对象都是通过原型链找Object下面的。
var arr = [];
alert( arr.toString == Object.prototype.toString ); //false
/function Aaa(){
}
var a1 = new Aaa();
alert( a1.toString == Object.prototype.toString ); //true
toString() : 把对象转成字符串
var arr = [1,2,3];
Array.prototype.toString = function(){
return this.join('+');
};
alert( arr.toString() ); //'1+2+3'
toString() : 进制转换
var num = 255;
alert( num.toString(16) ); //'ff' //255转成16进制
利用toString做类型的判断 : (推荐)
var arr = [];
alert( Object.prototype.toString.call(arr) == '[object Array]' ); //'[object Array]'
用constructor和instanceof判断类型在iframe中判断不出
alert( arr.constructor == Array ); //false
alert( arr instanceof Array ); //false
16、继承
继承 :在原有对象的基础上,略作修改,得到一个新的对象。不影响原有对象的功能。
子类不影响父类,子类可以继承父类的一些功能 ( 代码复用 )
1)拷贝继承:
属性的继承 : 调用父类的构造函数 call
方法的继承 : for in : 拷贝继承 (jquery也是采用拷贝继承extend)
function CreatePerson(name,sex){ //父类
this.name = name;
this.sex = sex;
}
CreatePerson.prototype.showName = function(){
alert( this.name );
};
var p1 = new CreatePerson('小明','男');
//p1.showName();
function CreateStar(name,sex,job){ //子类
CreatePerson.call(this,name,sex); //继承 call改变this指向为对象
this.job = job;
}
//不能用对象的赋值,CreateStar.prototype = CreatePerson.prototype; 都指向同一内存地址,改变其中一个,另一个也会改变
extend( CreateStar.prototype , CreatePerson.prototype );
CreateStar.prototype.showJob = function(){
};
var p2 = new CreateStar('黄晓明','男','演员');
p2.showName();
function extend(obj1,obj2){ //把父类所有的非对象属性给子类
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
2)类式继承:
类 : JS是没有类的概念的 , 把JS中的构造函数看做的类
要做属性和方法继承的时候,要分开继承,避免子类对象之间的修改相互影响属性
属性的继承 : 调用父类的构造函数 call
方法的继承 : 利用构造函数(类)继承的方式
function Aaa(){ //父类
this.name = [1,2,3];
}
Aaa.prototype.showName = function(){
alert( this.name );
};
function Bbb(){ //子类 ,属性的继承
Aaa.call(this);
}
//方法的继承
var F = function(){};
F.prototype = Aaa.prototype; //只能继承方法
Bbb.prototype = new F();
Bbb.prototype.constructor = Bbb; //修正指向问题
var b1 = new Bbb();
b1.name.push(4);
alert( b1.name ); //[1,2,3,4]
var b2 = new Bbb();
alert( b2.name );//[1,2,3]
3)原型继承:
借助原型来实现对象继承对象
var a = {
name : '小明'
};
var b = cloneObj(a);
b.name = '小强';
alert( b.name );//小强
alert( a.name );//小明
function cloneObj(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
拷贝继承: 通用型的 有new或无new的时候都可以
类式继承: new构造函数
原型继承: 无new的对象
17、组件开发 : 多组对象,像兄弟之间的关系( 代码复用的一种形式 )
对面向对象的深入应用(UI组件,功能组件)
将 配置参数、方法、事件,三者进行分离
18、自定义事件 : 主要是跟函数有关系,就是让函数能够具备事件的某些特性
window.addEventListener('show',function(){
alert(1);
},false);
window.addEventListener('show',function(){
alert(2);
},false);
window.addEventListener('show',function(){
alert(3);
},false);
show(); //主动触发自定义事件