面向对象的程序设计

理解对象

面向对象和面向过程

  • 面向过程是分析出解决问题所需要的步骤,然后用函数把步骤一步一步实现,使用时一个一个依次调用就可以了。
  • 面向对象是把构成问题事务分解为各个对象,建立对象的目的是为了描述某个事物在整个解决问题的步骤中的行为。

    概念(我讲得通俗一点)

  • 类:js中没有类的概念,取了一个新的名字—原型对象

  • 原型对象:把猫(人、狗…)的特性提取出来,形成猫类(人类、狗类…),这个即为原型对象,代表着一类事物

  • 对象(实例):猫类(人类、狗类…)中的A猫(A人、A狗…),即为对象实例,代表一个具体事物

对象的属性类型

一般为基本数据类型,也可以是另外的对象

  • 数据属性

  • 访问器属性

    其特性详见书中139,感觉不会常用

小结

  1. 在js中一切都是对象
  2. 原型对象其实也是对象
  3. 判断一个对象实例具体是什么类型用instanceof运算符
    (引用类型中有详讲)
  4. 面向对象三大要素:封装、继承、多态。

创建对象

工厂模式(最古老)

1
2
3
4
5
6
7
8
9
10
11
这是一个能够根据接受的参数来构建一个包含所有必要信息的对象的函数
function Person(name,age){
var o=new Object(); //创建对象实例,但无法得知对象的类型
o.name=name; //添加属性
o.age=age;
o.sayName=function(){ //添加方法
alert(this.name);
};
return o; //返回对象引用
}
var person1=Person("greg",27);//调用函数创建对象实例

构造函数模式

1
2
3
4
5
6
7
8
function Person(name,age){
this.name=name; //实例属性
this.age=age;
this.sayName=function(){ //实例方法(不同实例地址不一样)
alert(this.name);
};
}
var person1=new Person("greg",27);

原型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Person(){  }
Person.prototype={
constructor:Person, //强制指向Person
name:"greg", //原型属性
friend:["blue","yellow"],
sayName:function(){ //原型方法(共享,地址一样)
alert(this.name);
}
};
var person1=new Person();
var person2=new Person();
person1.name="Lee"; //重写person1的名字
alert(person1.name); //Lee
alert(person2.name); //Greg
person1.friend.push("black");
alert(person1.friend); //blue,yellow,black
alert(person2.friend);//blue,yellow,black

小结:

  1. 属性的查找是先从实例中找,实例中没有该属性,便从原型中找
  2. 数组friend添加了一个项,是先从person1中找数组修改,由于没找到,所以顺着原型链找到原型中的数组并修改了
  3. 实例对象的proto指向,其构造函数的原型。构造函数的prototype获得构造函数的原型。构造函数原型的constructor指向对应的构造函数

组合使用构造函数模式和原型模式(最佳)

构造函数模式定义实例属性
原型模式定义方法和原型属性

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name,age){
this.name=name; //实例属性
this.age=age;
}
Person.prototype={
constructor:Person, //原型属性(共享)
sayName:function(){ //原型方法(共享)
alert(this.name);
}
};
var person1=new Person("Greg",27);
person1.sayName(); //"Greg"

动态原型模式

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name,age){
this.name=name;
this.age=age;
//仅在第一次调用时初始化
if(typeof this.sayName!="function"){
person.prototype.sayName=function(){
alert(this.name); //将原型方法封装在构造函数中
};
}
}
var person1=new Person("Greg",27);
person1.sayName(); //"Greg"

寄生构造函数模式(工厂模式+构造函数模式)

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name,age){
var o=new Object();
o.name=name;
o.age=age;
o.sayName=function(){
alert(this.name);
};
return o;
}
var person1=new Person("Greg",27);
person1.sayName();
不能确定对象关系,不推荐使用

稳妥构造函数模式

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name,age){
var o=new Object();
//可以在这里定义*私有*属性和方法
o.sayName=function(){
alert(name);
};
return o;
}
var person1=Person("Greg",27);
person1.sayName();
不引用this,不使用new操作符
但除了sayName()方法没有别的方法访问原始数据,安全性很高

继承

原型链继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function Super(){
this.val=1;
this.arr=[1];
}
Super.prototype.getSuper=function(){
return true; //原型方法
};
function Sub(){ }
//继承Super,把Super的对象实例赋给Sub的原型
Sub.prototype=new Super();
var sub1=new Sub();
var sub2=new Sub();
var super1=new Super();
sub1.val=2;
alert(sub1.val);//2
alert(sub2.val);//1
sub1.arr.push(2);
alert(sub1.arr);//1,2
alert(sub2.arr);//1,2
Sub.prototype.getSuper=function(){ //重写方法
return false;
};
alert(sub1.getSuper());//false //调用重写后的方法
alert(super1.getSuper());//true //调用原来的方法

小结:

  1. 重写方法或添加新方法需写在继承后
  2. 属性查找是先从对象实例中找,没有就顺着原型链继续向上查找
  3. 创建子类实例时,无法向父类构造函数传参
  4. 父类方法和属性得到复用,子类实例没有自己的属性

借用构造函数继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Super(val){
this.val=val;
this.arr=[1];
this.getSuper=function(){ };//方法在构造函数中,无法复用
}
function Sub(val){
Super.call(this,val); //继承Super,类似于对象冒充
}
var sub1=new Sub(1);
var sub2=new Sub(2);
alert(sub1.val); //1
alert(sub2.val); //2
alert(sub1.getSuper==sub2.getSuper);//false
每个子类实例都有新的方法地址,内存要爆炸了!!!

小结:
子类的每个实例都有自己的属性、方法,不会相互影响,但是占用内存。

组合继承(最常用)

原型链:对原型属性和方法的继承
借用构造函数:对实例属性的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Super(val){
this.val=val;
this.arr=[1];
}
Super.prototype.getSuper=function(){
return true; //原型方法
};
function Sub(val){
Super.call(this,val); //继承Super
}
Sub.prototype=new Super(); //继承Super
var sub1=new Sub(1);
var sub2=new Sub(2);
alert(sub1.val); //1
alert(sub2.val); //2
alert(sub1.getSuper==sub2.getSuper);//true

小结:

  1. 复用了方法,子类又有各自的属性。
  2. 因为父类构造函数被执行了两次,子类的原型对象(Sub.prototype)中也有一份父类的实例属性,而且这些属性会被子类实例的属性覆盖掉,也存在内存浪费。

原型式继承(类似于对象复制)

创建新对象–>返回该对象–>增强(添加属性/方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 生孩子函数 beget:龙beget龙,凤beget凤
//创建新的obj的对象实例
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
// 生孩子
var sub = beget(new Super()); // 核心
// 添加属性
sub.attr1 = 1;
sub.attr2 = 2;
alert(sub.val); // 1
alert(sub.arr); // 1
alert(sub.attr1); // 1
不像继承?

寄生式继承

创建新对象–>增强–>返回该对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 生孩子函数 beget:龙beget龙,凤beget凤。
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
function getSubObject(obj){
// 创建新对象
var clone = beget(obj); // 核心
// 添加属性
clone.attr1 = 1;
clone.attr2 = 2;

return clone;
}

var sub = getSubObject(new Super());
alert(sub.val); // 1
alert(sub.arr); // 1
alert(sub.attr1); // 1
给原型式继承穿个马甲?

寄生组合式继承(最佳,但组合继承最常用)

有缺陷的寄生式继承+不完美的组合继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 生孩子函数 beget:龙beget龙,凤beget凤
//创建新的obj的对象实例
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
// 在此处声明函数
Super.prototype.getSuper = function(){};
function Sub(){
Super.call(this); // 继承
}
//继承,把实例赋给Sub的原型
var proto = beget(Super.prototype);
proto.constructor = Sub;
Sub.prototype = proto;

var sub = new Sub();
alert(sub.val); //1
alert(sub.arr); //1

大总结:加油!棒棒哒!熬过这一章其余就轻松啦啦啦!已经写得尽可能详细了,多多复习!

ES6

创建对象

1
2
3
4
5
6
7
8
9
10
11
12
class father {
constructor(name,age){
this.name = name;
this.age = age;
} //方法与方法之间不需要逗号分隔
run(){
console.log("ok");
}
}
var ming = new father("ming",20);
ming.run(); //ok
console.log(ming.name); //ming

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class son extends father {
constructor(name,age,sex){
super(); //super方法要放在最前面
this.sex = sex; //添加新属性
}
run(){ //重写run方法
console.log("true");
}
get(){
//添加新方法
}
}
var hong = new son("hong",2,"女");
hong.run(); //true
console.log(hong.sex); //女

单词不要拼错:constructor

-------------本文结束 感谢您的阅读-------------