构造函数与原型
# 1.1 概述
在经典的OOP语言中,如Java,C++均存在class的概念,class是object的模板,而object是class的对象,但在ES6以前,JS中并无class的概念。
目前浏览器的JavaScript是ES5版本居多,大多数高级浏览器支持ES6,但也只支持了基本的特性和功能。而在ES6之前,object的实现不是基于class实现的,而是以一种称为构造函数的特殊函数实现的。
对象的创建:
//1.直接用{}实现【对象字面量】---直接使用
var obj1 = {};
//2.使用new方法 + Object对象---直接调用
var obj2 = new Object();
//3.使用new + 构造函数(特殊函数)---构造时
function Star(name,age){
this.name = name;
this.age = age;
this.sing = function(){
console.log('我会唱歌');
}
}
var obj3 = new Star("刘德华",45);
obj3.sing();
2
3
4
5
6
7
8
9
10
11
12
13
14
⚠️说明:
第一条,说明对象的本质就是一对花括号
{}
,我们可以完成对其幅值属性的操作。如obj1.属性名="str"
第二条,说明对象存在一个老祖宗
Object
,例如我们常用的数组创建,我们使用的Array
是基于Object
类通过构造函数魔改而来的,其内置了很多对矩阵的操作,如reverse()
等。var arr1 = new Array.(20,10,3); // object创建,方式二 var arr2 = [20,10,3] ; // 字面量创建,方式一 // 可直接调动内置方法 console.log(arr1.reverse());
1
2
3
4
# 1.2 构造函数
构造函数是一种特殊的函数,主要用来的初始化对象,我们常把对象中的一些公共的属性和方法抽取出来,并进行封装。
object的生成过程:new + 构造函数
内存中先开辟一块新的空间
this
指向这个空间内存执行构造函数内的内容
⚠️注意:这里并不是简单的copy[copy有可能只能copy到存放对象的索引],而是执行一遍伪"类"(构造函数)则不同,其可以完全拷贝"类"中的一切。
return
该函数,但是构造函数比较特殊,不需要写return
关键字
成员的添加:
JavaScript 的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的 this 上添加。通过这两种方式添加的成员,就分别称为静态成员和实例成员。
- 静态成员:在构造函数本上添加的成员称为静态成员,只能由构造函数本身来访问
- 实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问
# 1.3 内存爆炸
构造函数,会给每个object
开一个新地址去存function
,而这个不同object
中的functoin
是相同的,这样使用内存空间和时间开销都很大。
解决思路:在构造函数定义的时候,就预先设置一个公共的空间存放公有的方法【共享方法】,此方法就称之为原型。
# 1.4 原型 prototype
⚠️有几个点需要注意:
构造函数(
constructor
)通过prototype
属性访问公共区域【原型对象】。对象(
object
)通过__prototype__
属性访问公共区域【原型对象】。两者都是使用
constructor
关键字返回上级构造函数
构造函数对象、实例对象、原型对象三者转化
原型对象也存在prototype[原型对象],而该原型也存在constructor
函数,直到最初始的原型对象是Object
原型对象。
⚠️注意:原型对象里头存放的是方法,而原型中的
this
指向的是这个方法的调用者,即实例对象。
# 1.5 扩展案例
通过一个案例来说明,对内置的对象(如Array
)进行扩展sum
功能。
如果忘记JavaScript
中存在的方法,可以直接查看Array
的原型就可以看到内置方法(console.log(Array.prototype)
)
Demo:Array
中未定义求和的功能
<script type="text/javascript">
Array.prototype.sum = function() {
var sum = 0;
for (let i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
}
let array = [1, 2, 3];
console.log(array.sum());
console.log(Array.prototype);
</script>
2
3
4
5
6
7
8
9
10
11
12
查看自定义的方法:内置的都是浅色的,自定义以深色显示。
# 2.继承
ES6之前并没有给我们提供 extends
继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
# 2.1 call()
function fun(){...}
// 正常调用function,直接加()就可以了
fun();
// 修改this指针
fun.call(修改this的指向)
2
3
4
5
# 2.2 属性继承:修改this指针后执行父函数
function Father(name,age){
this.name = name;
this.age = age;
}
function Son(name,age,score){
Father.call(this,name,age,sex); // 在ES6中,使用super()重新执行一遍父类的constructor对象
this.score = score;
}
2
3
4
5
6
7
8
# 2.3 方法继承
基本操作:
子类.prototype = new 父类();
子类.prototype.constructor = 子类;
2
⚠️说明:
new
相当于新开内存,可以将子类的原型也给完整拷贝一份。
点击查看
Demo案例:
function Father(name, age) {
this.name = name;
this.age = age;
}
// 在外部定义原型
Father.prototype.fatherMethod = function() {
console.log("父类的方法");
}
function Son(name, age, score) {
Father.call(this, name, age); // 在ES6中,使用super()重新执行一遍父类的constructor对象
this.score = score;
}
// 在外部定义原型
Son.prototype = new Father();
Son.prototype.constructor = Son; // 注意:需要改上一层的指向
Son.prototype.sonMethod = function() {
console.log("子类的方法");
}
let father = new Father('刘德华', 46);
let son = new Son('刘德华', 46, 100);
console.log(father);
console.log(son);
son.fatherMethod();
son.sonMethod();
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