概念
在javaScript中我们我们有两大编程思想,面向过程(POP)和 面向对象(OOP);
面向对象的特征
- 继承 ->子类继承了父类的属性方法
- 封装-> 将实现同样的代码段,放到一个函数中,可以重复使用,不需要关系代码实现的细节,实现代码的高内聚,低耦合
- 多态
类和对象的关系
- 类是对象的抽象,对象是类的具体
- 类是对象的模板,对象是类的产品
如何创建类
使用class关键字,class后面跟上类名,类名的命名使用帕斯卡命名法(首字母大写)
如何实例化一个类
实例化一个类就是创建一个实例(对象),使用关键字new,后面跟上类名和实参列表,返回值是一个实例(对象)
构造器
- 构造器就是类中自带的一个方法,可以理解为前缀方法
- 当我们创建一个实例(对象)的同时,他会被自动调用,即出现new的时候,构造器自动执行;
- 每个类都有构造器,即使我们不定义构造器也存在,为空
- 构造器的方法是固定的,为constructor
- 因为class类没有传形参的地方,所以需要constructor()来传递形参,来实现动态类;
简单demo
1 2 3 4 5 6 7
| class Person{ constructor(){ console.log("构造器执行了") } } var person1 = new Person();
|
创建一个动态类
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Person{ constructor(name,age){ this.name=name; this.age=age; } say(){ console.log(`name is ${this.name},age is ${this.age}`) } } var person1 = new Person("张三",20); person1.say();
|
继承
- 一个类中存在另一个类的属性或方法,就是继承;
- 继承使用关键字extends;
- 继承的类称为子类,也叫派生类;
- 被继承的类称为父类,也叫基类;
demo1:
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 26 27 28 29 30
| class Worker { constructor(name, money) { this.name = name; this.money = money; } kq() { console.log(this.name + "的考勤") }; jx() { console.log(this.name + "的绩效" + this.money) } }
class Boss extends Worker { skq() { console.log(this.name + "正在审核考勤") } }
var worker = new Worker("张三", 10000); worker.kq(); worker.jx();
var boss = new Boss("李四", 15000); boss.kq(); boss.jx(); boss.skq()
|
demo2:
有时候父类定义的方法不一定能满足子类的需求,可以通过在子类中重写该方法,会直接覆盖父类的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Worker { constructor(name, money) { this.name = name; this.money = money; } kq() { console.log(this.name + "的考勤") }; }
class Boss extends Worker { kq() { console.log(this.name + "的考勤跟你不一样,俺的是年度的~~~") } }
var worker = new Worker("张三", 10000); worker.kq();
var boss = new Boss("李四", 15000); boss.kq();
|
继承构造器
- 我们可以通过刚才的方法来重写一个方法,但如果父类的构造器无法满足子类的需求,得需要继承构造器;
- 关键字super;
- 我们需要在子类中重新定义一个构造器,并继承父类所有参数,然后在子类的构造器中,使用super继承父类的构造器;
- 其实就是子类用到的动态变量,父类没有,然后需要在子类中传形参;
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 26 27 28 29 30
| class Worker { constructor(name, money) { this.name = name; this.money = money; } kq() { console.log(this.name + "的工资为" + this.money) }; }
class Boss extends Worker { constructor(name, money, sex) { super(name, money); this.sex = sex; } kq() { console.log(this.name + "的工资为" + this.money+"性别为"+this.sex) }; }
var worker = new Worker("张三", 10000); worker.kq();
var boss = new Boss("李四", 15000,1); boss.kq();
|
创建对象的方式
字面量方式
- 优点:可以对命名空间进行划分,防止属性或方法冲突 (多个name也能区分开,因为属于对象私有属性或方法)
- 缺点:属于手工作业模式,不能实现批量生产 (相同功能的代码冗余)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
var obj1 = { name: "哈哈", fn: function () { console.log("obj1"); } }; var obj2 = { name: "呵呵", fn: function () { console.log("obj2"); } }; console.log(obj1.name, obj2.name); obj1.fn(); obj2.fn();
|
工厂函数模式
我们可以封装一个函数,这个函数用于帮助我们创建一个对象,我们只需要重复调用这个函数即可;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function createPerson(name, age, height) { const person = {}; person.name = name; person.age = age; person.height = height; person.eat = function () { console.log(this.name + "is eating"); } return person; } const p1 = createPerson("张三", 18, 188); const p2 = createPerson("李四", 20, 177); const p3 = createPerson("王五", 24, 178);
|
构造函数模式
在ES6
之前,我们都是通过function
来声明一个构造函数(类)的,之后通过new
关键字来对其进行调用;(如果这么一个普通的函数被使用new
操作符来调用了,那么这个函数就称之为是一个构造函数;其实为了区别开,声明的时候首字母建议大写);
在ES6
之后,JavaScript可以像别的语言一样,通过class
来声明一个类;
- 优点:可以实现命名空间的划分,防止属性或方法冲突,可以实现批量生产,可以实例识别
- 缺点:不能实现公共的属性或方法公有,都是自己私有的
new的五步骤:
其实这个结合工厂函数(普通函数共同属性封装)
更好理解。构造函数
调用封装的函数,他既没有声明一个新对象
,也没将函数内部的变量return
出去,而且构造函数
内部使用的是this
与对象的属性产生关联;所以实例化对象
的时候,按道理是报错的,所以构造函数创建新对象,返回新对象,内部使用的this与创建的新对象关系绑定等操作,需要一个东西来完成,这个东西就是在调用构造函数前,使用的new
关键字来完成的。
- 创建一个新的空对象(空实例)(obj)
- 将构造函数的显式原型赋值给这个新对象,作为新对象的隐式原型
- 构造函数内部的this,会指向创建出来的新对象(this指向第一步的空对象)
- 执行函数体的代码块
- 返回对象
简单实现一下new
1 2 3 4 5 6 7 8 9 10
| function myNew(fn,...args){ const obj={}; obj.__proto__=fn.prototype; fn.apply(obj,args); return obj; }
|
instance of:检测当前这个实例(对象)是否属于某个类
1 2 3 4
| var arr = [1,2,3]; console.log(arr instanceof Array); console.log(arr instanceof String);
|
简单案例:
1 2 3 4 5 6 7 8 9
|
function Person(){ console.log(this); };
var person1 = new Person(); var person2 = new Person();
|
案例二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function Person(name, age) { this.name = name; this.age = age; this.say = function () { console.log(`名字:${this.name},年龄:${this.age}`) } };
var person1 = new Person("张三", 19); var person2 = new Person("李四", 20); console.log(person1); console.log(person2); console.log(person1.say === person2.say);
|
原型模式
- 优点:可以实现命名空间的划分,防止属性或方法冲突,可以实现批量生产,可以实例识别,可以实现公共属性或方法公有;
- 原型模式的基于构造函数模式的;
- 原型模式将当前实例(对象)公有的属性或方法写到prototype上;
- prototype是当前函数(类)的属性;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
function Person(name, age) { this.name = name; this.age = age; }
Person.prototype.say = function () { console.log(`name:${this.name},age:${this.age}`) }
Person.prototype.test = "我是测试用的~";
var person1 = new Person("zhangsan", 20); var person2 = new Person("lisi", 24); console.log(person1.say === person2.say);
|
详情可参考:
原型与原型链