不同于基于类的编程语言,如 C++ 和 Java,JavaScript 中的继承方式是基于原型的。同时由于 JavaScript 是一门非常灵活的语言,其实现继承的方式也非常多。 首要的基本概念是关于构造函数和原型链的,父对象的构造函数称为Parent,子对象的构造函数称为Child,对应的父对象和子对象分别为parent和child。 对象中有一个隐藏属性[[prototype]](注意不是prototype),在 Chrome 中是__proto__,而在某些环境下则不可访问,它指向的是这个对象的原型。在访问任何一个对象的属性或方法时,首先会搜索本对象的所有属性,如果找不到的话则会根据[[ JavaScript的继承机制是基于原型(Prototype)的,与C++和Java等基于类的语言不同。在JavaScript中,每个对象都有一个[[Prototype]]属性,通常在Chrome等浏览器中可以通过`__proto__`访问,用于指向对象的原型。当访问对象的一个属性时,如果对象本身没有该属性,就会沿着原型链向上查找,直到找到该属性或到达原型链顶端(即`null`),如果还找不到,则返回`undefined`。 以下是对JavaScript中9种继承实现方式的详细解析: 1. **原型链继承**: 这是最基础的继承方式,通过将子对象构造函数的`prototype`属性设置为父对象的一个实例来实现。例如: ```javascript function Parent() {} function Child() {} Child.prototype = new Parent(); Child.prototype.constructor = Child; ``` 但这种方式有一个问题,就是每次创建子类时都会创建一个新的父类实例,可能导致资源浪费。 2. **原型继承(非原型链)**: 为避免上述问题,可以直接让子对象的`prototype`指向父对象的`prototype`,但这样会导致共享同一对象,修改一方会影响到另一方。 ```javascript Child.prototype = Parent.prototype; Child.prototype.constructor = Child; ``` 3. **临时构造器继承**: 通过创建一个临时构造器`F`,使`F.prototype`等于`Parent.prototype`,然后让`Child.prototype`指向`new F()`的实例,这样就解决了共享问题。 ```javascript function extend(Child, Parent) { var F = function() {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; } ``` 这种方式可以确保每个子类都有自己的原型副本,而不会影响父类原型。 4. **属性拷贝**: 直接遍历父对象的`prototype`,将属性复制到子对象的`prototype`,但仅适用于基本类型,对象类型是引用复制。 ```javascript function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } } ``` 5. **使用Object.create()**: ES5引入的方法,直接在子对象中使用`Object.create(Parent.prototype)`来创建原型链。 ```javascript function Child() {} Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; ``` 6. **构造函数继承(call/apply)**: 在子类构造函数中调用父类构造函数,利用`call`或`apply`将`this`指向子类实例,从而在子类实例上执行父类构造函数。 ```javascript function Child(name) { Parent.call(this, name); } Child.prototype = new Parent(); Child.prototype.constructor = Child; ``` 7. **组合继承**: 结合了原型链继承和构造函数继承,既调用了父类构造函数,又将父类的`prototype`赋值给了子类的`prototype`。 ```javascript function Child(name) { Parent.call(this, name); } Child.prototype = new Parent(); Child.prototype.constructor = Child; ``` 8. **寄生组合式继承**: 通过创建父类的非实例化副本来避免父类构造函数的额外开销。 ```javascript function extend(Child, Parent) { var F = function() {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; Parent.call(Child.prototype); // 调用父类构造函数 } ``` 9. **使用ES6的Class语法**: ES6引入的`class`关键字提供了更简洁的语法,但底层仍然是基于原型链实现。 ```javascript class Parent { constructor(name) { this.name = name; } } class Child extends Parent { constructor(name) { super(name); } } ``` `super`关键字用于调用父类的方法或构造函数。 每种继承方式都有其适用场景和优缺点,选择哪种取决于具体需求,如性能、可读性、扩展性等因素。在实际开发中,通常会结合使用多种策略,以达到最佳效果。
- 粉丝: 4
- 资源: 971
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助