JavaScript原型、原型链以及继承总结

写在前面

JS的原型以及原型链的知识一直是JS中的一个难点和重点,想要学习好JS,必定要先翻过这座大山,本篇文章是对于该知识点的一个记录,方便自己回头翻阅。

引用类型

JavaScript中的数据类型分为基本数据类型以及引用类型,另一篇博客有讲到关于这方面的知识点。
JavaScript的基本类型和对象引用总结
了解了这些基础之后,我们要知道,引用类型除了对象之外,其实数组函数都属于引用类型。他们都具有自由扩展性,也就是可以动态添加属性和方法。其实数组和函数本质上也是一种对象。这也是为什么说JS万物皆对象。

普通对象和函数对象

虽然JS万物皆对象,但是对象也是有区别的,分为普通对象函数对象。这两者很容易区分:本身是函数,或者是通过new Function构造出来的对象都是函数对象,其余都算是普通对象,举个例子:

    <script>
       var obj1 = {}; 
       var obj2 = new Object();
       var obj3 = new f1();
       
       function f1(){}; 
       var f2 = function(){};
       var f3 = new Function('name','console.log(name)');
       
       console.log(typeof Object); //function 
       console.log(typeof Function); //function  
       
       console.log(typeof f1); //function 
       console.log(typeof f2); //function 
       console.log(typeof f3); //function   
       
       console.log(typeof obj1); //object 
       console.log(typeof obj2); //object 
       console.log(typeof obj3); //object
    </script>

ObjectFunction是JS自带的函数对象。

构造函数

构造函数是用来构造对象的,也可以说是实例化对象的,类似于其他面向对象语言类的概念,不过并不是类(ES6中已经有类的概念了),看名字就知道了,构造函数就是一个函数,看例子:

    <script>
        function Person(name,age){
            this.name = name;
            this.age = age;
        }
        var person1 = new Person("张三",21);
        var person2 = new Person("张三",22);
        console.log(person1.constructor === person2.constructor);
    </script>
Output:true

上面的例子有两个实例对象,person1和person2都是通过Person这个构造函数实例化出来的,每个实例对象都有一个属性叫constructor,这个属性是一个指针,指向的是该实例对象的构造函数。所以person1和person2的constructor相等,因为指向了同一个构造函数。

原型规则

1.之前我们提到过,对象、数组、函数都属于引用类型,所有的引用类型都有一个__proto__属性,称之为隐式原型,属性值是一个普通对象。
2.所有的函数对象都有一个prototype属性,称之为显式原型,属性值也是一个普通对象。
3.所有引用类型的__proto__属性值都指向它构造函数的prototype属性值

    <script>
        function Person(name){
            this.name = name;
        }
        var lan = new Person("Lan");
        console.log(lan.__proto__ === Person.prototype);
    </script>
Output: true

根据输出便可证明上面我们所说的第三条规则。
总结这三条规则,我们可以得出一条结论

每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性

原型对象

原型对象顾名思义,就是一个普通对象,其实就是Person.prototype,每个原型对象都会有一个默认的属性就是constructor,这个属性(是一个指针)指向 prototype 属性所在的函数(Person),我们可以在原型对象上面增加属性值和方法,这样通过构造函数实例化的对象都能拥有相同的属性和方法了。

    <script>
        function Person(){
            Person.prototype.name = "Lan";
            Person.prototype.sayName = function(){
                console.log(this.name);
            }
        }
        var person1 = new Person();
        var person2 = new Person();
        console.log(person1.name, person2.name);
        console.log(person1.sayName === person2.sayName);
    </script>
Output:
Lan Lan
true

那么如果我们试图去寻找一个对象本身没有属性的时候,那么就会去它的__proto__,也就是它构造函数的prototype中去寻找。

    <script>
        function Person(name, age){
            this.name = name;
            this.age = age;
        }

        Person.prototype.sayName = function(){
            console.log(this.name);
        }

        var person1 = new Person("Lan", 22);
        person1.sayAge = function(){
            console.log(this.age);
        }

        person1.sayAge();
        person1.sayName();

    </script>
Output:
22
Lan

从上面的代码可以知道,person1本身的属性只有name、age和一个sayAge方法,并没有sayName方法。所以当我们调用person1.sayName()方法时,会调用person1.__proto__.sayName()。值得注意一点的是:无论属性是本身或者还是从原型上面找的,this永远都是指向person1本身

原型链

    <script>
        function Person(name, age){
            this.name = name;
            this.age = age;
        }
        var person1 = new Person("Lan", 22);
        person1.toString();
    </script>

person1.toString(),当我们的对象没有这个属性的时候,就会去它自身的隐式原型__proto__中去寻找,也就是去它构造函数的显式原型prototype中去寻找,但是Person.prototype中也没有toString(),因为显式原型本身也是一个对象,也有自己的隐式原型__proto__,于是它又会去它的隐式原型中去找,即在其构造函数 (Object )的显式原型中去找 toString,再往上找就是null了。下面是原型链的示意图:
原型链

instanceof

instanceof 可以用于判断一个引用类型是否属于某个构造函数:

    <script>
        function Person(){}
        var person1 = new Person();
        console.log(person1 instanceof Person);
    </script>
Output: true

其实instanof的原理就是person1一层一层往上找,看能否对应到 Person.prototype(显式原型)。

利用原型链继承

我们知道了原型链的原理,我们便可以利用这个原理去实现继承。

    <script>
        function Animal(){
            this.eat = function(){
                console.log("animal eat")
            }
        }

        function Dog(){
            this.bark = function(){
                console.log("dog bark");
            }
        }
        Dog.prototype = new Animal();
        var dog = new Dog();
        dog.eat();
    </script>

我们可以手动的把一个Animal的实例赋值给Dog的prototype。这样就可以沿着原型链找到Animal中的属性。也就实现了继承的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值