总结JS继承的六种方式

7 篇文章 0 订阅

1. 继承

        1.1语法

// 父类
 class Father{    } 
// 子类继承父类
 class  Son  extends Father {   }     

例如:

class Father {
       constructor(sonname) {
         this.sonname= sonname;
       }
       say() {
         console.log('你的姓是' + this.sonname);
        }
 }
class Son extends Father{
  // 这样子类就继承了父类的属性和方法
 }
 let yours= new Son('王');
 yours.say();      //结果为 你的姓是王

        子类继承了父类的属性和方法。

        1.2 子类使用super关键字访问父类的方法
 

//定义了父类
 class Father {
    constructor(x, y) {
    this.x = x;
    this.y = y;
    }
    sum() {
    console.log(this.x + this.y);
     }
  }
 //子元素继承父类
     class Son extends Father {
      constructor(x, y) {
            super(x, y); //使用super调用了父类中的构造函数
               }
          }
     var son = new Son(1, 2);
     son.sum(); //结果为3

注意:

  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行 子类的
  2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这 个方法(就近原则)
  3. 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用 父类的构造函数,super 必须在子类this之前调用
// 父类有加法方法
  class Father {
    constructor(x, y) {
    this.x = x;
    this.y = y;
    }
    sum() {
    console.log(this.x + this.y);
    }
  }
  // 子类继承父类加法方法 同时 扩展减法方法
  class Son extends Father {
    constructor(x, y) {
    // 利用super 调用父类的构造函数 super 必须在子类this之前调用,放到 this之后会报错
    super(x, y);
    this.x = x;
    this.y = y;
  }
   sub() {
   console.log(this.x ‐ this.y);
   }
 }
 var son = new Son(5, 3);
 son.sub(); //运行结果为2 调用了子类中定义的相减的方法
 son.sum();//运行结果为8 子类继承了父类中的相加的方法

         1.3 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用. 

  1. constructor中的this指向的是new出来的实例对象
  2. 自定义的方法,一般也指向的new出来的实例对象
  3. 绑定事件之后this指向的就是触发事件的事件源

2.原型链 

  1. 当访问一个对象的属性(包括方法时),首先查找这个对象自身有没有该属性。  
  2. 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
  3.  如果还没有就查找原型对象的原型(Object的原型对象)。依此类推一直找到为null为止。
  4. __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。 

3.call() 

 this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。只不过apply第二个参数必须传入的是一个数组,而call 第二个参数可以是任意类型。

call(thisObject,arg1,arg2...) 

        function fn(x, y) {
            console.log('我想喝手磨咖啡');
            console.log(this);
            console.log(x + y);
        }
        var o = {
            name: 'andy'
        };
        // fn();
        // 1. call() 可以调用函数
        fn.call();
        // 2. call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
        fn.call(o, 1, 2);

4. 借用父构造函数继承属性

  1.     先定义一个父构造函数(this指向为window)
  2.     再定义一个子构造函数(this指向为new出的实例化对象)
  3.     子构造函数通过call()改变父的this指向继承父构造函数属性 
            function Hui(uname, age) {
                // this 指向父构造函数的对象实例
                this.uname = uname;
                this.age = age;
            }
            function Chen(value){
                this.value=value;
            }
            // 2 .子构造函数 
            function Miao(uname, age, value) {
                // this 指向子构造函数的对象实例
                Hui.call(this, uname, age);
                Chen.call(this,value);
            }
            var Miao = new Miao('辉', 21, 100);
            console.log(Miao);
  4. 优点:避免了原型链继承的两个缺点,可以向父类传参,且不会造成原型属性共享的问题(因为父类构造函数中的属性继承到子类构造函数中,而非原型对象中)。
  5. 缺点:不能继承原型属性,无法实现函数复用,所有方法都只能放在构造函数中。

5.原型链继承 

原型链是针对原型对象的,在查找实例属性时,现在实例中查找,如果没有找到,再到obj.__proto__(=f1.prototype)原型对象,再到f1.prototype.__proto__(=f2.prototype),依次向上查找。

function Father(){
    this.color = 'red';
}
Father.prototype.getName = function(){
    console.log(this.name);
}
 
function Son(){}
Son.prototype = new Father();//将父类的实例赋给子类的原型
 
var son = new Son();
console.log(son);

打印结果:

将Father实例化给Son的原型对象,再实例化Son赋值给变量son,打印结果可见son通过prototype指向Father函数,Father函数通过prototype指向Object对象,让Son来继承其中的属性。

注意:原型对象的构造函数是函数本身,因此每个构造函数的prototype对象可以看做一个实例化对象。

  1. 优点:能够继承构造函数属性,也能够继承原型属性。

  2. 缺点:
    1.首先属性值为引用类型值的原型属性被修改后可能会影响到所有的实例,并在此继承方式中,构造函数中的属性会继承到子类型,成为子类的原型对象的属性,这也是要在构造函数中定义属性,在原型中定义公共方法的原因。

    2.在创建子类的实例时,不能向父类的构造函数中传递参数。

6.组合继承

组合继承(combination inheritance),也叫做伪经典继承,指的是将原型链和借用父构造函数组合到一块,其思路是用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

function Person(name, age) {
    this.name = name,
    this.age = age,
    this.setAge = function () { }
}
Person.prototype.setAge = function () {
    console.log("111");
}
function Student(name, age, price) {
    Person.call(this, name, age);
    this.price = price;
    this.setScore = function () { };
}
Student.prototype = new Person();
Student.prototype.constructor = Student;//组合继承也是需要修复构造函数指向的
Student.prototype.sayHello = function () { };
var s1 = new Student('Tom', 20, 15000);
var s2 = new Student('Jack', 22, 14000);
console.log(s1);
console.log(s1.constructor);
Student;
console.log(s2.constructor);
console.log(Person);

运行结果: 


未完待续

  • 18
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
1. 原型 在 JavaScript 中,每个对象都有一个原型对象,原型对象是一个普通的对象,其中包含了一些属性和方法。当我们访问对象的属性或方法时,如果该对象本身没有该属性或方法,JavaScript 引擎会自动去该对象的原型对象中查找,如果原型对象中仍然没有,则会在原型对象的原型对象中查找,一直查找到 Object.prototype,最后如果还没有找到,则会返回 undefined 。 2. 继承JavaScript 中,我们可以通过继承来实现代码复用和对象之间的关系。JavaScript 中的继承有两种方式:通过原型链继承和通过构造函数继承。 3. 原型继承 原型继承是指在 JavaScript 中通过设置对象的原型来实现继承方式。具体的实现方式有很多种,其中一种比较常见的方式是使用 Object.create() 方法来创建一个新对象,并将原型指定为另一个对象。示例如下: ``` let animal = { eat() { console.log("eating"); } }; let rabbit = Object.create(animal); rabbit.eat(); // 输出 "eating" ``` 在上面的示例中,我们首先定义了一个 animal 对象,它包含一个 eat() 方法。然后我们通过 Object.create() 方法创建了一个新对象 rabbit,并将其原型设置为 animal,这样就实现了 rabbit 对象从 animal 对象继承 eat() 方法的功能。我们可以通过调用 rabbit.eat() 来验证该方法确实存在于 rabbit 对象中。 4. 关键字 在 JavaScript 中,有一些关键字和函数可以用来实现原型继承。其中最常用的关键字是 extends 和 super。 extends 关键字可以用来继承一个类,示例如下: ``` class Animal { eat() { console.log("eating"); } } class Rabbit extends Animal { run() { console.log("running"); } } let rabbit = new Rabbit(); rabbit.eat(); // 输出 "eating" rabbit.run(); // 输出 "running" ``` 在这个示例中,我们首先定义了一个 Animal 类,并为其定义了一个 eat() 方法。然后我们定义了一个 Rabbit 类,该类使用 extends 关键字继承了 Animal 类,并为其定义了一个 run() 方法。我们通过实例化 Rabbit 类来创建一个 rabbit 对象,并通过调用 rabbit.eat() 和 rabbit.run() 来验证其继承效果。 super 函数用于在子类中调用父类的同名方法。示例如下: ``` class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + " makes a noise."); } } class Dog extends Animal { constructor(name) { super(name); } speak() { console.log(this.name + " barks."); } } let dog = new Dog("Rex"); dog.speak(); // 输出 "Rex barks." ``` 在这个示例中,我们首先定义了一个 Animal 类,并为其定义了一个构造函数和一个 speak() 方法。然后我们定义了一个 Dog 类,该类继承了 Animal 类,并为其定义了一个构造函数和一个 speak() 方法。 在 Dog 类的构造函数中,我们调用了父类的构造函数,并传入了 name 参数。在 Dog 类的 speak() 方法中,我们使用 super 函数调用了父类的同名方法,并将其重写为输出“barks.”。我们通过实例化 Dog 类来创建一个 dog 对象,并通过调用 dog.speak() 来验证其继承效果。 总结JavaScript 中,原型继承是一种非常重要的对象复用和代码组织方式。我们可以使用原型对象来实现原型继承,也可以使用关键字和函数来简化继承的过程。在实际开发中,我们需要根据具体的业务场景和需求来选择最合适的继承方式和实现方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值