JavaScript中的继承

141 阅读3分钟

JavaScript中的继承

JS中的继承是我们工作中提高工作效率的非常实用且高效的一种手段,也是求职面试比问的一个知识模块。

常见的几种继承方式

  • 原型式继承
  • 构造函数式继承
  • 拷贝式继承
  • Class继承
  • hasOwnProperty():检测属性(不会)

原型式继承

原型式继承指的是:将父类的实例作为子类的原型。

// 原型式继承指的是:将父类的实例作为子类的原型。
        // 父类(超类/基类)
        function Person(name){
            this.name = name || "川振华"
            this.sayHello = function(){
                console.log(`我是${this.name},我是懂王`);
            }
        }
        Person.prototype.tp = function(){
            console.log(`我振华已经到太阳上了`);
        }

        // 子类
        function Student(){}
        Student.prototype = new Person()
        // 对子类的原型的构造器重新定义
        Student.prototype.constructor = Student

        let stu = new Student()
        console.log( stu.constructor )
        console.log( stu.name )
        stu.sayHello()
        stu.tp()

构造函数式继承

构造函数式继承主要是利用了call()和apply()两个方法。

JavaScript中的每一个Function对象都有一个apply()方法和一个call()方法。

  • apply():调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

  • call():调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

    let fn = new Function("haha") // Function.prototype.call = function(){} // Function.prototype.apply = function(){}

          /*apply()方法*/
          // fn.apply(thisObj[, argArray])
    
          /*call()方法*/
          // fn.call(thisObj[, arg1[, arg2[, [,...argN]]]]);
          
          function getSum( n1,n2 ){
              return n1+n2
          }
          function getDiff( n1,n2 ){
              return n1-n2
          }
          // getDiff调用getSum的功能
          let result1 = getSum.call(getDiff,10,5)
          console.log( result1 );
          let result2 = getSum.apply(getDiff,[10,5])
          console.log(result2);
    
          // 父类
          function Person(name){
              this.name = name || "川振华"
              this.sayHello = function(){
                  console.log(`我是${this.name},我是懂王`);
              }
          }
          Person.prototype.tp = function(){
              console.log(`我振华已经到太阳上了`);
          }
    
          // 子类
          function Student(name){
              Person.call(this,name)
          }
          let stu = new Student("建国")
          console.log(stu);
          // stu.tp()
    
          // 面试题:找出数组中的最大数字
          // let a = Math.max(10,20,3,45,21,14,55,22,74,56,9,58)
          let arr = [10,20,3,45,21,14,55,22,74,56,9,58]
          let a = Math.max.apply(null,arr)
          console.log(a);
    

call()方法和apply()方法的区别

这两个方法都是用另一个对象替换当前的对象。

这两个方法的不同点主要表现在参数上。

  • call():可以有无限制个参数
    • 参数1:新的this对象
    • 参数2,3,4...:其他参数
  • apply():只能有两个参数
    • 参数1:新的this对象
    • 参数2:数组或类数组对象

拷贝式继承

拷贝式继承也是我们常用的一种手段,但是拷贝也有区分:

  • 浅拷贝:直接赋值拷贝。
  • 深拷贝:将A对象的属性全部复制到B对象上。

浅拷贝

let obj1 = {
            name:"tom",
            age:20,
            hobby:["drink","smoke","basketball","sing","dance","rap"]
        }
let obj2 = obj1
obj2.name = "jack"
console.log(obj1);
console.log(obj2);

所谓的浅拷贝就类似于数组的赋值,而实际上就是把obj1的地址值传给了obj2。那么地址一样的话这两个对象的值也会相互影响。

深拷贝

// 深拷贝
        let obj3 = {}
        for( let key in obj1 ){
            obj3[key] = obj1[key]
        }
        obj3.name = "jack"
        console.log(obj1);
        console.log(obj3);

深拷贝的好处在于对obj2的操作不会影响到obj1对象,但是上面的代码其实是有bug的。

 // 深拷贝
let obj3 = {}
        for( let key in obj1 ){
            obj3[key] = obj1[key]
        }
        obj3.name = "jack"
        obj3.hobby.push("program")
        console.log(obj1);
        console.log(obj3);

当对象obj1的属性的值不再是基本数据类型时,我们就不能直接简单的复制了,因为简单的复制还是直接搬运别人的地址值。 所以我们需要更加严谨一些,我们可以使用递归。

解法一

// 解法1
        function deepClone( obj ){
            let newObj = new obj.constructor()
            // if( Array.isArray(obj) ){ // new obj.constructor() => new Student()
            //     newObj = []
            // }else{
            //     newObj = {}
            // }
            for( let key in obj ){
                // hobby
                if( typeof obj[key] === "object" && obj[key] !== null ){
                    newObj[key] = deepClone( obj[key] )
                }else{
                    newObj[key] = obj[key]// name age
                }
            }
            return newObj
        }
        let o = deepClone(obj1)

        // o.hobby.push("aahjhbja")
        // console.log(obj1);
        // console.log(o);

解法二

//解法二
let obj4 = JSON.parse( JSON.stringify(obj1) )
        obj4.hobby.push("haha")
        console.log(obj1);
        console.log(obj4);

Class继承

Class继承则需要使用super() 和extends两个关键字。

// 定义父类
        class Fu{
            // 构造器
            constructor(name){
                this.name = name
            }
            // Fu.prototype.show
            show(){
                console.log(`我是${this.name}`);
            }
        }
        // 定义子类继承父类
        class Zi extends Fu{
            constructor(name,age){
                // 子类继承父类时,要求需要在构造器中调用父类的构造器
                super(name)
                this.age = age
            }
        }
        let z = new Zi("tom",20)
        console.log(z);

hasOwnProperty():检测属性

Object的hasOwnProperty()方法返回一个布尔值,判断对象是否包含特定的自身(非继承)属性。

var o ={x:1}

o.hasOwnProperty("x"); true

o.hasOwnProperty("y");    false //非继承返回true

o.hasOwnProperty("toString");  false //继承属性返回false