JavaScript面向对象编程及设计模式

一、面向对象编程

1、简述

2、面向对象编程特点

3、封装

4、this

5、call和apply

6、new

7、继承

8、多态

JavaScript面向对象编程及设计模式

一、面向对象编程

1、简述

面向对象是一种程序的设计思想,与之对应的编程思想叫做面向过程

**例如:**比如我想要用代码描述一个场景,有一只叫做xiaoA的猫,吃了一个苹果,又吃了一条鱼,然后有一只叫做xiaoB的猫,吃了一根香蕉

// 面向过程

function xiaoAEatApple() {}

function xiaoAEatFish() {}

function xiaoBEatBanana() {}

xiaoAEatApple();

xiaoAEatFish();

xiaoBEatBanana();

1

2

3

4

5

6

7

// 面向对象

function Cat(name) {

this.name = name

}

Cat.prototype.eat = function(something) {}

let xiaoA = new Cat('xiaoA')

let xiaoB = new Cat('xiaoB')

xiaoA.eat('apple')

xiaoA.eat('fish')

xiaoB.eat('banana')

1

2

3

4

5

6

7

8

9

10

2、面向对象编程特点

面向对象注重于抽象事务,而面向过程注重于叙述事务

面向对象逻辑清晰有条理,而面向过程比较方面

JS通过函数和原型,模拟了传统面向对象编程中类的概念实现了面向对象的编程模式

面向对象的编程思想,主要为了实现3件事,封装,继承和多态

3、封装

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

<script>

// 普通方法

// let carA = {

// name: 'xiaoA',

// eat() {

// console.log('xiaoA eat something')

// }

// }

// let carB = {

// name: 'xiaoB',

// eat() {

// console.log('xiaoB eat something')

// }

// }

// let carC = {

// name: 'xiaoC',

// eat() {

// console.log('xiaoC eat something')

// }

// }

// 代码重复量多,需要封装

// 封装

// 使用工厂模式封装

// function createCat(name) {

// let obj = {}

// obj.name = name;

// obj.eat = () => {

// console.log(name + 'eat something')

// }

// return obj;

// }

// let catA = createCat(xiaoA)

// let catB = createCat(xiaoB)

// let catC = createCat(xiaoC)

// 使用面向对象的方式进行封装

function CreateCat(name) { // 构造函数

this.name = name;

this.eat = () => {

console.log(this.name + 'eat something')

}

}

let catA = new CreateCat('xiaoA')

let catB = new CreateCat('xiaoB')

let catC = new CreateCat('xiaoC')

</script>

</head>

<body>


</body>

</html>

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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

4、this

// this

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

<script>


// this

// 在函数执行的时候会在函数内部创建两个变量,arguments, this

// arguments是存储着实参的一个类数组变量

// this 指向函数的执行上下文 (谁调用这个函数, this就指向谁)

function aaa (a, b) {

console.log(arguments)

}

aaa(1, 2, 3, 4)

// 数组 var arr = {1, 2, 3, 4}

// 类数组对象 var arrObj = {0: 1, 1: 2, 2: 3, 3: 4, length: 4}


function bbb() {

console.log(this)

}

var objA = {

b: bbb,

c: {

d: bbb,

}

}

bbb(); // this 指向 window

objA.b() // this 指向 objA

objA.c.d(); // this 指向 objA.c

</script>

</head>

<body>


</body>

</html>

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

31

32

33

34

35

36

37

38

39

40

41

42

5、call和apply

// call-apply

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

<script>

// call apply 用来动态改变this的指向

// function aaa() {

// console.log(this)

// }

// var objA = {

// b: aaa

// }

// aaa() // this 指向window

// objA.b() // this 指向objA

// aaa.call(objA); // call 函数将aaa内的this 从指向 window 改成了 指向objA

// objA.b.call(window); // call 函数将aaa内的this 从指向 objA 改成了 指向window

function aaa(name, age) {

// console.log(this)

this.name = name

this.age = age

}

var objA = {

b: aaa

}

aaa.call(objA, 'xiaoA', 23); // call 函数将aaa内的this 从指向 window 改成了 指向objA

console.log(objA.name, objA.age); // 打印 xiaoA 23

objA.b.call(window, ['xiaoB', 30]); // call 函数将aaa内的this 从指向 objA 改成了 指向window

console.log(window.name, window.age)

objA.b.call(window, 'xiaoB', 30); // call 函数将aaa内的this 从指向 objA 改成了 指向window

console.log(window.name, window.age)

objA.b.apply(window, ['xiaoB', 30]); // call 函数将aaa内的this 从指向 objA 改成了 指向window

console.log(window.name, window.age)

</script>

</head>

<body>


</body>

</html>

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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

6、new

new 做了哪些操作

1.创建一个空对象

2.将构造函数的prototype属性赋值给新对象的__proto__属性

3.将构造函数的this指向新对象

4.执行构造函数的代码

5.将新对象返回

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

<script>


function CreateCat(name) {

this.name = name

}

let catA = new CreateCat('xiaoA')

console.log(catA.name)

/*

new 做了哪些操作

1.创建一个空对象

2.将构造函数的prototype属性赋值给新对象的__proto__属性

3.将构造函数的this指向新对象

4.执行构造函数的代码

5.将新对象返回

*/

// 闭包,自执行函数

var catB = (function() {

var obj = {}

obj.__proto__ = CreateCat.prototype;

CreateCat.call(obj, 'xiaoB');

return obj

})()

console.log(catB.name)

</script>

</head>

<body>


</body>

</html>

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

31

32

33

34

35

36

37

38

39

7、继承

在声明函数的时候,会自动创建一个prototype属性,我们管他叫做原型, 一般用来存放实例公用的方法

prototype:

是子类继承的父类的属性,也就是当调用子类构造函数时,总的来说,这里只能是继承一个具体的对象,不能是一个类(es6后会有所改变)

prototype内容:

__proto__内容

是类继承父类之后,子类对象中的父类属性,里面的属性是可以通过子类对象直接取的,不需要用__proto__。

这里检索逻辑是:

先检索子类的直接属性是否存在,然后再检索__proto__,然后就是俄罗斯套娃,一层一层套下去,到最后还是没有就返回 undefined

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

<script>

/*

new 做了哪些操作

1.创建一个空对象

2.将构造函数的prototype属性赋值给新对象的__proto__属性

3.将构造函数的this指向新对象

4.执行构造函数的代码

5.将新对象返回

*/


// 在声明函数的时候,会自动创建一个prototype属性,我们管他叫做原型, 一般用来存放实例公用的方法

function CreateCat(name) {

this.name = name;

}

// console.log('prototype:', CreateCat.prototype)

CreateCat.prototype.eat = function(something) {

console.log(this.name + ' eat ' + something)

}

var catA = new CreateCat('xiaoA')

catA.eat('fish')

/*

carA:

1. {} // 第一步创建新对象

2. {

__proto__: CreateCat.prototype

}

3. {

__proto__: CreateCat.prototype,

name: 'xiaoA'

}

4.执行构造函数的代码

5.return

*/

// 在JS里规定,访问对象属性的时候,如果对象下面没有这个属性,则去他下面的__proto__去寻找,如果没有,就一直向下寻找直到没有__proto__为止

console.log(catA)

// 类式继承

function A(name) {

this.name = name;

this.list = [1, 2, 3]

}

A.prototype.getName = () => {

console.log(this.name);

}

function SubA(name) {

this.subName = 'sub' + this.name;

}

SubA.prototype = new A()

var sa1 = new SubA('sa1');

console.log(sa1.list, sa1.name); // 打印出 [1, 2, 3] undefined

/*

new A() -> {

name: undefined,

list: [1,2,3],

__proto__: {

getName: fn,

constructor....

}

}

new SubA('sa1') -> {

subName: 'sub sa1',

__proto__: {

name: undefined,

list: [1,2,3],

__proto__: {

getName: fn,

constructor....

}

}

}

// 类式继承的问题

1.这种方法不支持父构造函数带参数

2.父构造函数里的方法和属性都会变成共有属性

*/

var sa1 = new SubA('sa1');

var sa2 = new SubA('sa2');

A.prototype.getName = function() {

console.log('fixed getName')

}

A.prototype.newFn = function() {

console.log('new Fn')

}

sa1.getName(); // 打印 fixed getName

sa2.newFn(); // 打印 new Fn

// 构造函数继承

function A(name) {

this.name = name;

this.list = [1, 2, 3]

}

A.prototype.getName = () => {

console.log(this.name);

}

function SubA(name) {

A.call(this, name)

this.subName = 'sub' + this.name;

}

var sa1 = new SubA('xiaoA');

console.log(sa1.name, sa1.subName); // 打印出 xiaoA subxiaoA

sa1.getName(); // 报错

/*

new SubA('xiaoA'); -> {

__proto__: {

constructor....

},

name: 'xiaoA',

list: [1, 2, 3],

subName: 'sub xiaoA'

}

// 构造函数继承问题

1.不能继承父构造函数的原型方法


*/

// 组合式继承

function A(name) {

this.name = name;

this.list = [1, 2, 3]

}

A.prototype.getName = () => {

console.log(this.name);

}

function SubA(name) {

A.call(this, name)

this.subName = 'sub' + this.name;

}

SubA.prototype = new A()

var sa1 = new SubA('xiaoA');

console.log(sa1.name, sa1.subName); // 打印出 xiaoA subxiaoA

sa1.getName(); // xiaoA

/*

new A() -> {

name: undefined,

list: [1, 2, 3],

__proto__: {

getName: fn

}

}

new SubA('xiaoA'); -> {

name: 'xiaoA',

list: [1, 2, 3],

subName: 'sub xiaoA',

__proto__: {

name: undefined,

list: [1, 2, 3],

__proto__: {

getName: fn

}

},

}

// 组合式继承问题

1.__proto__里的属性没有用

2.执行了两次父构造函数

问题点在于: SubA.prototype = new A()

*/

// 4.寄生组合式继承

function A(name) {

this.name = name;

this.list = [1, 2, 3]

}

A.prototype.getName = () => {

console.log(this.name);

}

function SubA(name) {

A.call(this, name)

this.subName = 'sub' + this.name;

}

// SubA.prototype = new A() // 优化这一句代码

function inheritPrototype(subClass, superClass) { // 子构造函数 父构造函数

function F() {};

F.prototype = superClass.prototype;

subClass.prototype = new F()

subClass.prototype.constructor = subClass; // 可有可无

}

inheritPrototype(SubA, A)

var sa1 = new SubA('xiaoA');

console.log(sa1.name, sa1.subName); // 打印出 xiaoA subxiaoA

sa1.getName(); // xiaoA

</script>

</head>

<body>


</body>

</html>

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

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

8、多态

多态: 表示不同对象调用相同方法会产生不同结果

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<meta http-equiv="X-UA-Compatible" content="ie=edge">

<title>Document</title>

<script>

// 多态: 表示不同对象调用相同方法会产生不同结果

function Base() {}

Base.prototype.initial = function() {

this.init()

}

function SubA() {

this.init = function () {

console.log('subA init')

}

}

function SubB() {

this.init = function () {

console.log('subB init')

}

}


SubA.prototype = new Base();

SubB.prototype = new Base();

var subA = new SubA()

var subB = new SubB()

subA.initial();

subB.initial();

</script>

</head>

<body>


</body>

</html>