zoukankan      html  css  js  c++  java
  • module1-03-JS6种继承方式

    JS6种继承方式

    继承的意义

    • 继承是面向对象的,使用这个方式可以让我们更好的复用代码,缩短开发周期,提升开发效率

    思考题

    • ① JS的继承方式到底有多少种实现方式呢?

    • ② ES5的 extends 关键字是用哪种继承方式实现的呢?

    一、原型链继承

    • 原型链继承是比较常见的继承方式之一

    • 让一段代码来感受一下

    (1)案例感受

    function Parent1() {
    this.name = 'parent1';
    this.play = [1, 2, 3]
    }
    function Child1() {
    this.type = 'child2';
    }
    Child1.prototype = new Parent1();
    console.log(new Child1());
    • 用代码测试一下

    var s1 = new Child2();
    var s2 = new Child2();
    s1.play.push(4);
    console.log(s1.play, s2.play);
    • 结果

    (2)优缺点分析

    • 实现简单,方便快捷

    • 虽然父类的方法和属性都能够访问,但其实有一个潜在的问题,比如上面那样,只改变了一个实例,而另一个实例也跟着改变。

      • 因为内存空间是共享的

      • 当一个发生变化的时候另一个也跟着发生变化

    二、构造函数继承(call)

    (1)案例感受

    function Parent1(){
    this.name = 'parent1';
    }

    Parent1.prototype.getName = function () {
    return this.name;
    }

    function Child1(){
    Parent1.call(this);
    this.type = 'child1'
    }

    let child = new Child1();
    console.log(child);  // 没问题
    console.log(child.getName());  // 会报错
    • 结果

    (2)优缺点分析

    • 子类可以拿到父类实例的属性和方法

    • 但是子类拿不到父类原型的属性和方法

    三、组合继承

    • 结合了原型继承和构造函数继承的优缺点

    (1)案例感受

    function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
    }

    Parent3.prototype.getName = function () {
    return this.name;
    }
    function Child3() {
    // 第二次调用 Parent3()
    Parent3.call(this);
    this.type = 'child3';
    }

    // 第一次调用 Parent3()
    Child3.prototype = new Parent3();
    // 手动挂上构造器,指向自己的构造函数
    Child3.prototype.constructor = Child3;
    var s3 = new Child3();
    var s4 = new Child3();
    s3.play.push(4);
    console.log(s3.play, s4.play);  // 不互相影响
    console.log(s3.getName()); // 正常输出'parent3'
    console.log(s4.getName()); // 正常输出'parent3'
    • 结果

    (2)优缺点分析

    • 很好解决了上述两个方法的问题

    • 但是会调用两次父类,造成多的性能开销

    四、原型式继承

    • 上面说的更多的是构造函数(类)的继承,那么围绕对象的继承又有哪些呢

    • 说起原型式继承不得不提到 ES5 的 Object.create 方法

      • 接受两个参数

        • ① 用作新对象的原型对象

        • ② 为新对象定义额外属性的对象

    (1)案例感受

    let parent4 = {
       name: "parent4",
       friends: ["p1", "p2", "p3"],
       getName: function() {
         return this.name;
      }
    };

    let person4 = Object.create(parent4);
    person4.name = "tom";
    person4.friends.push("jerry");

    let person5 = Object.create(parent4);
    person5.friends.push("lucy");

    console.log(person4.name);
    console.log(person4.name === person4.getName());
    console.log(person5.name);
    console.log(person4.friends);
    console.log(person5.friends);
    • 结果

    (2)优缺点

    • 可以实现对象的浅拷贝

    • 但是对于多个对象使用该方法的话,会出现共享问题

    五、寄生式继承

    • 在上一个方法的基础上进行优化的一个继承方法

    • 原理:

      • 使用原型式继承可以获得一份目标对象的浅拷贝,然后利用这个浅拷贝的能力在进行增强,添加一些方法

    (1)案例感受

    let parent5 = {
       name: "parent5",
       friends: ["p1", "p2", "p3"],
       getName: function() {
         return this.name;
      }
    };

    function clone(original) {
       let clone = Object.create(original);
       clone.getFriends = function() {
         return this.friends;
      };
       return clone;
    }

    let person5 = clone(parent5);

    console.log(person5.getName());
    console.log(person5.getFriends());
    • 结果

    (2)优缺点

    • 相比原型式继承,可以添加方法,从而使对象在继承的过程中又添加了一个方法

    • 缺点跟原型式继承一样,也只能实现浅拷贝

    六、寄生组合式继承

    • 在前面这几种继承方式的优缺点基础上进行改造,这是所有继承方式里面相对最优的继承方式

    (1)案例感受

      function clone (parent, child) {
       // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
       child.prototype = Object.create(parent.prototype);
       child.prototype.constructor = child;
    }

     function Parent6() {
       this.name = 'parent6';
       this.play = [1, 2, 3];
    }
      Parent6.prototype.getName = function () {
       return this.name;
    }
     function Child6() {
       Parent6.call(this);
       this.friends = 'child5';
    }

     clone(Parent6, Child6);

     Child6.prototype.getFriends = function () {
       return this.friends;
    }

     let person6 = new Child6();
     console.log(person6);
     console.log(person6.getName());
     console.log(person6.getFriends());
    • 结果

    (2)优缺点

    • 很好的结合以上的优点,较少了构造函数的调用次数,减少了性能开销

    • es6的语法糖extends

    (3)总结脑图

    七、ES6的extends关键字实现逻辑

    • 使用关键词很容易就实现JS的继承,但是巷深入了解extends语法糖怎么实现,就要深入研究extends的底层逻辑

    (1)es6中extends使用方法

    class Person {
     constructor(name) {
       this.name = name
    }
     // 原型方法
     // 即 Person.prototype.getName = function() { }
     // 下面可以简写为 getName() {...}
     getName = function () {
       console.log('Person:', this.name)
    }
    }
    class Gamer extends Person {
     constructor(name, age) {
       // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
       super(name)
       this.age = age
    }
    }
    const asuna = new Gamer('Asuna', 20)
    asuna.getName() // 成功访问到父类的方法
    • 在babel编译成es5的代码

    function _possibleConstructorReturn (self, call) { 
    		// ...
    		return call && (typeof call === 'object' || typeof call === 'function') ? call : self; 
    }
    function _inherits (subClass, superClass) { 
        // 这里可以看到
    	subClass.prototype = Object.create(superClass && superClass.prototype, { 
    		constructor: { 
    			value: subClass, 
    			enumerable: false, 
    			writable: true, 
    			configurable: true 
    		} 
    	}); 
    	if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 
    }
    
    var Parent = function Parent () {
    	// 验证是否是 Parent 构造出来的 this
    	_classCallCheck(this, Parent);
    };
    var Child = (function (_Parent) {
    	_inherits(Child, _Parent);
    	function Child () {
    		_classCallCheck(this, Child);
    		return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));
    }
    	return Child;
    }(Parent));
    
  • 相关阅读:
    页面登陆系统--带文字验证码
    Django-form表单
    Ajax 异步局部刷新
    Django认证系统auth认证
    cookie与session
    使用lorem在HTML中生成随机文本
    request模块
    java——第五天-面向对象OOP
    java——第四天
    java——第三天
  • 原文地址:https://www.cnblogs.com/lezaizhu/p/14455026.html
Copyright © 2011-2022 走看看