美文网首页
JavaScript中的继承介绍

JavaScript中的继承介绍

作者: vinterx | 来源:发表于2017-04-08 21:40 被阅读0次

一、继承的概念

继承是所有面向对象语言中最重要的一个特征。而执行环境也是JS最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据的范围,决定了它们各自的行为。

主要继承方式:接口继承和实现继承

JS主要支持实现继承,而且是在原型链的基础上实现的,继承是发生在对象与对象之间

二、原型链的概念

原型链作为实现继承的主要方法,是利用原型让一个引用类型继承另一个引用类型的属性和方法

构造函数、原型(对象)和构造函数创造的对象之间的关系:每个构造函数都有一个属性 prototype 指向一个原型对象,每个原型对象也有一个属性constructor指向函数,通过new 构造函数()创建出来的对象内部有一个不可见的属性[[proto]]指向构造函数的原型。

1、更换构造函数的原型

原型其实就是一个对象,只是默认情况下原型对象是浏览器会自动帮我们创建的,而且自动让构造函数的prototype属性指向这个自动创建的原型对象。我们完全可以将原型对象更换成我们自定义类型的对象。

    //定义一个构造函数。
    function Father () {
        // 添加name属性.  默认直接赋值了。当然也可以通过构造函数传递过来
        this.name = "马云";
    }
    //给Father的原型添加giveMoney方法
    Father.prototype.giveMoney = function () {
        alert("我是Father原型中定义的方法");
    }
    //再定义一个构造函数。
    function Son () {
        //添加age属性
        this.age = 18;
    }
    //关键地方:把Son构造方法的原型替换成Father的对象。  因为原型是对象,任何对象都可以作为原型
    Son.prototype = new Father();
    //给Son的原型添加getMoney方法
    Son.prototype.getMoney = function () {
        alert("我是Son的原型中定义的方法");
    }
    //创建Son类型的对象
    var son1 = new Son();

以上代码即实现了Son继承了Father的过程

注意:继承过程中的访问属性和方法的过程为:

对象本身->原型->原型的原型->...->原型链的顶端(Object原型对象)这也是为什么我们随意创建一个对象,就有很多方法可以调用,其实这些方法都是来自Object的原型对象(Object原型对象的原型有人说是null,也有人说是它本身)

2、测试数据类型的四种方法

2.1、typeof:一般用来测试简单数据类型和函数的类型。如果用来测试对象,则会一直返回object,没有太大意义。

2.2、instanceof:用来测试一个对象是不是属于某个类型。结果为boolean值。

function Father () {
    }
    function Son () {   
    }

    Son.prototype = new Father();
    var son = new Son();
    alert(son instanceof Son);  // true
    // Son通过原型继承了Father
    alert(son instanceof Father);  // true
    //Father又默认继承了Objcet
    alert(son instanceof Object); // true

2.3、isPrototypeOf( 对象 ) : 这是个 原型对象 的方法,参数传入一个对象,判断参数对象是不是由这个原型派生出来的。 即判断这个原型是不是参数对象原型链中的一环。

    function Father () {
        
    }
    function Son () {
        
    }

    Son.prototype = new Father();
    var son = new Son();
    alert(Son.prototype.isPrototypeOf(son));  // true
    alert(Father.prototype.isPrototypeOf(son)); // true
    alert(Son.prototype.isPrototypeOf(Father)); //false
    alert(Object.prototype.isPrototypeOf(Father)) //true
    alert(Object.prototype.isPrototypeOf(son)); // true
    
    //从参数对象原型开始

2.4、Object.prototype.toString.call(对象或基本类型)

    function Foo(){
    
    }
    
    var arr = new Array("a");
    var s = arr.toString();
    console.log(s);
    console.log(Object.prototype.toString.call(foo))  //测试数据类型

三、 原型链在继承中的缺陷

1、在原型链中,父类型的构造函数创建的对象,会成为子类型的原型。则父类型中定义的实例属性,就会成为子类型的原型属性。对子类型来说,这和我们以前说的在原型中定义方法,构造函数中定义属性是违背的。子类型原型(父类型对象)中的属性被所有的子类型的实例所共有,如果有一个实例(即构造函数创建的对象)去更改,则会很快反应的其他的实例上。

    function Father () {
        this.girls = ["志玲", "凤姐"];
    }
    function Son () {
        
    }
    // 子类的原型对象中就有一个属性 girls ,是个数组
    Son.prototype = new Father();   
    var son1 = new Son();
    var son2 = new Son();
    //给son1的girls属性的数组添加一个元素
    son1.girls.push("亦非");
    //这时,发现son2中的girls属性的数组内容也发生了改变
    alert(son2.girls);  // "志玲", "凤姐", "亦非"

2、向父类型的构造函数中传递参数问题
如以上,父对象的属性和方法会被子对象(实例所共享)则父类型传递的参数也会被子对象(实例所共享):父类型传递的地方:Son.prototype = new Father();

四、借用构造函数调用"继承"(函数借调)

为解决以上属性共享问题:
借用构造函数调用继承,又叫伪装调用继承或冒充调用继承。虽然有了继承两个字,但是这种方法从本质上并没实现继承,只是完成了构造方法的调用而已。

1、借调方法

主要有call和apply方法:功能都是更改一个构造方法内部的this指向到指定的对象上。(后面详细有介绍)

            function Father(x , y ){
                this.x = x;
                this.y = y;
                
            }
            function Son(x , y , z){
                Father.apply(this , [x , y]);//将数组展开,但是call不会
                this.z = z;
            }
            var s1 = new Son(1 , 2 , 3);
            console.log(s1);//Son {x: 1, y: 2, z: 3}

call和apply应用

    var name = "a"
    function foo(a, b){
        console.log(a + "  " + b);
        this.name = "d"
    }
    var obj = {
        name : "c"
    }
    foo.call(obj, 1, 3);  //不影响后面内容
    console.log(obj.name);  //"d"
    
//计算一个数组中的最大值和最小值
    var arr = [30, 6, 9, 30, 50];
    var max = Math.max.apply(Math, arr)//50
    console.log(max);
    console.log(Math.min.apply(Math, arr));//6

注意:函数借调其实并没有真的继承,仅仅是调用了Father构造函数而已。也就是说,son对象和Father没有任何的关系。

通过函数借调方式将父类型传递的属性值进行覆盖,从而实现了子对象(实例)属性不相同的效果。

相关文章

网友评论

      本文标题:JavaScript中的继承介绍

      本文链接:https://www.haomeiwen.com/subject/tbjrattx.html