### JavaScript Prototype 继承详解 #### 一、基本概念与用法 JavaScript 是一种基于原型的语言,这使得它能够以非常灵活的方式实现继承。在 JavaScript 中,可以通过将一个类的实例赋值给另一个类的 `prototype` 属性来实现继承。这种继承方式被称为**原型链继承**。 例如,在给定的示例中,`ClassB` 继承了 `ClassA` 的所有属性: ```javascript function ClassA() { this.a = 'a'; } function ClassB() { this.b = 'b'; } ClassB.prototype = new ClassA(); var objB = new ClassB(); for (var p in objB) document.write(p + "<br>"); ``` 在这个例子中,创建了一个 `ClassA` 的新实例,并将其赋值给 `ClassB.prototype`。这意味着 `ClassB` 的所有实例都将共享 `ClassA` 实例上的属性。当我们遍历 `objB` 的属性时,可以看到它不仅具有自身定义的 `b` 属性,还具有从 `ClassA` 继承而来的 `a` 属性。 #### 二、原型继承的本质 JavaScript 的原型继承本质上是一种**引用传递**,而不是**值传递**。这意味着,当通过 `prototype` 属性进行继承时,子类实际上是引用了父类实例的对象,而不是复制了该对象。这意味着任何对原型对象的修改都会影响到所有继承自该原型的子类实例。 例如: ```javascript function ClassA() { this.a = 'a'; } function ClassB() { this.b = 'b'; } ClassB.prototype = new ClassA(); var objB = new ClassB(); alert(objB.a); // 输出 "a" ClassB.prototype.a = 'changed!!'; alert(objB.a); // 输出 "changed!!" ``` 在这个例子中,我们首先获取了 `objB` 的 `a` 属性,然后修改了 `ClassB.prototype.a` 的值。由于 `objB` 的 `a` 属性实际上引用的是 `ClassB.prototype.a`,所以当我们修改原型属性时,`objB` 的 `a` 属性也会发生变化。 #### 三、子类对象的写操作与读取行为 子类对象的写操作仅会作用于子类对象本身,而不会影响到其他实例或原型对象。这意味着如果我们尝试修改一个继承自原型对象的属性,实际上是在当前对象上添加了一个新的同名属性。 例如: ```javascript function ClassA() { this.a = 'a'; } function ClassB() { this.b = 'b'; } ClassB.prototype = new ClassA(); var objB1 = new ClassB(); var objB2 = new ClassB(); objB1.a = '!!!'; alert(objB1.a); // 输出 "!!!" alert(objB2.a); // 输出 "a" ``` 这里,`objB1.a` 被赋值为 `"!!!"`,但实际上这是在 `objB1` 上添加了一个新的属性 `a`。因此,`objB2` 仍然保留了从 `ClassA` 继承的原始 `a` 属性。 #### 四、子类对象中的原型成员 所有子类对象都持有相同的原型对象引用,因此,它们实际上共享同一组原型成员。这意味着,对于函数类型的成员,所有子类实例指向的都是同一个函数对象。 例如: ```javascript function ClassA() { this.a = function () { alert(); }; } function ClassB() { this.b = function () { alert(); }; } ClassB.prototype = new ClassA(); var objB1 = new ClassB(); var objB2 = new ClassB(); alert(objB1.a == objB2.a); // 输出 "true" alert(objB1.b == objB2.b); // 输出 "false" ``` 在这个例子中,`objB1` 和 `objB2` 的 `a` 函数实际上是指向同一个函数对象的,而 `b` 函数则是各自独立的。 #### 五、构造子类时原型的构造函数不被执行 当使用 `new` 关键字构造一个子类的实例时,原型对象的构造函数并不会被调用。这意味着 `ClassA` 的构造函数不会被执行。 例如: ```javascript function ClassA() { alert("a"); this.a = function () { alert(); }; } function ClassB() { alert("b"); this.b = function () { alert(); }; } ClassB.prototype = new ClassA(); var objB1 = new ClassB(); var objB2 = new ClassB(); ``` 这里,虽然 `ClassB.prototype` 是 `ClassA` 的一个实例,但构造 `objB1` 和 `objB2` 时,并没有调用 `ClassA` 的构造函数。 #### 六、访问原型的成员对象问题 当子类对象访问原型中的成员对象时,这些成员对象实际上是所有子类实例所共有的。这意味着,对其中任何一个成员对象的修改会影响到所有的子类实例。 例如: ```javascript function ClassA() { this.a = []; } function ClassB() { this.b = function () { alert(); }; } ClassB.prototype = new ClassA(); var objB1 = new ClassB(); var objB2 = new ClassB(); objB1.a.push(1, 2, 3); alert(objB2.a); // 输出 "[1, 2, 3]" ``` 在这个例子中,我们修改了 `objB1` 的 `a` 属性,但这个变化也反映到了 `objB2` 的 `a` 属性上,因为它们共享同一个数组对象。 #### 七、总结与建议 - **避免在原型中使用成员对象**:如上所述,应避免在原型对象中使用成员对象,以免引发意外的副作用。 - **优先使用值类型数据**:在原型中使用字符串或其他简单数据类型是比较安全的做法。 - **利用 prototype 继承的优点**:利用 prototype 继承可以提高执行效率并减少内存占用,同时还能方便地为父类添加新方法。 理解 JavaScript 的原型继承机制对于有效地使用 JavaScript 进行面向对象编程至关重要。通过深入掌握这些概念,开发者可以更高效地构建复杂的应用程序。
- 粉丝: 6
- 资源: 967
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 基于Flink商品实时推荐系统详细文档+全部资料.zip
- 基于flink流数据处理平台详细文档+全部资料.zip
- 基于flink实时流计算任务详细文档+全部资料.zip
- 基于Flink实现实时冰蝎(Behinder)流量检测详细文档+全部资料.zip
- 基于Flink实现的商品实时推荐系统、flink统计商品热度,放入redis缓存,分析日志信息,将画像标签和实时记录放入Hbase,根据用户画像重排序热度榜,并
- 基于flink统计实时ip热点统计详细文档+全部资料.zip
- 基于flink用户画像的基本代码详细文档+全部资料.zip
- 基于Flink用户埋点行为日志分析平台,项用户行为日志收集、存储、分析平台,支持用户自定义查询条件详细文档+全部资料.zip
- 基于flink最新的[FLIP-27]架构对MQTT connector的实现详细文档+全部资料.zip
- 基于Flink与Storm的流式计算详细文档+全部资料.zip
- 远卓—深圳庄维房产—庄维年度培训计划申请表1206.doc
- 远卓—深圳庄维房产—庄维培训实施申请表1206.doc
- 远卓—深圳庄维房产—庄维培训计划制定流程V0.95why1206.doc
- 远卓—深圳庄维房产—庄维年度培训计划申请表V0.95why1206.doc
- 远卓—深圳庄维房产—庄维培训计划制定流程1206.doc
- 远卓—深圳庄维房产—庄维员工培训制度1206.doc