七.JavaScript多种继承方式

继承的目的:让子类的实例同时也具备父类中的私有属性和公共方法

子类的实例能够用子类私有的和原型上共有的

父类的实例能够用父类私有的和原型上共有的

方法一:原型继承

将子类的原型对象指向父类的实例,通过这种方法,子类能继承父类私有的和公有的。

图片[1]-七.JavaScript多种继承方式-一一网
习题1:

      function Parent() {
        this.name = "Parent";
        this.sex = "boy";
      }
      Parent.prototype.getName = function () {
        console.log(this.name);
      };
      function Child() {
        this.name = "child";
      }
      Child.prototype = new Parent();

      var child1 = new Child();
      child1.getName();//child
      console.log(child1);//Child {name: "child"}
      //sex、getName都是Child原型对象上的属性,所以并不会表现在child1上。
复制代码

习题2:

      function Parent(name) {
        this.name = name;
        this.sex = "boy";
        this.colors = ["white", "black"];
      }
      function Child() {
        this.feature = ["cute"];
      }
      var parent = new Parent("parent");
      Child.prototype = parent;

      var child1 = new Child("child1");
      child1.sex = "girl";
      child1.colors.push("yellow");
      child1.feature.push("sunshine");

      var child2 = new Child("child2");

      console.log(child1);
      console.log(child2);

      console.log(child1.name);
      console.log(child2.colors);
复制代码

无标sss题.png

  • 优点:
  • 继承了父类的模板,又继承了父类的原型对象
  • 缺点:
  • 如果要给子类的原型上新增属性和方法,就必须放在Child.prototype = new Parent()这样的语句后面(顺序问题)
  • 无法实现多继承(因为已经指定了原型对象了)
  • 来自原型对象的所有属性都被共享了,这样如果不小心修改了原型对象中的引用类型属性,那么所有子类创建的实例对象都会受到影响(这点从修改child1.colors可以看出来)
  • 创建子类时,无法向父类构造函数传参数(这点从child1.name可以看出来)

instanceof检测

instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
再来看看通俗点的简介:

a instanceof B
实例对象a instanceof 构造函数B
检测a的原型链(proto)上是否有B.prototype,有则返回true,否则返回false。

function Parent () {
  this.name = 'parent'
}
function Child () {
  this.sex = 'boy'
}
Child.prototype = new Parent()
var child1 = new Child()

console.log(child1 instanceof Child)//true
//查找顺序:child1 -> child1.__proto__ -> Child.prototype

console.log(child1 instanceof Parent)//true
//查找顺序:child1 -> child1.__proto__ -> Child.prototype-> Child.prototype.__proto__ -> Parent.prototype

console.log(child1 instanceof Object)//true
复制代码

isPrototypeOf()

这个方法属于Object.prototype上的方法,isPrototypeOf()的用法和instanceof相反。
它是用来判断指定对象object1是否存在于另一个对象object2的原型链中,是则返回true,否则返回false。

      function Parent() {
        this.name = "parent";
      }
      function Child() {
        this.sex = "boy";
      }
      Child.prototype = new Parent();
      var child1 = new Child();

      console.log(Child.prototype.isPrototypeOf(child1));//true
      console.log(Parent.prototype.isPrototypeOf(child1));//true
      console.log(Object.prototype.isPrototypeOf(child1));//true
复制代码

方法二:call 继承

在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和它没关系了),并且用call改变this指向。

Parent.call(this);
CALL 继承(只能继承父类中私有的,让父类私有的变成子类私有的;不能继承父类中公共的)

function Parent() {
  this.x = 100;
}

Parent.prototype.getX = function getX() {
  return this.x;
};

function Child() {
  // 在子类构造函数中,把父类当做普通方法执行(没有父类实例,父类原型上的那些东西也就和它没关系了)
  // this -> Child的实例c1
  Parent.call(this); // this.x=100: 相当于强制给c1这个实例设置一个私有的属性x,属性值100,相当于让子类的实例继承了父类的私有的属性,并且也变为了子类私有的属性 “拷贝式”
  this.y = 200;
}

Child.prototype.getY = function getY() {
  return this.y;
};

let c1 = new Child();
console.log(c1);
复制代码

方法三:寄生组合式继承(CALL+另类原型继承)

第一步:让子类原型通过原型链指向父类原型,这样父类公有方法就变成子类公有方法了。

Child.prototype.__proto__=Parent.prototype
兼容写法:Child.prototype=Object.create(Parent.prototype);这句话的含义是:创建一个空对象指向父类的原型,然后让子类的原型指向空对象

第二步 在子类构造函数中,把父类当做普通方法执行,并让this指向子类实例。

Parent.call(this)

第三步让原型的constructor正常指回,指向所属类。

Child.prototype.constructor = Child;

function Parent() {
  this.x = 100;
}

Parent.prototype.getX = function getX() {
  return this.x;
};

function Child() {
  Parent.call(this);
  this.y = 200;
}

Child.prototype = Object.create(Parent.prototype);
// 重定向了Child.prototype,重新指向Child
Child.prototype.constructor = Child;

Child.prototype.getY = function getY() {
  return this.y;
};

let c1 = new Child();
console.log(c1);
复制代码

方法四:ES6 类与继承

4.1 class类

ES6的类可以包含构造函数方法、实例方法、获取函数、设置函数和静态类方法,但都不是必须的。
实际上 class只是一个语法糖 是构造函数的另一种写法;
class不存在变量提升 (由于继承有关 必须确保子类在父类之后定义)。

class Person{
}
console.log(typeof Person)                             //funciton
console.log(Person.prototype.constructor === Person)   //true
复制代码
      class Student {
        constructor(name, number) {
          this.name = name;
          this.number = number;
          this.gender = "semale";
        }
        sayHi() {
          console.log(`姓名 ${this.name} ,学号 ${this.number}`);
          console.log("姓名:" + this.name, "学号:" + this.number);
        }
      }
      const luozhi = new Student("洛枳", 12345);
      console.log(luozhi); //{name: "洛枳", number: 12345, gender: "semale"}
      luozhi.sayHi();
      /*
        姓名 洛枳 ,学号 12345
        姓名:洛枳 学号:12345
      */
复制代码
4.1.1 类的属性和方法

定义在constructor 内的属性和方法,即调用在this上,属于实例属性和方法 否则属于原型属性和方法.

class Person {
  constructor (name) {
    this.name = name            //constructor内定义的方法和属性是实例对象自己的,
  }
  say () {                      //而constructor外定义的方法和属性则是所有实例对象可以共享的 注意!
    console.log('hello')
  }
}
let jon = new Person()
jon.hasOwnPrototype('name')     //true
jon.hasOwnPrototype('say')      //false
复制代码
4.1.2 静态方法

不能通过实例对象,可以直接通过类来调用的方法,其中的 this 指向类本身
静态方法可以被子类继承

class Person {
  static doSay () {
    this.say()
  }
  static say () {
    console.log('hello')
  }
}
Person.doSay()              //hello
复制代码

4.1.3 取值函数(getter)和存值函数(setter)
class Person {
  get name () {
    return 'getter'
  }
  set name(val) {
    console.log('setter' + val)
  }
}

let jon = new Person()
jon.name = 'jon' // setter jon
jon.name         // getter
复制代码

4.2 类的继承

  • 在子类后面添加 extends 并指定父类的名称
  • 在子类的 constructor 构造函数中通过 super 方法借助父类的构造函数,也可以调用父类的普通函数。
class Parent {
  constructor() {
    this.x = 100;
  }
  // Parent.prototype.get X = function... ,在Parent.prototype上添加方法getX
  getX() {
    return this.x;
  }
}
// 继承: extends Parent(类似于寄生组合继承)
// 注意:继承后,【如果子类有constructor,】一定要在constructor第一行加上super
class Child extends Parent {
  constructor() {
    // 类似于我们之前的CALL继承。super(100,200):相当于把Parent中的constructor执行,传递了100和200
    super();
    this.y = 200;
  }
  getY() {
    return this.y;
  }
}
let c1 = new Child();
console.log(c1);

// Child();ES6中创建的就是类,不能当做普通函数执行,只能new执行
复制代码
补充案例
function wjl(name, age) {
  // name、age通过参数传进来的,是每一个实例对象私有的属性。
  // height、house不是通过参数传进来的,是所有实例对象共有的属性。
  this.name = name;
  this.age = age;
  this.height = 188;
  this.house = function () {
    return "兰博基尼";
  };
}

wjl.prototype.money = 888;
var obj = new wjl("王健林", 12); // obj是wjl的实例对象, 参数 '王健林'、12是obj这个实例对象私有的,不是wjl所有的实例对象共有,这里的 '王健林'、12 和 wsc的实例对象o 没有关系
console.log("obj-------", obj); // wjl {name: "王健林", age: 12, height: 188, house: ƒ}

function wsc(names, ages) {
  this.names = names;
  this.ages = ages;
  wjl.call(this); // 继承wjl,而不是wjl的实例
}

var o = new wsc("王思聪", 22, 33, 55);
console.log("o----", o); // wsc {names: "王思聪", ages: 22, name: undefined, age: undefined, height: 188, …}

// 【'王健林'、12是obj实例的私有属性,不是wjl类的所有实例继承的属性。如果在wjl类中写 this.height = 188,而不是传进来的参数,那么this.height = 188就是wjl类的所有实例继承的属性、属性值,就像wjl类的this.house。】
console.log(o.house, o.name, o.age); // 函数 undefined undefined
console.log(o.house, o.names, o.ages); // 函数 王思聪 22
复制代码

五.补充对象的一些操作

5.1 获取对象类型—-对象是由哪些类创造出来的呢?

5.1.1 constructor-获取的是直接所属的类
let arr = new Array();
console.log(arr.constructor.name); // Array
let p = new Person();
// console.log(typeof p); // object
console.log(p.constructor.name); // Person
复制代码
5.1.2 instanceof-判断 “对象” 是否是指定构造函数的 “实例”

只要 构造函数的原型对象出现在实例对象的原型链中都会返回true

      let arr = new Array();
      console.log(arr instanceof Array); //true
      console.log(arr instanceof Object); //true

      class Person {}
      let p = new Person();

      console.log(p instanceof Person); //true
      console.log(p instanceof Array); //false
      console.log(p instanceof Object); //true
复制代码
5.1.3 isPrototypeOf—判断 一个对象是否是另一个对象的原型

只要调用者在传入对象的原型链上都会返回true

      class Person {
        name = "lnj";
      }
      let p = new Person();
      console.log(Person.prototype.isPrototypeOf(p)); // 判断Person原型对象是否是实例对象p的原型,返回true
复制代码

5.2 判断对象属性

5.2.1 in

判断某一个对象类中或原型中是否拥有某一个属性可以用“in”,只要类中或者原型对象中有, 就会返回true

        class Person{
            name = null;
            age = 0;
        }
        Person.prototype.height = 0;
        let p = new Person();
        console.log("name" in p); // true
        console.log("height" in p); // true
复制代码
5.2.2 hasOwnProperty

判断某一个对象自身是否拥有某一个属性可以用hasOwnProperty

特点: 只会去类中查找有没有, 不会去原型对象中查找

        let p = new Person();
        console.log(p.hasOwnProperty("name")); // true
        console.log(p.hasOwnProperty("height")); // false
复制代码

5.3对象属性增删改查、遍历

      class Person {
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }
      }
 // 1.创建实例对象
      let p = new Person("蔡徐坤", "22");
      console.log(p); //{name: "蔡徐坤", age: "22"}

//2.给对象增加属性(增加没有的属性)
      p.sex = "男";
      p["hobby"] = "singing,dance";
      console.log(p); // {name: "蔡徐坤", age: "22", sex: "男", hobby: "singing,dance"}
      
//3.给对象删除属性(删除已有的属性)
      delete p.sex;
      console.log(p); // {name: "蔡徐坤", age: "22", hobby: "singing,dance"}
      
//4.给对象修改属性(对操作已有的属性)
      p.name = "蔡小葵";
      p["age"] = 23;
      console.log(p); // {name: "蔡小葵", age: 23, hobby: "singing,dance"}
      
// 5.查询属性
      console.log("我是" + p.name, "我的爱好是" + p["hobby"]); //我是蔡小葵 我的爱好是singing,dance
      
// 6.对象属性遍历(for...in...)
      for (let key in p) {
        console.log(key + ":" + p[key]);
        /*
         name:蔡小葵
         age:23
         hobby:singing,dance
        */
      }
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享