简单记录两种js实现继承方式的比较

方案A

// 寄生组合式继承
function clone(parent, child) {
    // 使用Object.create()方法创建一个新的对象,并将其原型指向父类的原型对象
    child.prototype = Object.create(parent.prototype);
    child.prototype.constructor = child;
}

// 父类
function Parent() {
    this.name = '我是父类中的name';
    this.play = ['打篮球', '踢足球'];
}

// 父类原型方法
Parent.prototype.getName = function () {
    return this.name;
}

// 子类
function Child() {
    Parent.call(this);
    this.friend = '我是子类中的friend';
}

// 寄生组合式继承
clone(Parent, Child);

// 子类原型方法
Child.prototype.getFriend = function () {
    return this.friend;
}

let child1 = new Child();
let child2 = new Child();
console.log(child1.getName()); // 我是父类中的name
console.log(child1.getFriend()); // 我是子类中的friend
console.log(child1.play); // ['打篮球', '踢足球']
child1.play.push('打羽毛球');
console.log(child1.play); // ['打篮球', '踢足球', '打羽毛球']
console.log(child2.play); // ['打篮球', '踢足球']

方案B:

// 寄生组合式继承
function clone(parent, child) {
    // 创建一个中间函数F,避免直接修改父类的原型对象
     function F() {}
     F.prototype = parent.prototype;
     child.prototype = new F();
     child.prototype.constructor = child;

}

// 父类
function Parent() {
    this.name = '我是父类中的name';
    this.play = ['打篮球', '踢足球'];
}

// 父类原型方法
Parent.prototype.getName = function () {
    return this.name;
}

// 子类
function Child() {
    Parent.call(this);
    this.friend = '我是子类中的friend';
}

// 寄生组合式继承
clone(Parent, Child);

// 子类原型方法
Child.prototype.getFriend = function () {
    return this.friend;
}

let child1 = new Child();
let child2 = new Child();
console.log(child1.getName()); // 我是父类中的name
console.log(child1.getFriend()); // 我是子类中的friend
console.log(child1.play); // ['打篮球', '踢足球']
child1.play.push('打羽毛球');
console.log(child1.play); // ['打篮球', '踢足球', '打羽毛球']
console.log(child2.play); // ['打篮球', '踢足球']

增加一下小案例,给自己方便记忆

// 1. 定义父类
function Parent(name) {
    this.name = name;
}
Parent.prototype.sayName = function() {
    console.log(`Parent says my name is: ${this.name}`);
};

// 2. 定义子类,并使用 Object.create 的第二个参数
function Child(name, age) {
    // 借用构造函数继承父类实例属性
    Parent.call(this, name);
    this.age = age;
}

// 核心代码:建立原型链 + 同时定义子类原型属性
Child.prototype = Object.create(Parent.prototype, {
    // 属性 1:普通方法(注意:默认情况下,通过这种方式定义的属性,enumerable 为 false)
    sayHello: {
        value: function() {
            console.log(`Hello! I am ${this.name}, ${this.age} years old.`);
        },
        writable: true, // 是否可以修改
        enumerable: false, // 不可枚举(for...in 遍历不到)
        configurable: true // 
    },
    
    // 属性 2:使用 getter/setter
    ageInfo: {
        get: function() {
            return `${this.name} is ${this.age} years old.`;
        },
        enumerable: true,
        configurable: true
    }
});

// 别忘了修正 constructor 指向(因为 Object.create 创建的是纯净空对象)
Child.prototype.constructor = Child;

// 3. 测试运行
const child1 = new Child('张三', 18);

// 测试父类原型方法
child1.sayName(); 
// 输出: Parent says my name is: 张三

// 测试通过第二参数定义的子类方法
child1.sayHello(); 
// 输出: Hello! I am 张三, 18 years old.

// 测试 getter 属性
console.log(child1.ageInfo); 
// 输出: 张三 is 18 years old.

// 验证 constructor 指向
console.log(child1.constructor === Child); // 输出: true

// 验证不可枚举特性
for (let key in child1) {
    console.log(key); 
}
// 输出: name, age, ageInfo 
// (注意:sayHello 没有打印出来,因为它是不可枚举的)

emm,这坨东西是真的麻烦,我直接一手ES6的 extends 语法糖,直接解决

posted on 2026-07-11 21:36  中文还在写码  阅读(4)  评论(0)    收藏  举报