首页 web前端 前端问答 javascript继承有什么用

javascript继承有什么用

Jan 18, 2022 pm 01:48 PM
javascript 继承

javascript继承的作用:1、可以不调用“父类”的构造方法就创造新的实例;2、修改“父类”的prototype可以动态修改所有已经创造的实例;3、可以动态修改一个对象的原型。

javascript继承有什么用

本教程操作环境:windows7系统、javascript1.8.5版、Dell G3电脑。

JavaScript 是以对象为基础,以函数为模型,以原型为继承的面向对象开发模式。

javascript继承的作用:

1、可以不调用「父类」的构造方法就创造新的实例

2、修改「父类」的 prototype 可以动态修改所有已经创造的实例

3、可以动态修改一个对象的原型,也就是说爹妈可以换

下面介绍定义 JavaScript 类型的方法,以及实现继承的常用模式。

构造原型

直接使用 prototype 原型设计类的继承存在两个问题。

由于构造函数事先声明,而原型属性在类结构声明之后才被定义,因此无法通过构造函数向原型动态传递参数。这样实例化对象都是一个模样,没有个性。要改变原型属性值,则所有实例都会受到干扰。

当原型属性的值为引用类型数据时,如果在一个对象实例中修改该属性值,将会影响所有的实例。

示例1

简单定义 Book 类型,然后实例化。

function Book () {};  //声明构造函数
Book.prototype.o = {x : 1, y : 2};  //构造函数的原型属性o是一个对象
var book1 = new Book ();  //实例化对象book1
var book2 = new Book ();  //实例化对象book2
console.log(book1.o.x);  //返回1
console.log(book2.o.x);  //返回1
book2.o.x = 3;  //修改实例化对象book2中的属性x的值
console.log(book1.o.x);  //返回3
console.log(book2.o.x);  //返回3
登录后复制

由于原型属性 o 为一个引用型的值,所以所有实例的属性 o 的值都是同一个对象的引用,一旦 o 的值发生变化,将会影响所有实例。

构造原型正是为了解决原型模式而诞生的一种混合设计模式,它把构造函数模式与原型模式混合使用,从而避免了上述问题的发生。

实现方法:对于可能会相互影响的原型属性,并且希望动态传递参数的属性,可以把它们独立出来使用构造函数模式进行设计。对于不需要个性设计、具有共性的方法或属性,则可以使用原型模式来设计。

示例2

遵循上述设计原则,把其中两个属性设计为构造函数模式,设计方法为原型模式。

function Book (title, pages) {  //构造函数模式设计
    this.title = title;
    this.pages = pages;
}
Book.prototype.what = function () {  //原型模式设计
    console.log(this.title + this.pages);
};
var book1 = new Book("JavaScript 程序设计", 160);
var book2 = new Book("C语言程序设计", 240);
console.log(book1.title);
console.log(book2.title);
登录后复制

构造原型模式是 ECMAScript 定义类的推荐标准。一般建议使用构造函数模式定义所有属性,使用原型模式定义所有方法。这样所有方法都只创建一次,而每个实例都能够根据需要设置属性值。这也是使用最广的一种设计模式。

动态原型

根据面向对象的设计原则,类型的所有成员应该都被封装在类结构体内。例如:

function Book (title, pages) {  //构造函数模式设计
    this.title = title;
    this.pages = pages;
    Book.prototype.what = function () {  //原型模式设计,位于类的内部
        console.log(this.title + this.pages);
    };
}
登录后复制

但当每次实例化时,类 Book 中包含的原型方法就会被重复创建,生成大量的原型方法,浪费系统资源。可以使用 if 判断原型方法是否存在,如果存在就不再创建该方法,否则就创建方法。

function Book (title, pages) {
    this.title = title;
    this.pages = pages;
    if (typeof Book.isLock == "undefined") {  //创建原型方法的锁,如果不存在则创建
        Book.prototype.what = function () {
            console.log(this.title + this.pages);
        };
        Book.isLock = true;  //创建原型方法后,把锁锁上,避免重复创建
    }
}
var book1 = new Book("JavaScript 程序设计", 160);
var book2 = new Book("C语言程序设计", 240);
console.log(book1.title);
console.log(book2.title);
登录后复制

typeof Book.isLock 表达式能够检测该属性值的类型,如果返回为 undefined 字符串,则不存在该属性值,说明没有创建原型方法,并允许创建原型方法,设置该属性的值为 true,这样就不用重复创建原型方法。这里使用类名 Book,而没有使用 this,这是因为原型是属于类本身的,而不是对象实例的。

动态原型模式与构造原型模式在性能上是等价的,用户可以自由选择,不过构造原型模式应用比较广泛。

工厂模式

工厂模式是定义类型的最基本方法,也是 JavaScript 最常用的一种开发模式。它把对象实例化简单封装在一个函数中,然后通过调用函数,实现快速、批量生产实例对象。

示例1

下面示例设计一个 Car 类型:包含汽车颜色、驱动轮数、百公里油耗 3 个属性,同时定义一个方法,用来显示汽车颜色。

function Car (color, drive, oil) {  //汽车类
    var _car =  new Object();  //临时对象
        _car.color = color;  //初始化颜色
        _car.drive = drive;  //初始化驱动轮数
        _car.oil = oil;  //初始化百公里油耗
        _car.showColor = function () {  //方法,提示汽车颜色
            console.log(this.color);
        };
        return _car;  //返回实例
}
var car1 = Car("red", 4, 8);
var car2 = Car("blue", 2, 6);
car1.showColor();  //输出“red”
car2.showColor();  //输出“blue”
登录后复制

上面代码是一个简单的工厂模式类型,使用 Car 类可以快速创建多个汽车实例,它们的结构相同,但是属性不同,可以初始化不同的颜色、驱动轮数和百公里油耗。

示例2

在类型中,方法就是一种行为或操作,它能够根据初始化参数完成特定任务,具有共性。因此,可以考虑把方法置于 Car() 函数外面,避免每次实例化时都要创建一次函数,让每个实例共享同一个函数。

function showColor () {  //公共方法,提示汽车颜色
    console.log(this.color);
};
function Car (color, drive, oil) {  //汽车类
    var _car = new Object();  //临时对象
        _car.color = color;  //初始化颜色
        _car.drive = drive;  //初始化驱动轮数
        _car.oil = oil;  //初始化百公里油耗
        _car.showColor = showColor;  //引用外部函数
    return _car;  //返回实例
}
登录后复制

在上面这段重写的代码中,在函数 Car() 之前定义了函数 showColor()。在 Car() 内部,通过引用外部 showColor() 函数,避免了每次实例化时都要创建一个新的函数。从功能上讲,这样解决了重复创建函数的问题;但是从语义上讲,该函数不太像是对象的方法。

类继承

类继承的设计方法:在子类中调用父类构造函数。

在 JavaScript 中实现类继承,需要注意以下 3 个技术问题。

在子类中,使用 apply 调用父类,把子类构造函数的参数传递给父类父类构造函数。让子类继承父类的私有属性,即 Parent.apply(this, arguments); 代码行。

在父类和子类之间建立原型链,即 Sub.prototype = new Parent(); 代码行。通过这种方式保证父类和子类是原型链上的上下级关系,即子类的 prototype 指向父类的一个实例。

恢复子类的原型对象的构造函数,即 Sub.prototype.constructor=Sub;语句行。当改动 prototype 原型时,就会破坏原来的 constructor 指针,所以必须重置 constructor。

示例1

下面示例演示了一个三重继承的案例,包括基类、父类和子类,它们逐级继承。

//基类Base
function Base (x) {  //构造函数Base
    this.get = function () {  //私有方法,获取参数值
        return x;
    }
}
Base.prototype.has = function () {  //原型方法,判断get()方法返回值是否为0
    return ! (this.get() == 0);
}
//父类Parent
function Parent () {  //构造函数Parent
    var a = [];  //私有数组a
    a = Array.apply(a, arguments);  //把参数转换为数组
    Base.call(this, a.length);  //调用Base类,并把参数数组长度传递给它
    this.add = function () {  //私有方法,把参数数组补加到数组a中并返回
        return a.push.apply(a, arguments)
    }
    this.geta = function () {  //私有方法,返回数组a
        return a;
    }
}
Parent.prototype = new Base();  //设置Parent原型为Base的实例,建立原型链
Parent.prototype.constructor = Parent;  //恢复Parent类原型对象的构造器
Parent.prototype.str = function (){  //原型方法,把数组转换为字符串并返回
    return this.geta().toString();
}
//子类Sub
function Sub () {  //构造函数
    Parent.apply(this, arguments);  //调用Parent类,并把参数数组长度传递给它
    this.sort = function () {  //私有方法,以字符顺序对数组进行排序
        var a = this.geta();  //获取数组的值
        a.sort.apply(a, arguments);  //调用数组排序方法 sort()对数组进行排序
    }
}
Sub.prototype = new Parent();  //设置Sub原型为Parent实例,建立原型链
Sub.prototype.constructor = Sub;  //恢复Sub类原型对象的构造器
//父类Parent的实例继承类Base的成员
var parent = new Parent (1, 2, 3, 4);  //实例化Parent类
console.log(parent.get());  //返回4,调用Base类的方法get()
console.log(parent.has());  //返回true,调用Base类的方法has()
//子类Sub的实例继承类Parent和类Base的成员
var sub = new Sub (30, 10, 20, 40);  //实例化Sub类
sub.add(6, 5);  //调用Parent类方法add(),补加数组
console.log(sub.geta());  //返回数组30,10,20,40,6,5
sub.sort();  //排序数组
console.log(sub.geta());  //返回数组10,20,30,40,5,6
console.log(sub.get());  //返回4,调用Base类的方法get()
console.log(sub.has());  //返回true,调用Base类的方法has()
console.log(sub.str());  //返回10,20,30,40,5,6
登录后复制

【设计思路】

设计子类 Sub 继承父类 Parent,而父类 Parent 又继承基类 Base。Base、Parent、Sub 三个类之间的继承关系是通过在子类中调用的构造函数来维护的。

例如,在 Sub 类中,Parent.apply(this, arguments); 能够在子类中调用父类,并把子类的参数传递给父类,从而使子类拥有父类的所有属性。

同理,在父类中,Base.call(this, a.length); 把父类的参数长度作为值传递给基类,并进行调用,从而实现父类拥有基类的所有成员。

从继承关系上看,父类继承了基类的私有方法 get(),为了确保能够继承基类的原型方法,还需要为它们建立原型链,从而实现原型对象的继承关系,方法是添加语句行 Parent.prototype=new Base();。

同理,在子类中添加语句 Sub.prototype=new Parent();,这样通过原型链就可以把基类、父类和子类串连在一起,从而实现子类能够继承父类属性,还可以继承基类的属性。

示例2

下面尝试把类继承模式封装起来,以便规范代码应用。

function extend (Sub, Sup) {  //类继承封装函数
    var F = function () {};  //定义一个空函数
    F.prototype = Sup.prototype;  //设置空函数的原型为父类的原型
    Sub.prototype = new F ();  //实例化空函数,并把父类原型引用传给给子类
    Sub.prototype.constructor = Sub;  //恢复子类原型的构造器为子类自身
    Sub.sup = Sup.prototype;  //在子类定义一个私有属性存储父类原型
    //检测父类原型构造器是否为自身
    if (Sup.prototype.constructor == Object.prototype.constructor) {
        Sup.prototype.constructor = Sup;  //类继承封装函数
    }
}
登录后复制

【操作步骤】

1) 定义一个封装函数。设计入口为子类和父类对象,函数功能是子类能够继承父类的所有原型成员,不涉及出口。

function extend (Sub, Sup) {  //类继承封装函数
    //其中参数Sub表示子类,Sup表示父类
}
登录后复制

2) 在函数体内,首先定义一个空函数 F,用来实现功能中转。设计它的原型为父类的原型,然后把空函数的实例传递给子类的原型,这样就避免了直接实例化父类可能带来的系统负荷。因为在实际开发中,父类的规模可能会很大,如果实例化,会占用大量内存。

3) 恢复子类原型的构造器为子类自己。同时,检测父类原型构造器是否与 Object 的原型构造器发生耦合。如果是,则恢复它的构造器为父类自身。

下面定义两个类,尝试把它们绑定为继承关系。

function A (x) {  //构造函数A
    this.x = x;  //私有属性x
    this.get = function () {  //私有方法get()
        return this.x;
    }
}
A.prototype.add = function () {  //原型方法add()
    return this.x + this.x;
}
A.prototype.mul = function () {  //原型方法mul()
    return this.x * this.x;
}
function B (x) {  //构造函数B
    A.call (this.x);  //在函数体内调用构造函数A,实现内部数据绑定
}
extend (B, A);  //调用封装函数,把A和B的原型捆绑在一起
var f = new B (5);  //实例化类B
console.log(f.get());  //继承类A的方法get(),返回5
console.log(f.add());  //继承类A的方法add(),返回10
console.log(f.mul());  //继承类A的方法mul(),返回25
登录后复制

在函数类封装函数中,有这么一句 Sub.sup=Sup.prototype;,在上面代码中没有被利用,那么它有什么作用呢?为了解答这个问题,先看下面的代码。

extend (B, A);
B.prototype.add = function () {  //为B类定义一个原型方法
    return this.x + "" + this.x;
}
登录后复制

上面的代码是在调用封装函数之后,再为 B 类定义了一个原型方法,该方法名与基类中原型方法 add() 同名,但是功能不同。如果此时测试程序,会发现子类 B 定义的原型方法 add() 将会覆盖父类 A 的原型方法 add()。

console.log(f.add());  //返回字符串55,而不是数值10
登录后复制

如果在 B 类的原型方法 add() 中调用父类的原型方法 add(),避免代码耦合现象发生。

B.prototype.add = function () {  //定义子类B的原型方法add()
    return B.sup.add.call(this);  //在函数内部调用父类方法add()
}
登录后复制

【相关推荐:javascript学习教程

以上是javascript继承有什么用的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

C++ 函数继承详解:如何在继承中使用'基类指针”和'派生类指针”? C++ 函数继承详解:如何在继承中使用'基类指针”和'派生类指针”? May 01, 2024 pm 10:27 PM

在函数继承中,使用“基类指针”和“派生类指针”来理解继承机制:基类指针指向派生类对象时,执行向上转型,只访问基类成员。派生类指针指向基类对象时,执行向下转型(不安全),必须谨慎使用。

C++ 中继承和多态性如何影响类的耦合度? C++ 中继承和多态性如何影响类的耦合度? Jun 05, 2024 pm 02:33 PM

继承和多态性会影响类的耦合度:继承会增加耦合度,因为派生类依赖于基类。多态性可以降低耦合度,因为对象可以通过虚函数和基类指针以一致的方式响应消息。最佳实践包括谨慎使用继承、定义公共接口、避免向基类添加数据成员,以及通过依赖注入解耦类。实战案例展示了如何使用多态性和依赖注入降低银行账户应用程序中的耦合度。

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

C++ 函数继承详解:如何调试继承中出现的错误? C++ 函数继承详解:如何调试继承中出现的错误? May 02, 2024 am 09:54 AM

继承错误调试技巧:确保正确的继承关系。使用调试器逐步执行代码,检查变量值。确保正确使用virtual修饰符。检查隐藏的继承带来的菱形继承问题。检查抽象类中未实现的纯虚函数。

如何在JavaScript中获取HTTP状态码的简单方法 如何在JavaScript中获取HTTP状态码的简单方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP状态码获取方法简介:在进行前端开发中,我们常常需要处理与后端接口的交互,而HTTP状态码就是其中非常重要的一部分。了解和获取HTTP状态码有助于我们更好地处理接口返回的数据。本文将介绍使用JavaScript获取HTTP状态码的方法,并提供具体代码示例。一、什么是HTTP状态码HTTP状态码是指当浏览器向服务器发起请求时,服务

C++ 函数继承详解:如何理解继承中的'is-a”和'has-a”关系? C++ 函数继承详解:如何理解继承中的'is-a”和'has-a”关系? May 02, 2024 am 08:18 AM

C++函数继承详解:掌握“is-a”和“has-a”关系什么是函数继承?函数继承是C++中一种将派生类中定义的方法与基类中定义的方法关联起来的技术。它允许派生类访问和重写基类的方法,从而扩展了基类的功能。“is-a”和“has-a”关系在函数继承中,“is-a”关系指派生类是基类的子类型,也就是说,派生类“继承”了基类的特性和行为。“has-a”关系指派生类包含对基类对象的引用或指针,也就是说,派生类“拥有”了基类对象。语法以下是如何实现函数继承的语法:classDerivedClass:pu

C++ 函数继承详解:什么时候不应使用继承? C++ 函数继承详解:什么时候不应使用继承? May 04, 2024 pm 12:18 PM

在以下情况下不应使用C++函数继承:派生类需要不同实现时,应创建具有不同实现的新函数。派生类不需要函数时,应声明为一个空类或使用私有、未实现的基类成员函数来禁用函数继承。函数不需要继承时,应使用其他机制(例如模板)来实现代码重用。

'PHP面向对象编程入门:从概念到实践” 'PHP面向对象编程入门:从概念到实践” Feb 25, 2024 pm 09:04 PM

什么是面向对象编程?面向对象编程(OOP)是一种编程范式,它将现实世界中的实体抽象为类,并使用对象来表示这些实体。类定义了对象的属性和行为,而对象则实例化了类。OOP的主要优点在于它可以使代码更易于理解、维护和重用。OOP的基本概念OOP的主要概念包括类、对象、属性和方法。类是对象的蓝图,它定义了对象的属性和行为。对象是类的实例,它具有类的所有属性和行为。属性是对象的特征,它可以存储数据。方法是对象的函数,它可以对对象的数据进行操作。OOP的优点OOP的主要优点包括:可重用性:OOP可以使代码更

See all articles