【JavaScript】面向对象

Posted by ARTROY on 2018-05-22

面向过程、面向对象及面向对象编程

面向过程(Procedure Oriented:分析解决问题所需要的步骤,然后按照顺序一步一步调用不同的函数。例如:开(冰箱) -> (大象)装进冰箱 -> 关(冰箱)。
面向对象(Object Oriented,OO:面向对象有三大特点(封装:将功能封装程一个独立、继承:代码重用、减少编码,间接减少维护成本、多态:不同场合作出不同响应),把构成问题事物分解程各个对象,建立对象目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题问题的步骤中的行为。例如:冰箱.开门() -> 冰箱.装进(大象) -> 冰箱.关门()。
面向对象编程(Object Oriented Programming,OOP:是一种编程开发思想,它将真实世界各种复杂的关系,抽象成一个个对象,每个对象都是功能的中心,分工明确,可以接收信息,处理数据,发出信息等任务。因此,面向对象编程具有灵活性、代码可复用、高度模块化的特点。容易维护和开发。

注意:
(1)面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊。
(2)面向对象就是找一个对象,将执行者变成指挥者。
(3)面向对象不是面向过程的替代,而是面向过程的封装。

构造函数的属性

1
2
3
4
5
6
7
8
9
10
11
funcion A(name) {
this.name = name; // 实例基本属性 (该属性,强调私有,不共享)
this.arr = [1]; // 实例引用属性 (该属性,强调私用,不共享)
this.say = function() { // 实例引用属性 (该属性,强调复用,需要共享)
console.log('hello')
}
}

注意:数组和方法都属于‘实例引用属性’,但是数组强调私有、不共享的。方法需要复用、共享。

注意:在构造函数中,一般很少有数组形式的引用属性,大部分情况都是:基本属性 + 方法。

一、类与实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 类的声明:js
function Animal1() {
this.name = 'name'
this.run = functionn() {
console.log("跑")
}
}
// 类的声明:es6
class Animal2 {
constructor() {
this.name = 'name'
}
}
// 实例化
var objo = Animal1(); // 工厂模式 只只要知道所需要生产的东西,无需关心具体过程,
var obj1 = new Animal1(); // 构造函数模式
let obj2 = new Animal2();

二、类与继承

2.1 借助构造函数实现继承(构造函数继承)

原理:改变函数运行时的上下文,即this的指向
优点:实现了继承属性,可以传值
缺点:实现部分继承,父类的原型对象上的方法无法继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Parent1() {
this.name = 'Parent1';
}
Parent1.proptotype.say = function() {
console.log(this.name);
} // 父类的原型对象
function Child1() {
Parent1.call(this) // apply 核心
this.name = 'Parent1';
this.type = 'Child1';
}
// new Parent1().say() // Parent1
// new Child1().say()报错,不存在
// say方法如果在构造函数里面可以实现继承,但如果父类的原型对象上还有方法,子类是拿不到方法。
console.log(new Child1, new Child1().say())

2.2 借助原型链实现继承

原理:改变子类的prototype(即其实例的__proto__)属性指向其父类的实例
优点:实现了构造函数的继承,也继承了父类原型上的方法
缺点:原型链中的原型对象是共用的,子类实例的原型对象是同一个引用,当一个实例的属性改变时,其他实例也跟着改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Parent2() {
this.name = 'Parent2';
this.arrs = [1, 2];
}
function Child2() {
this.type = 'Child2';
}
/**
* `prototype`是子类构造函数的一个属性,每个函数都有这个属性,
* 这个属性是一个对象,这个对象是可以随意赋值的,
* 父类 Parent2 一个实例 赋值 给 prototype
* new Child2().__proto__ === Child2.prototype
*/
Child2.prototype = new Parent2(); // 核心
console.log(new Child2().__proto__);

var a20 = new Child2();
var a21 = new Child2();
a21.arrs.push(3)
console.log(a20.arrs, a21.arrs) // [1, 2, 3], [1, 2, 3]

2.3 组合继承(构造函数+原型链)

原理:结合构造函数和原型链方法的优点,弥补了此两种方法的不足
优点:拥有两种方法的优点,弥补以上两个缺点
缺点:多次执行父类构造函数,浪费内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 父类构造函数多次执行(两次)
function Parent3() {
this.name = 'Parent3';
this.arrs = [1, 2];
}
Parent1.proptotype.say = function() {
console.log(this.name);
}
function Child3() {
Parent3.call(this)
this.type = 'Child3';
}
Child3.prototype = new Parent3();

var a30 = new Child3();
var a31 = new Child3();
p30.say(); // Child3
a30.arrs.push(3)
console.log(a30.arrs, a31.arrs) // [1, 2, 3], [1, 2]

2.4 组合继承优化1(构造函数+原型链)

原理:子类原型直接引用父类的原型
优点:不需要多次执行父类构造函数
缺点:不知道实例是由谁创造的(以上方法都存在)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 优化:父类构造函数执行一次
function Parent4() {
this.name = 'Parent4';
this.arrs = [1, 2];
}
function Child4() {
Parent4.call(this)
this.type = 'Child4';
}
Child4.prototype = Parent4.prototype; // 核心

var a40 = new Child4();
var a41 = new Child4();
a40.arrs.push(3)
console.log(a40.arrs, a41.arrs) // [1, 2, 3], [1, 2]

// 强化:因优化不知道父类原型对象和子类原型对象
console.log(a40 instanceof Child4, a41 instanceof Parent4) // true true
console.log(a41.constructor) // Parent4

2.5 组合继承优化2(寄生组合继承)

原理:利用Object.create()函数创建的对象,子类与父类的原型对象进行隔离,覆盖constructor
优点:不需要多次执行父类构造函数,且知道是由谁创造的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Parent5() {
this.name = 'Parent5';
this.arrs = [1, 2];
}
function Child5() {
Parent5.call(this);
this.type = 'Child5';
}
Child5.prototype = Object.create(Parent5.prototype) // 核心 __.proto__向上找
Child5.prototype.constructor = Child5; // 核心 设置子类的构造函数 不然还是会根据原型链进行查找

var a5 = new Child5();
console.log(a5 instanceof Child5, a5 instanceof Parent5) // true true
console.log(a5.constructor) // Child5

2.6 ES6 Class extends

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A {
constructor() {
this.color = '#FFF';
this.arrs = [1, 2, 3]
}
say(word) {
console.log(word);
}
}

class B extends A {
constructor() {
super();
this.color = '#FF4'
}
}
let a1 = new B();
let a2 = new B();
a1.arrs.push(9);
a1.say('hello');
console.log(a1.arrs, a2.arrs) // [1, 2, 3, 9] [1, 2, 3]

原理: 创建了父类A,然后B子类继承父类,两个类中都有一个constructor构造方法,实质就是构造函数 AB
在子类的构造方法中调用了 super 方法,它表示父类的构造函数,用来新建父类的this对象。
注意:子类必须 constructor 方法中调用 super 方法,否则新建实例时会报错。
这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。
如果不调用 super 方法,子类就得不到 this 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {}

class B {}

Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

//


支付宝打赏 微信打赏

欣赏此文,打赏一下



-->