面向对象编程是一种编程范式。
面向对象。何为对象?
复习一下:
JavaScript中的数据类型分为:
原始类型:数值型,字符串型,布尔型、ES6新增的symbol
特殊类型:undefined型,null型
组合类型:对象型Object
对象是一个组合,是一个容器。他把关于这个实物的所有属性和行为都放在了一起。例如我们把一个人给数据化了。
let p1 = {
name:"张三",
age:30,
sex:"男",
job:'程序员',
address:'xx省xx市xxx',
eat(){
console.log("吃饭");
},
sleep(){
console.log("睡觉");
}
}
上面就是创建了一个对象,这个对象表示的张三这个人。
如果我要在创建一个李四 一个王五 这应该怎么办?这些对象都是同一类的东西,我们可以使用一个模板,然后调用这个模板,就可以得出这一类的对象、这个模板就是 类。
那么,JavaScript中如何创建类呢?
构造函数创建类
在es5中,是通过构造函数来创建类的。
function Person(name,age,sex,address){
this.name = name;
this.age = age;
this.sex = sex;
this.address = address;
this.eat = function(){
console.log('吃饭');
};
this.sleep = function(){
console.log("睡觉");
}
}
var p1 = new Person("张三",30,'男','北京');
p1.eat();
p1.sleep();
var p2 = new Person("李四",20,'男','上海');
p2.eat();
p2.sleep();
ps:javascript这个语言,是一个养成类的语言,历史变化太多,导致创建一个对象有很多办法。这里我们只说最后,最成熟的办法。
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.eat = function(){
console.log('吃吃吃');
}
let p = new Person('张三',20,'男');
p.eat();
console.log(p);
通过上面的代码,我们可以知道:
- 构造函数也是一个函数,只不过通常构造函数的首字符大写;
- 调用构造函数的时候,必须加new 关键字,才可以创建对象;
- 构造函数内部使用this,this指向的是新创建的对象。
使用new关键字调用函数的时候,程序到底是怎么执行的呢?
- 创建一个空对象 {};
- 将构造函数中的this指针指向创建的空对象;
- 执行构造函数的函数体,给空对象添加属性与属性值;
- 最后返回这个对象。
但是上面的方法有个问题,就是对于eat sleep这些行为,没有必要每一个对象都创建一个。可以把eat sleep这些行为,放在一个地方,并且这个地方的东西,可以被这个构造函数创建的所有的类共享。这个地方就是原型对象。
原型对象
- 每一个函数都是一个对象,是Function类的对象;
- 每一个函数都有一个原型属性,属性值叫做原型对象。
- 这个原型属性原型对象是针对构造函数来讲的;
- 构造函数创建的对象共享该构造函数的原型对象上的所有属性。
所以我们最后es5中创建类的成熟写法是:
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.eat = function(){
console.log('吃吃吃');
}
let p = new Person('张三',20,'男');
p.eat();
console.log(p);
面向对象的三大特点:封装、继承、多态。
上面创建类的过程就是封装;
接下来,看下如何实现继承。
继承
ES5的继承是基于原型对象的。
继承就是子类可以使用父类的东西;
每一个构造函数创建的对象都可以共享(使用)其原型对象的属性和方法;
所以,当一个子类想要继承父类的属性和方法的时候,就要把父类对象放在子类的构造函数的原型对象上。
function A()
{
this.a = "aaa";
}
function B(){
this.b = "bbb";
}
B.prototype = new A();
//原型继承: 将子类的构造函数的原型指针指向父类的实例
var b1 = new B();
console.log(b1);
console.log(b1.b);
console.log(b1.a);//可以使用另一个对象中的属性 完成了继承
每一个构造函数都有一个原型对象,原型对象中的属性与方法可以被这个构造函数创建出来的所哟的实例共享。那么,如果将A构造函数的原型指向另一个对象的实例new B(),那个这个对象的实例b中也有一个指针指向了B的原型对象,依次下去,就形成原型链。
es6中的类封装
es6中引入了关键字class进行封装。代码的可读性更好。
class Person{
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
eat(){
console.log(this.name + '吃饭');
}
sleep(){
console.log('睡觉');
}
}
let p = new Person('张三',20,'男');
p.eat();
p.sleep();
注意:
1、class关键字;
2、每一个类中都必须包含一个名为constructor的构造函数。注意函数名必须是constructor
3、calss中封装的方法相当于是添加到了原型对象上;
es6中的类的继承
class Person{
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
eat(){
console.log(this.name + '吃饭');
}
sleep(){
console.log('睡觉');
}
}
let p = new Person('张三',20,'男');
p.eat();
p.sleep();
class Teacher extends Person{
constructor(name,age,sex,subject){
super(name,age,sex);
this.subject = subject;
}
wrok(){
console.log(this.name+"老师正在上课。科目是:"+this.subject);
}
}
let t = new Teacher('王五',30,'男','数学');
t.eat();
t.wrok();
注意:
1、继承的关键字 extends
2、子类中调用父类,是通过super关键字实现的。
目前推荐es6的写法。