JS构造函数使用的必要及原型(prototype)属性使用的必要性
首先通过一系列代码的改写,来观察构造函数及原型属性的使用的必要性与重要性:
初代代码:
let obj1 = {
name: 'Jack',
myRun: function () {
console.log('我是' + this.name + '我会跑');
}
}
let obj2 = {
name: 'Rose',
myRun: function () {
console.log('我是' + this.name + '我会跑');
}
}
obj1.myRun();
obj2.myRun();
这段代码中,主要有两大问题:
问题一:每当new一个新对象,都要重新写一遍对象的结构,不灵活
问题二:在这段代码中,浪费内存资源的部分最主要的是对象里myRun函数的声明。
因为每当new一个对象时,都要在堆内存中开辟一个存放此对象的内存。而在这个例子中,这段堆内存主要存放name与myRun() 两个数据。显然,name属性是必要的,用来区分每个不同对象类似于id;但是myRun()方法是可以用调用来节省空间的,也就是说只用声明一次即可,不需要每次都跟着新对象开辟一段新空间;
二代代码(解决初代代码问题一):
function person(name) { // 工厂函数,
this.name = name;
this.myRun = function () {
console.log('我是' + this.name + '我会跑');
}
}
let person1 = new person('Jack');
person1.myRun();
let person2 = new person('Rose');
person2.myRun();
这段代码中,使用了工厂函数来构造同一类对象的模板,而每次new一个obj时都可以通过这段模板来新建对象,代码复用率更高;
这段工厂函数也就是JS中所谓的构造函数,使用十分方便,通过一个function就可以创造多个对象,这里应该是JS“一切皆对象”思想;不像java 、c++这些面向对象语言,构造函数和对象使用十分繁琐
三代代码(解决初代代码问题二):
let run = function () {
console.log('我是' + this.name + '我会跑');
}
let obj1 = {
name: 'Jack',
myRun: run
};
let obj2 = {
name: 'Rose',
myRun: run
}
obj1.myRun();
obj2.myRun();
此段代码中,run()方法只构造声明了一次,在堆内存中存放着。虽然,每new一次新的obj,都赋值了一次myRun属性,但是只是把run()方法在堆内存中的地址赋值给了每一个对象的myRun属性。所以,这个过程类似于变量赋值和引用,大大节省了内存空间相比于初代代码。
四代代码(在构造函数的基础上用原型(prototype/__ proto __)构造对象自身方法)
person.prototype.myRun = function run() {
console.log('我是' + this.name + '我会跑');
}
function person(name) {
this.name = name;
}
let person1 = new person('Jack');
person1.myRun();
let person2 = new person('Rose');
person2.myRun();
console.log(person2)