首页 > js教程 > 正文

关于JS继承的详解

原创 2018-03-13 17:31:30 0 2
这次给大家带来关于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的灵活性使我们可以通过多种方式实现继承,了解其中的原理和实现可以帮助我们在不同的场景中选择适合的方法。

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

用JS代码做出弹幕效果

用H5的canvas做出弹幕效果

以上就是关于JS继承的详解的详细内容,更多请关注php中文网其它相关文章!

  • 相关标签:javascript 详解 继承
  • 本文原创发布php中文网 ,转载请注明出处,感谢您的尊重!
  • 相关文章


  • 原生javascript实现分享到朋友圈功能 支持ios和android_javascript技巧
  • jQuery链式调用与show知识浅析_jquery
  • JS实现登录页面记住密码和enter键登录方法推荐_javascript技巧
  • BootstrapTable与KnockoutJS相结合实现增删改查功能【一】_javascript技巧
  • 网友评论

    文明上网理性发言,请遵守 新闻评论服务协议

    我要评论
    独孤九贱(4)_PHP视频教程

    独孤九贱(4)_PHP视频教程

    江湖传言:PHP是世界上最好的编程语言。真的是这样吗?这个梗究竟是从哪来的?学会本课程,你就会明白了。 PHP中文网出品的PHP入门系统教学视频,完全从初学者的角度出发,绝不玩虚的,一切以实用、有用...

    独孤九贱(5)_ThinkPHP5视频教程

    独孤九贱(5)_ThinkPHP5视频教程

    ThinkPHP是国内最流行的中文PHP开发框架,也是您Web项目的最佳选择。《php.cn独孤九贱(5)-ThinkPHP5视频教程》课程以ThinkPHP5最新版本为例,从最基本的框架常识开始,将...

    独孤九贱(1)_HTML5视频教程

    独孤九贱(1)_HTML5视频教程

    《php.cn原创html5视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了HTML知识。 ...

    ThinkPHP5实战之[教学管理系统]

    ThinkPHP5实战之[教学管理系统]

    本套教程,以一个真实的学校教学管理系统为案例,手把手教会您如何在一张白纸上,从零开始,一步一步的用ThinkPHP5框架快速开发出一个商业项目。

    PHP入门视频教程之一周学会PHP

    PHP入门视频教程之一周学会PHP

    所有计算机语言的学习都要从基础开始,《PHP入门视频教程之一周学会PHP》不仅是PHP的基础部分更主要的是PHP语言的核心技术,是学习PHP必须掌握的内容,任何PHP项目的实现都离不开这部分的内容,通...

    作者信息

    相关视频教程

  • javascript初级视频教程 javascript初级视频教程
  • jquery 基础视频教程 jquery 基础视频教程
  • javascript三级联动视频教程 javascript三级联动视频教程
  • 独孤九贱(3)_JavaScript视频教程 独孤九贱(3)_JavaScript视频教程
  • 独孤九贱(6)_jQuery视频教程 独孤九贱(6)_jQuery视频教程
  • 相关视频章节