jsp中如何使用js对象_js中的面向对象(三)

bd8977ebce1c43eeb89525223364212d.png

继承的写法

上面几篇文章提到面向对象并没有明确的定义,只是一种编程套路。

js 本身就是一种动态语言。何为动态语言呢?字面意思来讲就是一会是这个状态,一会是那个状态,即 js 本身就是满足多态的。那么,如何用 js 来实现 Java 中的面向对象(封装、继承、多态)呢?

问:什么是类?
答:士兵的配方:独有属性(ID和生命值)、共有属性(兵种,死亡动画);
人类的配方:独有属性(名字、肤色)、共有属性(吃喝拉撒)。
若 士兵 继承 人类,s = new 士兵, 则 s 直接就可以吃喝拉撒

那么,接下来要做的就是怎样用 js 模拟继承。

原型链实现继承

var personCommon = {
    吃: '',
    喝: '',
    拉: '',
    撒: ''
}

var soldierCommon = {
    兵种: "美国大兵",
    攻击力: 5,
    行走: function () { /* 行走的代码 */},
    奔跑: function () { /* 奔跑的代码 */},
    死亡: function () { /* 死亡的代码 */},
    攻击: function () { /* 攻击的代码 */},
    防御: function () { /* 防御的代码 */}
}

soldierCommon.__proto__ = personCommon

var s = {}
s.__proto__ = soldierCommon
s.名字 = ''
s.肤色 = ''
s.ID = ''
s.生命值 = ''

将如上代码运行,打印 s 则可看到:s 拥有的自有属性为名字、肤色、ID以及生命值,其共有属性包括 soldier 以及 person 中的属性。

511406da4894aa9b14525f3e652f8882.png

上述代码太散,于是我们使用构造函数将零散的代码封装一下。

// 先定义一个Human的构造函数,拥有自有属性name和肤色
function Human(options){
    this.name = options.name
    this.肤色 = options.肤色
}
//  Human的公有属性 eat,drink,poo
Human.prototype.eat = function(){}
Human.prototype.drink = function(){}
Human.prototype.poo = function(){}

//  定义一个Soldier的函数,其name和肤色来自人类,故先调用Human获取name和肤色
//  Soldier的自有属性为id和生命值
function Soldier(options) {
    Human.call(this, options)
    this.id = options.id    // ID 不能重复
    this.生命值 = 42
}
// Soldier的公有属性
Soldier.prototype.兵种 = "美国大兵",
Soldier.prototype.攻击力 = 5
Soldier.prototype.行走 = function () { /* 行走的代码 */}
Soldier.prototype.奔跑 = function () { /* 奔跑的代码 */}
Soldier.prototype.死亡 = function () { /* 死亡的代码 */}
Soldier.prototype.攻击 = function () { /* 攻击的代码 */}
Soldier.prototype.防御 = function () { /* 防御的代码 */}

// 实现Soldier有Human的公有属性
Soldier.prototype.__proto__ = Human.prototype
var s = new Soldier({name: 'John', id:1, 肤色:'yellow'})

上述,我们使用构造函数实现了 new 一个 Soldier,即可拥有 Human 的自有属性和公有属性。一切看起来都是那么的完美。但是,我们在生产环境中不可以使用 __proto__,也就是上述代码中的 Soldier.prototype.__proto__ = Human.prototype 在生产环境中是不可以使用的。但是,我们又知道 js 自己的 new 是可以使用 __proto__ 的。

思考:如下代码中发生了什么?

// new一个对象可以做如下几件事情
function Human(){
    this = {}
    this.__proto__ = Human.prototype
    return this
}

Soldier.prototype = new Human()
// Soldier.prototype === Human中的this
// Soldier.prototype.__proto__ === this.__proto === Human.prototype


function Human(){}
h = new Human()
h.__proto__ === Human.prototype   // true
Soldier.prototype = new Human()
Soldier.prototype.__proto__ === Human.prototype  // true

那么,我们可以使用new来改造一下之前代码。将 Soldier.prototype.__proto__ = Human.prototype 替换成 Soldier.prototype =newSoldier({name:'Jack', 肤色:'white'})

// 先定义一个Human的构造函数,拥有自有属性name和肤色
function Human(options){
    this.name = options.name
    this.肤色 = options.肤色
}
//  Human的公有属性 eat,drink,poo
Human.prototype.eat = function(){}
Human.prototype.drink = function(){}
Human.prototype.poo = function(){}

//  定义一个Soldier的函数,其name和肤色来自人类,故先调用Human获取name和肤色
//  Soldier的自有属性为id和生命值
function Soldier(options) {
    Human.call(this, options)
    this.id = options.id    // ID 不能重复
    this.生命值 = 42
}
// Soldier的公有属性
Soldier.prototype.兵种 = "美国大兵",
Soldier.prototype.攻击力 = 5
Soldier.prototype.行走 = function () { /* 行走的代码 */}
Soldier.prototype.奔跑 = function () { /* 奔跑的代码 */}
Soldier.prototype.死亡 = function () { /* 死亡的代码 */}
Soldier.prototype.攻击 = function () { /* 攻击的代码 */}
Soldier.prototype.防御 = function () { /* 防御的代码 */}

// 实现Soldier有Human的公有属性
Soldier.prototype = new Soldier({name: 'Jack', 肤色:'white'})
var s = new Soldier({name: 'John', id:1, 肤色:'yellow'})

打印 s 查看,于是我们发现了 s 的公有属性中不仅有 Human 的公有属性,还有 Human 的自有属性。一个没有自有属性的构造函数使用 new 可以实现 __proto__ 的赋值。

5495d6a7f1649f32fe42cb173cbbf9b5.png

因此,我们定义一个假的 Human 构造函数 FakeHuman,使得 FakeHuman.prototype = Human.prototype,如此我们便有了一个没有自有属性但是公有属性跟 Human 的相同的构造函数。

function Soldier(options) {
    Human.call(this, options)
    this.id = options.id    // ID 不能重复
    this.生命值 = 42
}

function Human(options){
    this.name = options.name
    this.肤色 = options.肤色
}
Human.prototype.eat = function(){}
Human.prototype.drink = function(){}
Human.prototype.poo = function(){}

// 声明一个假的Human对象,只保留Human的公有属性,不要其私有属性
// 兼容IE的
function fakeHuman(){}
fakeHuman.prototype = Human.prototype
Soldier.prototype = new fakeHuman()
// 上述语句 Soldier.prototype.__proto__ === fakeHuman.prototype === Human.prototype

Soldier.prototype.兵种 = "美国大兵",
Soldier.prototype.攻击力 = 5
Soldier.prototype.行走 = function () { /* 行走的代码 */}
Soldier.prototype.奔跑 = function () { /* 奔跑的代码 */}
Soldier.prototype.死亡 = function () { /* 死亡的代码 */}
Soldier.prototype.攻击 = function () { /* 攻击的代码 */}
Soldier.prototype.防御 = function () { /* 防御的代码 */}

var s = new Soldier({name: 'John', id:1, 肤色:'yellow'})

于是,上述代码避开了生产环境中不能使用的 __proto__,实现了继承。

我们再来看下面代码:

function A(){
    this.age = 22
}
A.prototype.行动 = function(){}
B.prototype = Object.create(A.prototype)
var b  = new B()

9a4d4bc1feac74e838bac3f6f6449109.png

可以看到,B 继承了 A 的公有属性,但是并没有继承 A 的自有属性。所以,我们可以使用Object.create() 代替之前代码的假的 Human 函数 FakeHuman ,但是这种写法不支持IE浏览器。

于是,代码在不需要支持 IE 浏览器的情况下,可以变成如下的样子:

function Soldier(options) {
    Human.call(this, options)
    this.id = options.id    // ID 不能重复
    this.生命值 = 42
}

function Human(options){
    this.name = options.name
    this.肤色 = options.肤色
}
Human.prototype.eat = function(){}
Human.prototype.drink = function(){}
Human.prototype.poo = function(){}

// 不兼容 IE的
Soldier.prototype = Object.create(Human.prototype)

Soldier.prototype.兵种 = "美国大兵",
Soldier.prototype.攻击力 = 5
Soldier.prototype.行走 = function () { /* 行走的代码 */}
Soldier.prototype.奔跑 = function () { /* 奔跑的代码 */}
Soldier.prototype.死亡 = function () { /* 死亡的代码 */}
Soldier.prototype.攻击 = function () { /* 攻击的代码 */}
Soldier.prototype.防御 = function () { /* 防御的代码 */}

var s = new Soldier({name: 'John', id:1, 肤色:'yellow'})

如上,便使用原型链的方式实现了继承。

class实现继承

MDN定义class:类实际上是个“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。

我们将构造函数中的 function 改成 class,将其自有属性写在 constructor 中。

class Human{
    constructor(options){
        this.name = options.name
        this.肤色 = options.肤色
    }
    eat(){}
    drink(){}
    poo(){}
}

class Soldier extends Human {
    constructor(options){
        super(options)
        this.id = options.id    // ID 不能重复
        this.生命值 = 42
        this.兵种 = "美国大兵"
        this.攻击力 = 5
    }
    行走() { /* 行走的代码 */}
    奔跑() { /* 奔跑的代码 */}
    死亡() { /* 死亡的代码 */}
    攻击() { /* 攻击的代码 */}
    防御() { /* 防御的代码 */} 
}
var s = new Soldier({name: 'John', id:1, 肤色:'yellow'})

然而,上述代码中存在两个问题。

  1. 上述定义的Human,我们使用 typeof Human 打印出来的是 function,也就是说是一个假的class,本质还是 function,但是如果我们使用 Human.call() 的时候,会报错 Class constructor Human cannot be invoked without 'new' ,意思是说只能通过 new 去调用。因此我们只能将 class 理解为是一个特殊的函数,只能通过 new 去调用的函数。
  2. class 中的公有属性只能是 function,自有属性都必须写在 constructor 中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值