关于JS继承的详解

这次给大家带来关于js继承的详解,使用js继承的注意事项有哪些,下面就是实战案例,一起来看一下。

ECMAScript 实现继承的方式不止一种。这是因为 JavaScript 中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。可以根据需求决定适合的继承方式。

对象冒充

构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。因为构造函数只是一个函数,所以可使ClassA构造函数成为ClassB的方法,然后调用它。ClassB就会收到ClassA的构造函数中定义的属性和方法。

function ClassA(name) {    this.name = name;    this.sayName = function () {        console.log(this.name);    };}function ClassB(name,age) {    this.classA = ClassA;    this.classA(name);    delete this.classA;    this.age = age;    this.sayAge = function(){        console.log(this.age);    }}var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);tom.sayName();                         //'Tom'jerry.sayName();                       //'Jerry'jerry.sayAge();                        //25console.log(tom instanceof ClassA);    //trueconsole.log(jerry instanceof ClassA);  //falseconsole.log(jerry instanceof ClassB);  //true

登录后复制

所有新属性和新方法都必须在删除了新方法的代码行后定义,因为可能会覆盖超类的相关属性和方法

对象冒充可以实现多重继承
如果存在ClassA和ClassB,这时ClassC想继承这两个类,如下:

function ClassA(name){    this.name = name;    this.sayName = function (){        console.log(this.name);    }}function ClassB(age){    this.age = age;    this.sayAge = function(){        console.log(this.age);    }}function ClassC(name,age){    this.method = ClassA;    this.method(name);        this.method = ClassB;    this.method(age);    delete this.method;}var tom = new ClassC('Tom',25);tom.sayName();                       //'Tom';tom.sayAge();                        //25console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //falseconsole.log(tom instanceof ClassC);  //true

登录后复制

这种实现方式的缺陷是:如果两个类ClassA和ClassB具有同名的属性或方法,ClassB具有高优先级,因为它从后面的类继承。
由于这种继承方法的流行,ECMAScript的第三版为Function对象加入了两个方法,即call()和apply()。

call方法是与经典的对象冒充方法最相似的方法。它的第一个参数用作this的对象,其他参数都直接传递给函数自身

function sayName(prefix) {    console.log(prefix + this.name);};var tom = {};tom.name = "Tom";sayName.call(tom, 'This is ');  //'This is Tom'

登录后复制

函数sayName在对象外定义,但也可以引用this。
call方法改写对象冒充

function ClassA(name){    this.name = name;    this.sayName = function(){        console.log(this.name);    }}function ClassB(name,age){    //this.method = ClassA;    //this.method(name);    //delete this.method;    ClassA.call(this,name);    this.age = age;    this.sayAge = function (){        console.log(this.age);    }}var tom = new ClassB('Tom',25);tom.sayName();                       //'Tom'tom.sayAge();                        //25console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //true

登录后复制

call方法替代了使用属性引用ClassA的方式。

apply

apply方法有两个参数,用作this的对象和要传递给函数的参数数组

function sayName(prefex,mark) {    console.log(prefex+ this.name+ mark);};var tom = {};tom.name = 'Tom';sayName.apply(tom, ['This is ','!']);  //'This is Tom!'

登录后复制

同样可以使用apply改写对象冒充

function ClassA(name){    this.name = name;    this.sayName = function(){        console.log(this.name);    }}function ClassB(name,age){    ClassA.apply(this,arguments);    this.age = age;    this.sayAge = function (){        console.log(this.age);    }}var tom = new ClassB('Tom',25);tom.sayName();                       //'Tom'tom.sayAge();                        //25  console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //true

登录后复制

只有超类中参数顺序和子类中的参数完全一致时才可以传递参数数组

原型链

prototype对象是个模板,要实例化的对象都以这个模板为基础,prototype对象的任何属性和方法都被传递给这个类的所有实例,原型链就是利用这种功能来实现继承机制。

function ClassA() {}ClassA.prototype.name = 'Tom';ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB() {}ClassB.prototype = new ClassA();var tom = new ClassB();tom.sayName();                       //'Tom'console.log(tom instanceof ClassA);  //trueconsole.log(tom instanceof ClassB);  //true

登录后复制

这里把ClassB的prototype属性设置称ClassA的实例,避免逐个赋值prototpye属性。
在调用ClassA时没有设置参数,因为在原型链中要确保构造函数是无参的。
在原型链中,instanceof的结果也有了变化,对于ClassA和ClassB都返回了true。

因为prototype属性的重指定,子类中的新属性都必须出现在prototype被赋值后。

function ClassA() {}ClassA.prototype.name = 'Tom';ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB() {}ClassB.prototype = new ClassA();ClassB.prototype.age = 25;ClassB.prototype.sayAge = function () {    console.log(this.age);};var tom = new ClassA();var jerry = new ClassB();tom.sayName();                         //'Tom'jerry.sayName();                       //'Tom'jerry.name = 'Jerry';tom.sayName();                         //'Tom'jerry.sayName();                       //'Jerry'jerry.sayAge();                        //25console.log(tom instanceof ClassA);    //trueconsole.log(jerry instanceof ClassA);  //trueconsole.log(jerry instanceof ClassB);  //true

登录后复制

原型链的缺陷是不能实现多重继承,因为类的prototype会被重写。

混合方式

对象冒充的问题是必须使用构造函数方式,而使用原型链就无法使用带参数的构造函数,不过,可以试试两者结合。
用对象冒充继承构造函数的属性,用原型链继承prototype的方法。

function ClassA(name) {    this.name = name;}ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB(name, age) {    ClassA.call(this, name);    this.age = age;}ClassB.prototype = new ClassA();ClassB.prototype.sayAge = function () {    console.log(this.age);};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(tom instanceof ClassA);                    //trueconsole.log(jerry instanceof ClassA);                  //trueconsole.log(jerry instanceof ClassB);                  //trueconsole.log(jerry.constructor === ClassA);             //trueconsole.log(ClassB.prototype.constructor === ClassA);  //true

登录后复制

在ClassB构造函数中用对象冒充继承了ClassA的name属性,用原型链继承了ClassA的sayName方法,由于使用了原型链继承方式,instanceof运行方式正常。
但是constructor属性暴露出了问题,每一个prototype对象都有一个constructor属性指向它的构造函数,而ClassB实例的构造函数却指向了ClassA,这会导致继承链的错乱。可以手动修改constructor的指向。

function ClassA(name) {    this.name = name;}ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB(name, age) {    ClassA.call(this, name);    this.age = age;}ClassB.prototype = new ClassA();ClassB.prototype.constructor = ClassB;ClassB.prototype.sayAge = function () {    console.log(this.age);};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(tom instanceof ClassA);                    //trueconsole.log(jerry instanceof ClassA);                  //trueconsole.log(jerry instanceof ClassB);                  //trueconsole.log(ClassA.constructor === ClassB);            //falseconsole.log(jerry.constructor === ClassA);             //falseconsole.log(ClassB.prototype.constructor === ClassA);  //false

登录后复制

直接继承原型链

为了节省内存,可以不执行创建ClassA实例,直接让ClassB的原型指向ClassA的原型

function ClassA(name) {    this.name = name;}ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB(name, age) {    ClassA.call(this, name);    this.age = age;}ClassB.prototype = ClassA.prototype;ClassB.prototype.constructor = ClassB;ClassB.prototype.sayAge = function () {    console.log(this.age);};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //trueconsole.log(ClassA.prototype.constructor === ClassB);   //true

登录后复制

这样的缺陷是由于直接修改原型链指向,对于ClassB原型链中的属性也会影响到ClassA上,于是就出现了ClassA具有sayAge方法、ClassA的构造函数属性为ClassB。

空对象作中介

为解决直接继承原型链的缺点,可以利用一个空对象作为中介。

function ClassA(name) {    this.name = name;}ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB(name, age) {    ClassA.call(this, name);    this.age = age;}var fn = function(){};fn.prototype = ClassA.prototype;ClassB.prototype = new fn();ClassB.prototype.constructor = ClassB;ClassB.prototype.sayAge = function () {    console.log(this.age);};console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //falseconsole.log(ClassA.prototype.constructor === ClassB);    //false

登录后复制

虽然还是创建了对象实例,但由于空对象几乎不占内存,修改ClassB的原型也不会影响到ClassA。

封装成extends方法

function extends(child,parent){    var fn = function (){};    fn.prototype = parent.prototype;    child.prototype = new fn();    child.prototype.constructor = child;    child.super = parent.prototype;}

登录后复制

JS的灵活性使我们可以通过多种方式实现继承,了解其中的原理和实现可以帮助我们在不同的场景中选择适合的方法。

相信看了本文案例你已经掌握了方法,更多精彩请关注【创想鸟】其它相关文章!

推荐阅读:

用JS代码做出弹幕效果

用H5的canvas做出弹幕效果

以上就是关于JS继承的详解的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2783207.html

(0)
上一篇 2025年3月8日 16:46:03
下一篇 2025年3月8日 16:46:15

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 详解js在html中的加载执行顺序

    js在html中的加载执行顺序 1.加载顺序:引入标记的出现顺序, 页面上的js代码是js的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记的出现顺序, 标记里面的或者通过src引入的外部JS,都是按照其语句出现的顺序执…

    编程技术 2025年4月4日
    100
  • HTML和JS实现简单的计算器

    HTML和JS实现计算器功能的也是很容易的,本文主要和大家分享HTML和JS实现简单的计算器,希望能帮助到大家。 下面是代码:    nbsp;html>无标题文档 var result=””; function jisuan(num…

    编程技术 2025年4月4日
    100
  • 实现在HTML页面加载完毕后运行js方法

    本文主要和大家分享实现在HTML页面加载完毕后运行js方法,主要以代码的方法和大家分享,希望能帮助到大家。 Js方法: window.onload=function (){ var userName=”xiaoming”; alert(us…

    编程技术 2025年4月4日
    100
  • django控件及传参使用详解

    这次给大家带来djangodjango及传参使用详解,django控件及传参使用的django有哪些,下面就是实战案例,一起来看一下。 本文对djangoHTML的表单控件中的单选及多选进行介绍,并说明如何进行参数传递。 1.HTML中的表…

    编程技术 2025年4月4日
    200
  • H5离线应用与客户端存储使用详解

    这次给大家带来H5离线应用与客户端存储使用详解,使用H5离线应用与客户端存储的注意事项有哪些,下面就是实战案例,一起来看一下。 支持离线 Web 应用开发是 HTML5 的另一个重点。所谓离线 Web 应用,就是在设备不能上网的情况下仍然可…

    编程技术 2025年4月4日
    200
  • pushState与replaceState使用步骤详解

    这次给大家带来pushState与replaceState使用步骤详解,pushState与replaceState使用的注意事项有哪些,下面就是实战案例,一起来看一下。 一、简介 HTML5引入了 history.pushState() …

    编程技术 2025年4月4日
    100
  • JavaScript实现获取远程的html到当前页面中

    今天做个项目,需要在当前的html页面中引用一个远程的html页面,百度了一下,发现一个非常好用的代码,这里分享给大家,有相同需求的小伙伴可以来看看 html代码 登录后复制 立即学习“Java免费学习笔记(深入)”; javascript…

    编程技术 2025年4月4日
    200
  • JavaScript模块优化

    给大家分享一下如何才能开发出更好的javascript模块和功能,有兴趣的朋友参考学习下吧。 不少人都曾经在 npm 上发布过自己开发的 JavaScript 模块,而在使用一些模块的过程中,我经常产生“这个模块很有用,但如果能 xxx 就…

    编程技术 2025年4月4日
    100
  • Java 框架在其他编程语言中是否易于集成?

    java 框架可在多语言环境中集成,受平台兼容性、语言支持和中间层影响。平台兼容性:java 框架在 jvm 上运行,与 jvm 兼容的语言兼容。语言支持:部分 java 框架支持其他语言,如 groovy 和 kotlin。中间层:jna…

    2025年4月2日
    100
  • Laravel中where方法的基本用法详解

    Laravel中where方法的基本用法详解 Laravel是一款流行的PHP开发框架,提供了丰富的数据库操作方法,其中where方法是常用的一个之一。本文将详细介绍Laravel中where方法的基本用法,通过具体的代码示例来帮助读者更好…

    2025年4月2日
    100

发表回复

登录后才能评论