比如,现在有一个"动物"对象的构造函数。 function Animal(){ this.species = "动物"; } 还有一个"猫"对象的构造函数。 function Cat(name,color){ this.name = name; this.color = color; } 怎样才能使"猫"继承"动物"呢? 一、 构造函数绑定 第一种方法也是最简单的方法,使用call或apply方法,将父对象的构造函数绑定在子对象上,即在子对象构造函数中加一行: function Cat(name,color){ Animal.apply(this, arguments); this.name = name; this.color = color; } var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 二、 prototype模式 第二种方法更常见,使用prototype属性。 如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。 Cat.prototype = new Animal(); Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。 Cat.prototype = new Animal(); 它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢? Cat.prototype.constructor = Cat; 原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。如果没有"Cat.prototype = new Animal();"这一行,Cat.prototype.constructor是指向Cat的;加了这一行以后,Cat.prototype.constructor指向Animal。 alert(Cat.prototype.constructor == Animal); //true 更重要的是,每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性。 alert(cat1.constructor == Cat.prototype.constructor); // true 因此,在运行"Cat.prototype = new Animal();"这一行之后,cat1.constructor也指向Animal! alert(cat1.constructor == Animal); // true 这显然会导致继承链的紊乱(cat1明明是用构造函数Cat生成的),因此我们必须手动纠正,将Cat.prototype对象的constructor值改为Cat。这就是第二行的意思。 这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象, o.prototype = {}; 那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。 o.prototype.constructor = o; 三、 直接继承prototype 第三种方法是对第二种方法的改进。由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。 现在,我们先将Animal对象改写: function Animal(){ } Animal.prototype.species = "动物"; 然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。 Cat.prototype = Animal.prototype; Cat.prototype.constructor = Cat; var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。 所以,上面这一段代码其实是有问题的。请看第二行 Cat.prototype.constructor = Cat; 这一句实际上把Animal.prototype对象的constructor属性也改掉了! alert(Animal.prototype.constructor); // Cat 四、 利用空对象作为中介 由于"直接继承prototype"存在上述的缺点,所以就有第四种方法,利用一个空对象作为中介。 var F = function(){}; F.prototype = Animal.prototype; Cat.prototype = new F(); Cat.prototype.constructor = Cat; F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。 alert(Animal.prototype.constructor); // Animal 我们将上面的方法,封装成一个函数,便于使用。 function extend(Child, Parent) { var F = function(){}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; Child.uber = Parent.prototype; } 使用的时候,方法如下 extend(Cat,Animal); var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 这个extend函数,就是YUI库如何实现继承的方法。 另外,说明一点,函数体最后一行 Child.uber = Parent.prototype; 意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。(uber是一个德语词,意思是"向上"、"上一层"。)这等于在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。 五、 拷贝继承 上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?这样我们就有了第五种方法。 首先,还是把Animal的所有不变属性,都放到它的prototype对象上。 function Animal(){} Animal.prototype.species = "动物"; 然后,再写一个函数,实现属性拷贝的目的。 function extend2(Child, Parent) { var p = Parent.prototype; var c = Child.prototype; for (var i in p) { c[i] = p[i]; } c.uber = p; } 这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。 使用的时候,这样写: extend2(Cat, Animal); var cat1 = new Cat("大毛","黄色"); alert(cat1.species); // 动物 (本系列未完,请继续阅读第三部分《非构造函数的继承》。) (完)
相关推荐
JavaScript 的 9 种继承实现方式归纳 JavaScript 中的继承方式是基于原型的,与基于类的编程语言,如 C++ 和 Java,截然不同。JavaScript 的灵活性使得其实现继承的方式非常多样化。本文将介绍 JavaScript 中的 9 ...
Javascript中的几种继承方式对比分析_.docx
在JavaScript中,实现继承的几种方式可以带来不同的好处和权衡,主要包括原型链继承、构造函数继承、组合继承以及原型式继承等。 首先,原型链继承是基于原型对象的属性共享机制。每个对象都有一个指向其原型对象的...
在JavaScript中,由于其动态性质,继承的实现方式与传统的面向对象语言有所不同。本文将详细介绍JavaScript中的六种继承方式,包括它们的工作原理以及实际应用示例。 #### 二、原型链继承 **1. 原理概述** 原型链...
下面,我们将探讨在Javascript中实现继承的几种主要方法,并对每种方法进行详细的解释。 1. 原型链继承 原型链继承是Javascript中最基本的继承方式。它利用了原型对象的特性,即所有对象实例共享同一个原型对象上的...
**原理**: 在JavaScript中,原型链继承是一种实现继承的方式,它通过让子类的原型对象指向父类的一个实例来实现属性和方法的共享。 - **核心概念**: 每个函数都具有一个`prototype`属性,该属性默认是一个空的对象...
本文通过分析JavaScript的继承机制,提供了几种继承的实现方式,并分析了各自的优缺点和使用场景。同时,通过分析John Resig开发的知名开源框架中的核心源代码,展示了继承机制在JavaScript框架级项目开发中的应用。...
JavaScript定义类的几种方式包括工厂方式、构造函数和原型方式,每种方法都有其特点和适用场景,接下来我们将详细探讨。 工厂方式是最早期的面向对象编程在JavaScript中的实践之一。使用工厂方式定义类,本质上就是...
在JavaScript中,对象可以通过原型链实现继承,这是JavaScript中实现面向对象编程的重要方式。本文将详细介绍JavaScript中的三种主要继承方法:原型链继承、构造函数继承以及组合继承。 1. **原型链继承**: - ...
这里提到的"工具函数二"可能是介绍了一种或多种继承模式的实现方式。下面我们将详细讨论这些概念: 1. **原型链继承**:这是JavaScript默认的继承方式,通过`__proto__`或`Object.create()`来实现。子对象的原型被...
随后,文章介绍了几种主流的继承方式: 1. 原型链继承是JavaScript中最基本的继承方式。它涉及到一个子类型的原型被设置为一个父类型实例的操作。通过这种方式,子类型可以继承父类型的属性和方法。但是,原型链...
在JavaScript中实现类的方式主要有几种,包括对象字面量(Object Literal)、构造函数(Constructor)、原型链(Prototype Chain)以及ES6引入的类语法。每种方式都有其适用的场景和特点,理解它们有助于我们更好地...
Javascript原型继承是一个被说烂掉了的话题,但是自己对于这个问题一直没有彻底理解,今天花了点时间又看了一遍《Javascript模式》中关于原型实现继承的几种方法,下面来一一说明下,在最后我根据自己的理解提出了一...
### JavaScript继承的三种方法实例详解 #### 一、概述 在JavaScript中,虽然原生语言层面没有提供传统意义上的“类”这一概念,但它...理解并掌握这几种继承方式对于编写高质量、可维护的JavaScript代码至关重要。
在JavaScript中,有多种实现继承的方式,每种都有其优缺点,适用于不同的场景。 首先,我们来看**原型链继承**。这种方式是通过将父类的实例赋值给子类的原型来实现的。例如: ```javascript function Person(name...
以下就是几种在JavaScript中实现继承的方式。 对象冒充(Pseudoclassical Inheritance): 对象冒充是利用构造函数和函数的call方法来实现的。构造函数使用this关键字定义属性和方法,而call方法则可以调用指定对象...
在JavaScript中,有多种实现继承的方式,主要包括原型链继承、构造继承、组合继承以及寄生组合继承。下面将详细阐述这四种方式。 1. **原型链继承** 原型链继承是JavaScript最基础的继承方式,它利用了原型对象的...
本文将深入探讨JavaScript中的几种继承方式,包括它们的基本原理、优缺点以及适用场景。 首先,原型链继承是JavaScript中最早实现继承的方式。它基于原型链机制,即每个对象都有一个`__proto__`属性,指向创建该...
JavaScript继承的实现方式多样,除了原型链之外,还有如下几种常见方式: 1. **构造函数继承**:通过调用父构造函数来初始化子对象,但不能避免方法重复。 2. **原型链继承**:通过修改子构造函数的原型使其指向父...