详解JavaScript中基于原型prototype的继承特性

作者:函数茶馆

时间:2026-10-30

浏览:496

本文深入解析JavaScript基于原型的继承机制。通过原型链实现继承时,存在引用类型共享和无法向超类构造函数传参两大缺陷。文章详细演示了原型链的实现方式、instanceof与isPrototypeOf的用法,并探讨了使用对象字面量重写原型的风险。此外,还介绍了鉴别原型属性、构造函数结合原型模式、Object.create对象继承及模块模式等实践技巧,帮助开发者理解并规避常见陷阱。

文章目录

JavaScript原型继承概述

JavaScript的继承机制较为特殊,它不支持接口继承,仅能依赖原型继承来实现。

原型链机制与实现

原型链是继承的核心机制。原型本质上是一个对象,通过构造函数创建的实例会拥有一个内部指针指向该原型,从而获取原型的属性和方法。此时,实例对象同时具备构造函数的属性和原型的属性方法。若需实现继承,可将另一个构造函数的原型指向该实例,从而使新构造函数拥有原实例的所有属性和方法。具体实现逻辑可参考以下代码示例:


//声明超类,通过构造函数和原型添加有关属性和方法
function Super(){
  this.property = true;
}
Super.prototype.getSuperValue = function() {
  return this.property;
};

//声明子类的构造函数
function SubType() {
  this.subproperty = false;
}
//将子类的原型指向超类的实例,得到超类的一切
SubType.prototype = new Super();
SubType.prototype.constructor = SubType;
SubType.prototype.getSubValue = function(){
  return this.subproperty;
};
//由子类创建对象,测试是否继承超类方法和属性
var instance = new SubType();
console.log(instance.getSuperValue());

原型关系判断

所有函数的默认原型均为Object的实例,因此默认原型内部均包含一个指向Object.prototype的指针。可以通过instanceof运算符和isPrototypeOf方法来判断原型与实例之间的关系:


instance instanceof Object;
Object.prototype.isPrototypeOf(instance);

原型链使用的注意事项

在使用原型链时,定义方法需格外谨慎。若子类需要重写或扩充超类型的方法,必须确保这些操作在替换原型的语句之后执行,否则修改将不会生效。此外,应避免使用对象字面量语法来创建原型方法,因为这会导致原型链被重写:


......
SubType.prototype = new Super();
SubType.prototype = {
  ....
};

原型链继承的缺陷分析

上述代码会更换指针指向新对象,从而破坏了原有的原型链。原型链继承方式存在明显缺陷,主要体现在两个方面: 第一,包含引用类型值原型属性会被所有实例共享。前文已提及,若原型属性包含引用类型值,修改一个实例会导致其他实例同步变化,因此通常建议在构造函数中定义属性。而在原型链继承中,无论超类属性定义在构造函数还是原型中,最终都会被子类实例继承,进而影响所有子类实例。 第二,在创建子类型实例时,无法向超类型的构造函数传递参数。原型链继承直接将子类原型指向超类的实例,此时可以向超类传递参数。然而,当创建子类实例时,只能向子类的构造函数传递参数,无法向超类的构造函数传递参数。 鉴于这些限制,实际开发中很少单独使用原型链继承。

相关代码实践

相关的一些代码实践

鉴别原型属性

鉴别一个原型属性


function hasPrototypeProperty(object, name) {
  return name in object && !object.hasOwnProperty(name);
}

构造函数与原型对象结合

在构造函数中使用原型对象


function Person(name) {
  this.name = name;
}

Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name);
  },
  toString: function() {

  }
};

var person1 = new Person('Nicholas');
var person2 = new Person('Greg);

console.log(person1 instanceof Person); // true
console.log(person1.constructor === Person); // true
console.log(person1.constructor === Object); // false

console.log(person2 instanceof Person); // true
console.log(person2.constructor === Person); // true
console.log(person2.constructor === Object); // false

对象继承方式

对象继承


var person1 = {
  name: 'Nicholas',
  sayName: function () {
    console.log(this.name);
  }
};

var person2 = Object.create(person1, {
  name: {
    configurable: true,
    enumerable: true,
    value: 'Greg',
    writable: true
  }
});

person1.sayName(); // Nicholas
person2.sayName(); // Greg

console.log(person1.hasOwnProperty('sayName')); // true
console.log(person1.isPropertyOf(person2)); // true
console.log(person2.hasOwnProperty('sayName')); // false

模块模式应用

模块模式


var person = (function () {
  var age = 25;

  function getAge() {
    return age;
  }

  function growOlder() {
    age++;
  }

  return {
    name: 'Nicholas',
    getAge: getAge,
    growOlder: growOlder
  };
}());

作用域内的构造函数

作用域的构造函数


function Person(name) {
  this.name = name;
}

Person.prototype.sayName = function() {
  console.log(this.name);
};

var person1 = Person('Nicholas');

console.log(person1 instanceof Person); // false
console.log(typeof person1); // undefined
console.log(name); // Nicholas

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章