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

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();

// 面向对象
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')

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>

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>

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>

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>

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>

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>
————————————————

posted @ 2021-10-30 20:03  aixuexi666888  阅读(119)  评论(0编辑  收藏  举报