JavaScript中的原型、原型链、构造函数以及实例对象

其实原型我看了有一段时间了,也没有看很长时间,就是找一个下午的时间听听课,然后就感觉很懵,似懂非懂的,接着就自己想想,然后就还是懵。平时翻翻技术类的公众号或者CSDN上的文章也会涉及到原型的内容,加上前天我又看一一遍讲解,看的多了慢慢的就更熟悉更了解了。
今天,下定决心要总结总结写写原型了。内容多少会有点问题,谅解谅解,毕竟是菜鸟的小白为了记录自己的学习过程。

构造函数和实例对象

构造函数也是一个函数,它与普通函数的区别就在于构造函数名首字母要大写。通过构造函数创建对象的过程叫对象的实例化,创建的对象是实例对象。Object构造函数,是系统提供好的构造函数,我们自定义的构造函数也是构造函数。

var obj2 = new Object();
function Person(uname, uage) {
        this.uname = uname;
        this.uage = uage;
}
Person("xz", 18);

构造函数中的属性和方法,分为两种:实例成员和静态成员。一般构造函数中都是实例成员,静态成员的添加方法是直接在构造函数上面添加的。
上面的uname和uage都是实例成员,静态成员:Person.sex = “male”,这里的sex就是静态成员。

实例成员:构造函数内部通过this添加的属性和方法
静态成员:构造函数本身添加的属性和方法

创建对象

再谈谈创建对象的几种方式吧。
一般是三种方式:对象字面量创建、Object创建、自定义构造函数创建。其实总的来说是两种方式:对象字面量创建和通过构造函数创建。

        // 1. 字面量创建对象
        var obj1 = {
            name: "lyf",
            age: 18
        }
        // 2. 通过 new Object
        var obj2 = new Object();
        obj2.name = "zly";
        obj2.age = 17;
        // 3. 通过自定义构造函数
        function Person(uname, uage) {
            this.uname = uname;
            this.uage = uage;
        }
        // 通过new创建出来的对象,是构造函数实例化出来的对象 —— 对象的实例化
        var obj3 = new Person("xz", 18);

new关键的过程

  1. 先创建一个空对象
  2. 把空对象赋值给this
  3. 把属性和方法都挂在this上面
  4. 最后把this对象返回
    所以这就是在创建对象时,不需要返回值的原因。

原型

上面说完了构造函数和实例对象,接下来我们就来介绍介绍原型。
每个函数都有一个prototype属性叫原型,这个属性是一个对象,所以叫原型对象。
每个对象都有一个__proto__属性,叫对象的原型,它指向构造该对象的构造函数的原型对象。

自定义构造函数:

// 只要是函数,就有原型对象
        function Person(uname, uage) {
            this.uname = uname;
            this.uage = uage;
        }
        // 构造函数也是函数, 所以构造函数也有自己的原型对象 
        var obj = new Person("xz", 18);
        // 通过prototype访问函数的原型对象
        console.log(Person.prototype);

这就是原型对象。可以看到,原型对象里有constructor和__proto__两个属性。
在这里插入图片描述
上面说了,只要是对象就有__proto__ 属性,指向构造该对象的构造函数的原型对象。
__proto__是对象的原型,这里__proto__指向创建Person.prototype对象的构造函数的原型对象 —— Object.prototype。
constructor是构造函数,指向创建该对象的函数。简单来说,就是记录谁创建了这个对象。这里constructor指向创建Person.prototype对象的函数 —— Person。Person创建了Person.prototype原型对象,所以constructor指向Person。

		// 通过prototype访问函数的原型对象
        console.log(Person.prototype); // Person的原型对象
        console.log(Person.prototype.__proto__); // Person原型对象的原型
        // 原型对象里有一个constructor属性,该属性指向创建它(原型对象)的函数
        console.log(Person.prototype.constructor); 
        console.log(Person.prototype.__proto__ === Object.prototype);

第一个输出的是Person构造函数的原型对象:Person.prototype。
第二个输出的是Person构造函数的原型对象的原型:Object.prototype。Object是顶级对象,任何一个对象都是Object的实例化对象。
第三个输出的是Person构造函数的原型对象的构造函数:Person。constructor属性是指向创建该对象的构造函数。
第四个输出的是true。
在这里插入图片描述

看了构造函数的原型,现在再来看下实例对象的原型

		var obj = new Person("xz", 18);
        // 构造函数实例化的对象
        console.log(obj);
        // 实例化对象有一个原型属性:__ptoro__
        // 对象的__proto__ 属性 指向构造函数的原型对象
        console.log(obj.__proto__);
        // 对象的原型 === 构造函数的原型对象
        console.log(obj.__proto__ === Person.prototype);

第一个输出的是obj实例对象自己,对象除了有构造函数定义的属性外,还有有__ptoto__属性。
第二个输出的是obj的原型 —— Person构造函数。因为obj是通过Person构造函数实例化出来的,所以obj的原型是Person构造函数d的原型对象。
第三个输出的是true。因为obj的原型是Person构造函数原型对象,Person构造函数的原型对象是Person.prototype,所以输出true。
在这里插入图片描述
来一张obj、Person、Person.prototype的图,就明白了
在这里插入图片描述
这个图再完善一下,加上Object就更清晰了。我们都知道,所有的对象到最后都会返回Object,Object对象是顶级对象。
在这里插入图片描述
这样的话,是不是有人还想试试Object.prototype.proto?那就试试呗。
在这里插入图片描述
返回null,这就解释了为什么Object是顶级对象,再往上找就没有了。

原型链

讲完原型,终于可以说原型链了。接下来原型链就在原型的基础上就稍稍的简单了一点。
看图看图。
在这里插入图片描述
没错,就是看这个图,有没有看到对象的__proto__,用__proto__连接起来的就是原型链。当访问到对象本身没有的属性或者方法时,此时就会顺着原型链进行查找,一直找到Object,prototype,如果找到的话就调用返回,如果没有找到就会返回undefined。
还是这个例子。

		function Person(uname, uage) {
            this.uname = uname;
            this.uage = uage;
        }
        // 构造函数也是函数, 所以构造函数也有自己的原型对象 
        var obj = new Person("xz", 18);

假如现在访问sex属性。肯定返回undefined,整个原型链上都没有sex属性。

console.log(obj.sex);

我在Perso.prototype上添加sex属性后,再去访问。此时就可以访问到了,obj对象本身没有这个属性,但是obj的原型Peroson.prototype上有,obj通过__proto__属性访问Peroson.prototype得到sex属性,最后返回。

Person.prototype.sex = "male";

在这里插入图片描述
同理,不在Peroson.prototype上添加sex属性,在Object.prototype上添加。同样的obj依然可以访问到,通过原型链中__proto__属性。
在这里插入图片描述
当obj对象、obj对象的原型以及Object原型对象都有sex属性时,obj对象会返回哪个值?

		Person.prototype.sex = "male";
        Object.prototype.sex = "女";

        function Person(uname, uage, sex) {
            this.uname = uname;
            this.uage = uage;
            this.sex = usex;
        }
        // 构造函数也是函数, 所以构造函数也有自己的原型对象 
        var obj = new Person("xz", 18, "男");
        console.log(obj.sex);

返回的是obj对象自己的属性值。
在这里插入图片描述
需要注意的是,先从自己找,自己没有就找自己的原型,原型没有就再往上以及原型找,就这样一层一层的找。

JavaScript查找机制:就近原则。

补充

这种形式是在原型对象上追加属性

		Person.prototype.sex = "male";

这种形式是改写了原型对象,也就是说,这个把系统提供的Person.prototype给覆盖了。再访问Person.prototype时只有sex这一个属性了。

        Person.prototype = {
            sex: "male"
        }

在这里插入图片描述
覆盖的问题要谨慎。

emmm,结束了结束了。原型和原型链暂时就了解了这些哈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章