美文网首页
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