JS面向对象编程笔记(二):5种对象创建模式
# 开头
笔记视频内容源自B站JavaScript从入门到放弃 第十二章 面向对象编程 (opens new window),笔记为自行整理复习使用,欢迎一同学习交流,转载请通知原作者
# 二、对象创建模式
# 2.1、对象的字面量方式
- 对象字面量
- 工厂模式
- 构造函数模式
- 构造函数拓展模式
- 寄生构造函数模式
- 稳妥构造函数模式
- 原型模式
- 组合模式
- 动态原型模式
对象字面量:
通过new构造函数
<body> <script> var obj = new Object(); obj.name = '陈' console.log(obj); </script> </body>
1
2
3
4
5
6
7对象字面量(语法糖)
<body> <script> var person = { name:'chen', age:20 } console.log(person); </script> </body>
1
2
3
4
5
6
7
8
9Object.create(对象)
:从一个实例对象生成另一个实例对象,create()
方法中的参数a作为返回实例对象b的一个原型对象,在a中定义的属性和方法都能被b实例对象继承下来<body> <script> var a = { getX: function () { console.log("x"); }, }; var b = Object.create(a); console.log(b); b.getX() </script> </body>
1
2
3
4
5
6
7
8
9
10
11
12
# 2.2、工厂模式
通过字面量创建对象往往在创建多个对象时会产生不必要的冗余,浪费资源,这时候就需要工厂模式:
<body>
<script>
function createPerson(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
console.log(this.name)
}
return o
}
var p1 = createPerson('chen','18');
var p2 = createPerson('chen','28');
var p3 = createPerson('chen','38');
p1.sayName()
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
虽然他解决了之前的问题,但是他自身也有一些问题。它的好处时能创建多个类似的对象,但是没有解决对象识别的问题(都是Object)
console.log(p1 instanceof Object)
console.log(p2 instanceof Object)
2
如何解决呢,我们就需要使用构造函数模式
# 2.3、构造函数模式
创建自定义的构造函数:
<body>
<script>
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
console.log(this.name)
}
}
var man = new Person('chen',18);
var woman = new Person('chen2',20);
man.sayName()
woman.sayName()
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
不同于工厂模式,他将变量和方法都存储在了this
中,同时解决了工厂函数无法识别对象(即统一为Object)的问题,但是仍会存在一些共同方法同时存在的冗余问题,即man和woman实例中都有一个sayName
的方法,但是他们的方法的功能是一样的。但是分别占用了不同的空间,浪费了内存空间。
console.log(man.sayName === woman.sayName)
这个就需要构造函数拓展模式
来解决
构造函数拓展模式:
<body>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName() {
console.log(this.name);
}
var man = new Person("chen", 18);
var woman = new Person("chen2", 20);
console.log(man.sayName === woman.sayName);
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
问题:但是定义多个全局函数会严重污染全局空间,没有封装性可言
寄生构造函数模式:
创建一个函数,函数体内部实例化一个对象,并且将对象返回,在外部的使用new关键字实例化对象,结合了工厂模式和构造函数模式:
<body>
<script>
function Person(name, age) {
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function () {
console.log(this.name);
};
return o;
}
var man = new Person("chen", 18);
var woman = new Person("chen2", 20);
console.log(man.sayName === woman.sayName);
console.log(man.__proto__ === Person.prototype);
console.log(man instanceof Person);
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
问题:
- 定义了相同的方法,浪费了内存空间
instanceof
运算符和prototype
属性都没有意义
该模式尽量避免使用
稳妥构造函数模式:
没有公共属性
,并且它的方法也不引用this对象,不适用new
关键字,想要访问其中的私有属性必须通过内部的方法来实现,可以结合闭包理解:
<body>
<script>
function Person(name) {
var a = 10;
var o = new Object();
//name就属于私有属性
o.sayName = function () {
console.log(a);
console.log(name);
};
return o;
}
var p1 = Person("chen");
p1.sayName();
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
p1对象叫稳妥对象
console.log(p1.sayName === p2.sayName)
这种模式也会有浪费内存空间的问题,同时没有使用new操作符构造也会缺失instanceof
运算符和prototype
属性
# 2.4、原型模式
使用prototype
属性来定制共享的属性和方法:
<body>
<script>
function Person(){};
Person.prototype.name = 'chen';
Person.prototype.age = 28;
Person.prototype.sayName = function(){
console.log(this.name)
}
var p1 = new Person()
p1.sayName()
console.log(p1)
var p2 = new Person()
p2.sayName()
console.log(p1.sayName === p2.sayName);
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
我们还可以进一步包装:
<body>
<script>
function Person() {}
// Person.prototype.name = 'chen';
// Person.prototype.age = 28;
// Person.prototype.sayName = function(){
// console.log(this.name)
// }
Person.prototype = {
constructor: Person,
name: "chen",
age: 28,
sayName: function () {
console.log(this.name);
},
};
var p1 = new Person();
p1.sayName();
console.log(p1);
var p2 = new Person();
p2.sayName();
console.log(p1.constructor === Person);
console.log(p1.sayName === p2.sayName);
</script>
</body>
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
但是也会出现一些问题,朋友的状态被共享了:
<body>
<script>
function Person() {}
Person.prototype = {
constructor: Person,
name: "chen",
age: 28,
friends: ["a", "b"],
sayName: function () {
console.log(this.name);
},
};
var me = new Person();
me.sayName();
var you = new Person();
you.sayName();
me.friends.push("c");
console.log(me.friends);
console.log(you.friends);
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
问题:在于引用类型值属性会被所以的实例对象共享,并且会被修改,很少使用原型模式的原因
# 2.5、组合模式(重要)
他能解决原型模式状态共享问题,将私有的属性定义在函数内部,共有的方法通过原型去实现继承引用,该模式是目前使用最广泛,认同度最高的一种创建自定义对象的模式:
<body>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.friends = ["a", "b"];
}
Person.prototype = {
//改变原型对象的同时,要改变该原型对象的constructor属性,让他指向当前的构造函数Person
constructor: Person,
sayName: function () {
console.log(this.name);
},
};
var wo = new Person("wo", 18);
var ni = new Person("ni", 28);
console.log(wo);
console.log(ni);
wo.friends.push('c')
console.log(wo);
console.log(ni);
wo.sayName();
ni.sayName();
</script>
</body>
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
组合模式定制当前对象的属性,原型的作用是定制各个实例对象的共享属性
# 2.6、动态原型模式
动态原型模式是将组合模式中分开的部分都封装到构造函数中,避免未调用就初始化的问题:
<body>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.friends = ["a", "b"];
if (typeof this.sayName != "function") {
//初始化原型对象上的属性
Person.prototype.sayName = function () {
console.log(this.name);
};
}
}
//未被初始化
console.log(Person.prototype);
var wo = new Person("wo", 18);
//初始化后
console.log(Person.prototype);
console.log(wo instanceof Person)
</script>
</body>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 2.7、总结
1、字面量方式 **问题:**创建多个对象会造成冗余的代码 2、工厂模式 解决对象字面量方式创建对象的问题 **问题:**对象识别的问题 3、构造函数模式 解决工厂模式的问题 **问题:**方法重复被创建 4、原型模式 解决构造函数模式创建对象的问题 **特点:**在于方法可以被共享 **问题:**给当前实例定制的引用类型的属性会被所有的实例所共享 5、组合模式(构造函数和原型模式) **构造函数模式:**定义实例属性 **原型模式:**用于定义方法和共享的属性,还支持向构造函数中传递参数。 该模式是使用最广泛,应用度最高的一种模式
了解: 构造函数扩展模式 寄生构造函数模式 稳妥构造函数模式 动态原型模式