js中的面向对象(二)

137 阅读2分钟

new的用法

MDN解释:new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

对于编程的人来说,new解决了批量创建对象的问题。

首先,我们想创建100个士兵这个对象,那么可以做如下操作:

var soldier = {
    id: 1,
    兵种: "美国大兵",
    攻击力: 5,
    生命值: 42,
    行走: function () { /* 行走的代码 */},
    奔跑: function () { /* 奔跑的代码 */},
    死亡: function () { /* 死亡的代码 */},
    攻击: function () { /* 攻击的代码 */},
   防御: function () { /* 防御的代码 */}
}
// 创建一个士兵
兵营.create(soldier)

// 创建100个士兵呢,则循环100次来创造
var soldiers = []
var soldier
for(var i=0; i<100; i++){
    soldier = {
        id: i,
        兵种: "美国大兵",
        攻击力: 5,
        生命值: 42,
        行走: function () { /* 行走的代码 */},
        奔跑: function () { /* 奔跑的代码 */},
        死亡: function () { /* 死亡的代码 */},
        攻击: function () { /* 攻击的代码 */},
        防御: function () { /* 防御的代码 */}
    }
    soldiers.push(soldier)
}

兵营.batchCreate(soldiers)

那么,在创建100个士兵的情况下,相同的兵种、攻击力以及行走、奔跑、死亡、攻击和防御等动作需要不断的重复存储,为此我们可以使用__proto__来封装所有的共有的属性,这样,在存储的时候就可以节省 5/6 的存储空间。

于是,第一次优化如下:

//  第一次优化
var soldiers = []
var soldier
// 将共有属性
var soldierCommon = {
    兵种: "美国大兵",
    攻击力: 5,
    行走: function () { /* 行走的代码 */},
    奔跑: function () { /* 奔跑的代码 */},
    死亡: function () { /* 死亡的代码 */},
    攻击: function () { /* 攻击的代码 */},
    防御: function () { /* 防御的代码 */}
}
for(var i=0; i<100; i++){
    soldier = {
        id: i,     // ID 不能重复
        生命值: 42
    }
    soldier.__proto__ = soldierCommon
    soldiers.push(soldier)
}
兵营.batchCreate(soldiers)

第二次优化,将上述代码变成构造函数(返回新对象的函数叫做构造函数)

//   第二次优化
var soldierCommon = {
    兵种: "美国大兵",
    攻击力: 5,
    行走: function () { /* 行走的代码 */},
    奔跑: function () { /* 奔跑的代码 */},
    死亡: function () { /* 死亡的代码 */},
    攻击: function () { /* 攻击的代码 */},
    防御: function () { /* 防御的代码 */}
}
 
function createSoldier(i) {
    var obj = {
        id: i,     // ID 不能重复
        生命值: 42
    }
    obj.__proto__ = soldierCommon
    return obj
}

var soldiers = []
for(var i=0; i<100; i++){
    soldiers.push(createSoldier(i))
}
兵营.batchCreate(soldiers)

第三次优化,将 createSoldier 和 soldierCommon 从名字上关联起来,不至于有人误删 soliderCommon 。

//   第三次优化
createSoldier.soldierCommon = {
    兵种: "美国大兵",
    攻击力: 5,
    行走: function () { /* 行走的代码 */},
    奔跑: function () { /* 奔跑的代码 */},
    死亡: function () { /* 死亡的代码 */},
    攻击: function () { /* 攻击的代码 */},
    防御: function () { /* 防御的代码 */}
}
 
function createSoldier() {
    var obj = {
        id: i,     // ID 不能重复
        生命值: 42
    }
    obj.__proto__ = createSoldier.soldierCommon
    return obj
}

var soldiers = []
for(var i=0; i<100; i++){
    soldiers.push(createSoldier())
}
兵营.batchCreate(soldiers)

第四次优化,将上述的soldierCommon改成prototype,这只是个名字而已,规定为prototype,存的是同一个对象(对象在内存里面是没有名字的)。

//   第四次优化
createSoldier.prototype = {
    兵种: "美国大兵",
    攻击力: 5,
    行走: function () { /* 行走的代码 */},
    奔跑: function () { /* 奔跑的代码 */},
    死亡: function () { /* 死亡的代码 */},
    攻击: function () { /* 攻击的代码 */},
    防御: function () { /* 防御的代码 */}
}
 
function createSoldier() {
    var obj = {
        id: i,     // ID 不能重复
        生命值: 42
    }
    obj.__proto__ = createSoldier.prototype 
    return obj
}

var soldiers = []
for(var i=0; i<100; i++){
    soldiers.push(createSoldier())
}
兵营.batchCreate(soldiers)

第五次优化,来自 js 之父的关怀。在定义一个函数后,会有系统默认的一个属性prototype,表示谁创建了我们。若使用 createSoldier.prototype = {} 则会覆盖原先的 constructor。于是我们可以一个一个赋值,则不会覆盖原有的constructor。

// 第五次优化
function createSoldier(name) {
    this.id = i    // ID 不能重复
    this.生命值 = 42
    this.name = name || '无名战士'
}

// 绝对共有属性,谁创建了我们,系统默认会有的
/*
createSoldier.prototype = {
   constructor: createSoldier   
}
*/

// 推荐的方法为,一个一个赋值不会覆盖原有的constructor
createSoldier.prototype.兵种 = "美国大兵",
createSoldier.prototype.攻击力 = 5
createSoldier.prototype.行走 = function () { /* 行走的代码 */}
createSoldier.prototype.奔跑 = function () { /* 奔跑的代码 */}
createSoldier.prototype.死亡 = function () { /* 死亡的代码 */}
createSoldier.prototype.攻击 = function () { /* 攻击的代码 */}
createSoldier.prototype.防御 = function () { /* 防御的代码 */}

//  使用下面方法会覆盖原有的constructor
/**
createSoldier.prototype = {
    兵种: "美国大兵",
    攻击力: 5,
    行走: function () { },
    奔跑: function () { },
    死亡: function () { },
    攻击: function () { },
    防御: function () { }
}
*/

var soldiers = []
for(var i=0; i<100; i++){
    soldiers.push(new createSoldier())
}
兵营.batchCreate(soldiers)

使用上述代码创建士兵后,查看结果如下图所示。可以看到,是我们想要的结果。

到此,我们使用构造函数,批量创建了100个对象。构造函数需要注意一下四点:

  1. 构造函数一般情况首字母大写
  2. 构造函数可以省掉create
  3. 如果构造函数没有参数,那么可以省略括号
  4. 如果构造函数return的不是一个对象的话,则会继续返回这个对象 由上述2、 3可得知,之前的代码可以改写为如下:
// 第五次优化
function Soldier(name) {
    this.id = i    // ID 不能重复
    this.生命值 = 42
    this.name = name || '无名战士'
}

// 推荐的方法为,一个一个赋值不会覆盖原有的constructor
Soldier.prototype.兵种 = "美国大兵",
Soldier.prototype.攻击力 = 5
Soldier.prototype.行走 = function () { /* 行走的代码 */}
Soldier.prototype.奔跑 = function () { /* 奔跑的代码 */}
Soldier.prototype.死亡 = function () { /* 死亡的代码 */}
Soldier.prototype.攻击 = function () { /* 攻击的代码 */}
Soldier.prototype.防御 = function () { /* 防御的代码 */}

var soldiers = []
for(var i=0; i<100; i++){
    soldiers.push(new Soldier)   // 没有参数,将括号省略
}
兵营.batchCreate(soldiers)

构造函数默认返回this,若在构造函数中加入返回值,如果return一个对象,则该构造函数定义的是返回的对象;若return的不是对象,则忽略return的东西,即继续返回默认的this。

写在最后

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。