JS类_类的实例_继承

类, 类的实例,类的构造函数,类的成员函数/类原型

1: 类: 具有相同类型的一类实例的逻辑描述;
2: 类的实例: 被构造函数出来的具有这个类的实例特征的一个表;
3: 类的成员函数: 完成对应的逻辑,通过this来操作不通的实例;

//  “function Person”叫做类的构造函数
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// “.get_age”叫做类的方法,又叫做类的成员函数
// 用 this 操作类的实例来完成特定的逻辑;
Person.prototype.get_age = function() {
    return this.age;
}

Person.prototype.get_name = function() {
    return this.name;
}
// end

var xiaoming = new Person("xiaoming", 12);

//new 函数的作用相当于手写了下面这样的一个表:
var my_person = {
    //数据
        name: "xxxxx",
    age: 12,
        
        //成员函数
    __proto__: {
        get_name: Person.prototype.get_name,
        get_age: Person.prototype.get_age,
    }
};

//“xiaohong”叫做类的实例
var xiaohong = new Person("xiaohong", 13);
var xiaotian = new Person("xiaotian", 13);

console.log(xiaotian);
console.log(xiaohong);
console.log(xiaoming);


xiaotian.get_name();
xiaoming.get_name();
xiaotian.get_name();

// xiaotian, xiaoming, xiaohong 者三个实例都有相同的结构:
// 1:每个人实例 都有 name, age,
// 2: 每个实例都有一个 (函数)方法get_name, get_age;
// 他们这三个实例,属于同一个类型;
// 或者说他们三个实例,都是Person类
// 把Person看作是一个类, xiaotian, xiaoming,xiaohong可以看作是类的 3个实例;

举一个例子:新建一个Enemy.js文件,写入

// 类
function Enemy(name, level) {
    this.name = name;
    this.level = level;
}

// “Enemy.prototype”这个表我们又把他叫做“类原型”;
Enemy.prototype.attack_player = function() {
    console.log("attack_player", this);
}
// 类结束

module.exports = Enemy;
在 main.js 文件中
var Enemy = require("./Enemy")

var e1 = new Enemy("e1", 1);
e1.attack_player();

var e2 = new Enemy("e2", 2);
e2.attack_player();

继承

为何要用继承?

为了扩展“类的原型”,类的原型不需要构造函数里的实例

例1:继承机制: 猫的代码---> 波斯猫的代码(在猫的代码的基础上扩展波斯猫的代码
例2:敌人--> 扩展成特殊的敌人。在原来敌人的基础上--> 继承原来的代码, --> 再添加;


实现继承,例如:

  1. new 机制使得 Cat 的空表"__ proto __"被创建,并携带参数 name "Black Cat"
  2. 然后化身为函数 Cat 中的 this
  3. 接着被 .call 显式传递给函数 Animal 中的 this,并携带参数name "Black Cat"
  4. 执行函数 Animal,意味着向 Cat 表中添加和函数 Animal相同地 key 和 value
  5. 打印出 "Black Cat"
function Animal(name){
    this.name = name;
    this.showName = function(){
        console.log(this.name);
    }
}

function Cat(name){
    Animal.call(this, name);
}

var cat = new Cat("Black Cat");
cat.showName();

// step1: 创建一个BossEnemy的构造函数;

function BossEnemy(name, level) {
--------------------------------------------------------------------------------
    // 调用基类的构造函数
    // 如何继承“构造函数里的成员”?
    // new 一个 "BossEnemy",就是在表 BossEnemy 下新建一个类原型__proto__空表
    // 因为在 new 机制下,函数 BossEnemy 下的 this,就是类原型__proto__空表
    // 接着被 .call 显式传递给构造函数 Enemy,构造函数 Enemy 下的 this,就被传递为表 BossEnemy 下的类原型__proto__空表
    // 执行构造函数 Enemy,将构造函数 Enemy 下的实例,添加到表 BossEnemy 下的类原型__proto__空表
    // 又因为在 new 机制,会返回表 BossEnemy 下的类原型__proto__表
    // 当 var boss = new BossEnemy("通天教主", 10) 时
    // 意思是表 BossEnemy 下的类原型__proto__空表,携带着由参数name, level 分别传过来的 "通天教主", 10
    // 被传入到构造函数 Enemy 中,执行的内容是向类原型__proto__空表里添加数据
    // key = name,value = 参数name
    // 所以最终返回的表,就是 attack_player Enemy { name: '通天教主', level: 10, blood: 100 }
    Enemy.call(this, name, level) //这里的this 是 new BossEnemy() 的空表

    // 如何扩展自己的成员?
    this.blood = 100;
}

// step2: 把 Enemy 里面所有的原型方法,都复制过来;

// 注意:不能直接这样写。当我们修改BossEnemy.prototype原型的时候,同时也会修改到Enemy.prototype。
// 因为他们两个变量都指向同一个“表的实例”,所以要新建一个表
// BossEnemy.prototype = Enemy.prototype; 

--------------------------------------------------------------------------------
// 如何继承“构造函数 Enemy 的表 prototype”?
// 写法1
BossEnemy.prototype = {};
for(var i in Enemy.prototype) {
    BossEnemy.prototype[i] = Enemy.prototype[i];
}

// “规范的”写法2
// 通过下面这两行,var a 就相当于构造函数
// 要继承基类 Enemy.prototype,只继承 prototype。
// 如果直接 new 一个Enemy,除了继承它的 prototype
// 还会把函数 Enemy 里的数据(this.xxx) 全都拿过来。

var a = function() {}
//a.prototype 指向 Enemy.prototype
a.prototype = Enemy.prototype;
// 用 new 函数4个步骤,把类的原型 Enemy.prototype 复制到表__proto__中。
BossEnemy.prototype = new a(); // {__proto__: Enemy原型}

--------------------------------------------------------------------------------
// 如何扩展原型?
// 获取到原型以后,就继承了 Enemy 的原型方法;
BossEnemy.prototype.boss_attack = function() {
    console.log("boss_attack");
}

var boss = new BossEnemy("通天教主", 10);
// 表的访问
boss.boss_attack();
boss.attack_player();

--------------------------------------------------------------------------------
// 重载
// 根据优先找到表 BossEnemy.prototype.attack_player 的,会把Enemy.prototype.attack_player 冲掉
BossEnemy.prototype.attack_player = function() {
    //调用基类的 prototype
    Enemy.prototype.attack_player.call(this);
// 这里的 this,还是 new BossEnemy 的表
    console.log("BossEnemy get name");
    return this.name;
}

boss.attack_player();
/*
打印出:
attack_player Enemy { name: '通天教主', level: 10, blood: 100 }
BossEnemy get name
*/

Class函数

javascript 没有类,继承语法的, new, this, 模拟,有些地方些了一个继承函数。这个Class函数,能帮我们得到新的类

function Class(class_desic) {
    var new_class = function(name, level) {
        if (class_desic.extend) { // 有基类
            // 调用基类的构造函数
            class_desic.extend.call(this, name, level);
        }
        // 加一个初始化的接口 
        if (class_desic.init) {
            class_desic.init.call(this);
        }
    }

    if (class_desic.extend) {
        var a = function() {};
        a.prototype = class_desic.extend.prototype;
        new_class.prototype = new a();  
    }
    else {
        new_class.prototype = {};   
    }

    for(var i in class_desic) {
        if (i == "extend") {
            continue;
        }
        new_class.prototype[i] = class_desic[i];
    }


    return new_class;
}

var BossEnemy2 = Class({
    // 定义写死的关键字 extend 是继承自那个基类
    extend: Enemy, // 对象的名字

    init: function(){

    },

    boss_attack: function() {

    },

    add_blood: function() {
        console.log("add_blood", this);
/* 
这里的 this 指的也是 b2 的 this
b2 的实例显式的绑定过来
这里的 this 也是指后面调用的实例
*/
    },
});

var b2 = new BossEnemy2("boss2", 1111);
console.log(b2);
b2.add_blood();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。