JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承

  说好的讲解JavaScript继承,可是迟迟到现在讲解。废话不多说,直接进入正题。

  既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考《面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式》,接下来讲一般通过那些方法完成JavaScript的继承。

  原型链

  JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即“子类型.prototype = new 父类型();”,实现方法如下:

// 为父类型创建构造函数
function SuperType() {
    this.name = [‘wuyuchang‘, ‘Jack‘, ‘Tim‘];
    this.property = true;
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
    return this.property;
}

// 为子类型创建构造函数
function SubType() {
    this.test = [‘h1‘, ‘h2‘, ‘h3‘, ‘h4‘];
    this.subproperty = false;
}

// 实现继承的关键步骤,子类型的原型指向父类型的实例
SubType.prototype = new SuperType();

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

/* 以下为测试代码示例 */
var instance1 = new SubType();
instance1.name.push(‘wyc‘);
instance1.test.push(‘h5‘);
alert(instance1.getSuerperValue());        // true
alert(instance1.getSubValue());            // false
alert(instance1.name);                    // wuyuchang,Jack,Tim,wyc
alert(instance1.test);                    // h1,h2,h3,h4,h5

var instance2 = new SubType();
alert(instance2.name);                    // wuyuchang,Jack,Tim,wyc
alert(instance2.test);                    // h1,h2,h3,h4

  可以看到如上的代码就是通过原型链实现的一个简单的继承,但看到测试代码示例中还是存在些问题。相信看了我的博文《面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式》的童鞋一定知道原型链代码存在的第一个问题是由于子类型的原型是父类型的实例,也就是子类型的原型中包含的父类型的属性,从而导致引用类型值的原型属性会被所有实例所共享。以上代码的instance1.name.push(‘wyc‘);就可以证明此问题的存在。而原型链的第二个问题就是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。因此我们在实际开发中,很少单独使用原型链。

  借用构造函数

  为了解决原型链中存在的两个问题,开发人员开始使用一种叫做借用构造函数的技术来解决原型链中存在的问题。这种技术的实现思路也挺简单,只需要在子类型的构造函数内调用父类型的构造函数即可。别忘了,函数只不过是在特定环境中执行代码的对象,因此可以通过apply()或call()方法执行构造函数。代码如下:

// 为父类型创建构造函数
function SuperType(name) {
    this.name = name;
    this.color = [‘pink‘, ‘yellow‘];
    this.property = true;

    this.testFun = function() {
        alert(‘http://www.cnblogs.com/wuyuchang/‘);
    }
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
    return this.property;
}

// 为子类型创建构造函数
function SubType(name) {
    SuperType.call(this, name);
    this.test = [‘h1‘, ‘h2‘, ‘h3‘, ‘h4‘];
    this.subproperty = false;
}

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

/* 以下为测试代码示例 */
var instance1 = new SubType([‘wuyuchang‘, ‘Jack‘, ‘Nick‘]);
instance1.name.push(‘hello‘);
instance1.test.push(‘h5‘);
instance1.color.push(‘blue‘);
instance1.testFun();                        // http://www.cnblogs.com/wuyuchang/
alert(instance1.name);                        // wuyuchang,Jack,Nick,hello
// alert(instance1.getSuerperValue());        // error 报错
alert(instance1.test);                        // h1,h2,h3,h4,h5
alert(instance1.getSubValue());                // false
alert(instance1.color);                        // pink,yellow,blue

var instance2 = new SubType(‘wyc‘);
instance2.testFun();                        // http://www.cnblogs.com/wuyuchang/
alert(instance2.name);                        // wyc
// alert(instance2.getSuerperValue());        // error 报错
alert(instance2.test);                        // h1,h2,h3,h4
alert(instance2.getSubValue());                // false
alert(instance2.color);                        // pink,yellow

  可以看到以上代码中子类型SubType的构造函数内通过调用父类型"SuperType.call(this, name);",从而实现了属性的继承,也可以在子类型创建实例的时候为父类型传递参数了,但新的问题又来了。可以看到我在父类型的构造函数中定义了一个方法:testFun,在父类型的原型中定义了一个方法:getSuperValue。可是在实例化子类型后仍然是无法调用父类型的原型中定义的方法getSuperValue,只能调用父类型中构造函数的方法:testFun。这就同创建对象中只使用构造函数模式一样,使得函数没有复用性可言。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

  

  组合继承(原型链+借用构造函数)

  顾名思义,组合继承就是结合使用原型链与借用构造函数的优点,组合而成的一个模式。实现也很简单,既然是结合,那当然结合了两方的优点,即原型链继承方法,而在构造函数继承属性。具体代码实现如下:

// 为父类型创建构造函数
function SuperType(name) {
    this.name = name;
    this.color = [‘pink‘, ‘yellow‘];
    this.property = true;

    this.testFun = function() {
        alert(‘http://www.cnblogs.com/wuyuchang/‘);
    }
}

// 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
    return this.property;
}

// 为子类型创建构造函数
function SubType(name) {
    SuperType.call(this, name);
    this.test = [‘h1‘, ‘h2‘, ‘h3‘, ‘h4‘];
    this.subproperty = false;
}

SubType.prototype = new SuperType();

// 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

/* 以下为测试代码示例 */
var instance1 = new SubType([‘wuyuchang‘, ‘Jack‘, ‘Nick‘]);
instance1.name.push(‘hello‘);
instance1.test.push(‘h5‘);
instance1.color.push(‘blue‘);
instance1.testFun();                        // http://www.cnblogs.com/wuyuchang/
alert(instance1.name);                        // wuyuchang,Jack,Nick,hello
alert(instance1.getSuerperValue());            // true
alert(instance1.test);                        // h1,h2,h3,h4,h5
alert(instance1.getSubValue());                // false
alert(instance1.color);                        // pink,yellow,blue

var instance2 = new SubType(‘wyc‘);
instance2.testFun();                        // http://www.cnblogs.com/wuyuchang/
alert(instance2.name);                        // wyc
alert(instance2.getSuerperValue());            // true
alert(instance2.test);                        // h1,h2,h3,h4
alert(instance2.getSubValue());                // false
alert(instance2.color);                        // pink,yellow

  以上代码通过SuperType.call(this, name);继承父类型的属性,通过SubType.prototype = new SuperType();继承父类型的方法。以上代码很方便的解决了原型链与借用构造函数所遇到的问题,成为了JavaScript中最为常用的实例继承的方法。但混合模式也并非没有缺点,可以看到在以上代码中在继承方法的时候实际已经继承了父类型的属性,只不过此时对于引用类型属于共享的,因此在子类型的构造函数内在次调用父类型的构造函数从而继承了父类型的属性而去覆盖了原型中所继承的属性,这样调用两次构造函数显然没有必要,但有什么方法可以解决呢?在解决此问题时先看以下两个模式。

  

  原型式继承

  原型式继承的的实现方法与普通继承的实现方法不同,原型式继承并没有使用严格意义上的构造函数,而是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。具体代码如下:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

  代码示例:

/* 原型式继承 */
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var person = {
    name : ‘wuyuchang‘,
    friends : [‘wyc‘, ‘Nicholas‘, ‘Tim‘]
}

var anotherPerson = object(person);
anotherPerson.name = ‘Greg‘;
anotherPerson.friends.push(‘Bob‘);

var anotherPerson2 = object(person);
anotherPerson2.name = ‘Jack‘;
anotherPerson2.friends.push(‘Rose‘);

alert(person.friends);    // wyc,Nicholas,Tim,Bob,Rose

  

  寄生式继承

/* 寄生式继承 */
function createAnother(original) {
    var clone = object(original);
    clone.sayHi = function() {
        alert(‘hi‘);
    }
    return clone;
}

  使用示例:

/* 原型式继承 */
function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

/* 寄生式继承 */
function createAnother(original) {
    var clone = object(original);
    clone.sayHi = function() {
        alert(‘hi‘);
    }
    return clone;
}

var person = {
    name : ‘wuyuchang‘,
    friends : [‘wyc‘, ‘Nicholas‘, ‘Rose‘]
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();

  寄生组合式继承

  前面说过了JavaScrip中组合模式实现继承的缺点,现在我们就来解决它的缺点,实现思路是,对于构造函数继承属性,而原型链的混成形式继承方法,即不用在继承方法的时候实例化父类型的构造函数。代码如下:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

/* 寄生组合式继承 */
function inheritPrototype(subType, superType) {
    var prototype = object(superType.prototype);
    prototype.constructor = subType;
    subType.prototype = prototype;
}

  而在使用时只需要将组合模式中的“SubType.prototype = new SuperType();”这行代码替换成inheritPrototype(subType, superType);即可。寄生组合式继承的高效率体现在它只调用了一次父类型构造函数,避免了创建不必要的或多余的属性。与此同时,原型链还能保持不变,因此,还能够正常使用instanceof和isPrototypeof()。这也是目前来说最理想的继承方式了,目前也在向这种模式转型。(YUI也使用了这种模式。)

  此博文参考《JavaScript高级程序设计第3版》,代码为经过改写,更具体,并加了注释使大家更易懂。如对JS继承方面有独到见解的童鞋不别吝啬,回复您的见解供大家参考!

JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承,布布扣,bubuko.com

时间: 2024-10-12 08:01:28

JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承的相关文章

JavaScript构造函数+原型创建对象,原型链+借用构造函数模式继承父类练习

虽然经常说是做前端开发的,但常常使用的技术反而是JQuery比较多一点.在JavaScript的使用上相对而言少些.尤其是在创建对象使用原型链继承上面,在项目开发中很少用到.所以今天做个demo练习一下,以后忘记了也可以照搬一下. 说明一下: 1. Demo中使用的是构造函数+原型模式创建的对象.构造函数中存储对象实例使用的属性,原型模式增加实例使用的方法. 2. Demo中的继承分为两个方面.一个是属性继承,使用的是借用构造函数模式 call()方法.另一个是方法继承,这个就是使用原型方式继承

282 继承模式:原型链继承 : 得到方法,借用构造函数 : 得到属性,组合,new一个对象背后做了些什么

1.原型链继承 : 得到方法 function Parent(){} Parent.prototype.test = function(){}; function Child(){} Child.prototype = new Parent(); // 子类型的原型指向父类型实例 Child.prototype.constructor = Child var child = new Child(); //有test() <!DOCTYPE html> <html lang="e

javascript精髓篇之原型链维护和继承.

一.两个原型 很多人都知道javascript是原型继承,每个构造函数都有一个prototype成员,通过它就可以把javascript的继承演义的美轮美奂了.其实啊,光靠这一个属性是无法完成javascript的继承.我们在代码中使用的prototype完成继承在这里就不多说了.大家可以查一下资料.另外一个看不见的prototype成员.每一个实例都有有一条指向原型的prototype属性,这个属性是无法被访问到的,当然也就无法被修改了,因为这是维护javascript继承的基础. 1 //构

1--面试总结-js深入理解,对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This

参考一手资料:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/中文翻译版本:https://zhuanlan.zhihu.com/p/32042645 Javascript 是一种单线程编程语言,这意味着它只有一个调用栈,call Stack(调用栈 ,,先入后出) 核心:对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This js原型链? 定义 原型对象也是简单的对象并且可以拥有它们自

javascript学习笔记11(原型链)

<script type="text/javascript">    /**     * js实现继承的第一种方式是基于原型链的方式     */    function Parent() {        this.pv = "parent";    }    Parent.prototype.pp = "ok";    Parent.prototype.showParentValue = function() {        a

JavaScript 随笔2 面向对象 原型链 继承

第六章 面向对象的程序设计 1.创建对象的几种方式 A)工厂模式 function CreatObj(name,sex,age){ this.name=name; this.sex=sex; this.age=age; } 缺点:虽然可以批量创建对象,却不能知道对象的类型 只知道他是Object类型: B)构造函数 function Person(name,sex){ this.name=name; this.sex=sex; this.sayName=function(){ alert(thi

JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模)

什么是面向对象?面向对象是一种思想!(废话). 面向对象可以把程序中的关键模块都视为对象,而模块拥有属性及方法.这样我们如果把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.接下来将为大家讲解在JS中面向对象的实现.   工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,而由于在ECMAScript中无法创建类,因此用函数封装以特定接口创建对象.其实现方法非常简单,也就是在函数内创建一个对象,给对象赋予属性及方法再将对象返回即可. ? 1 2 3 4 5 6 7 8

JavaScript对象基础讲解

1.Object对象详解 javascript 里最基本的数据类型是对象. javaScript里的对象其实是一个无序的属性集合,属性又是一个个的名-值对. 除了字符串,数字,true,false,null或者undefined以外,其他所有的值在JavaScript里头都是对象. 对象是引用类型,如果变量x表示一个对象,当执行vary = x;语句后,实际上y和x指向的是同一个对象.所以,当你通过y改变对象的值,这种变化也会反映到x上. Object是在javascript中一个被我们经常使用

javascript 创建对象及对象原型链属性介绍

我们知道javascript里定义一个普通对象的方法,如: let obj = {}; obj.num = 1; obj.string = 'string'; obj.func = function(){alert('func')}; obj.arr = ['x','y']; console.log(obj.num); //1 console.log(obj.string); // "string" console.log(obj.func); //function(){alert('