构造函数与原型对象

前言

在学习了javascript基础后,我们应当去深层次的学习ES6。ES6有许多新特性,都将广泛运用于项目中。并且在企业的面试中,对ES6的考察也非常细致。

而es6中,笔者认为最难的部分是js的原型。本节就从面向对象开始,为大家逐渐深入原型讲解。原型方面的知识会涉及到后面对框架的理解,希望大家重视,也希望本节内容能帮助到大家!

一,面向对象编程

1.1 面向过程与面向对象

学过java,c++的同学应该能够理解面向对象。这里用通俗的语言对面向过程与面向对象做一个简单的介绍:

什么是面向过程?

面向过程的编程是以过程为中心进行编程。面向过程更注重因果关系,比如我要解决一个问题,我应该怎么做,我给出具体解法,让程序跑起来,最后解决问题。典型的是C语言。

什么是面向对象?

面向对象的编程是以对象为中心进行编程。它把很多个问题分成类,在写出一类问题的解法。比如我今天有一个问题需要解决,于是这个问题就是‘对象’,我去查找这一类问题怎么解决,这一类问题就是‘类’。面向对象就是把每个问题看成对象,调用这个对象对应的类下的方法来解决问题。典型是java语言,c++语言。

想要知道更多,读者可以去搜集更多面向对象的文档学习。

1.2 JS创建类和对象

创建类:

class 类名{
	方法1() {}
	方法2() {}
}

实例对象:

var 对象名 = new 类名()

于是,该对象可以调用该类中的方法,访问类中的数据。

具体参见下面实例:

        class Star {
            constructor(uname) {
                this.uname = uname;
            }
            sing() {
                console.log('我唱歌');
            }
        }
        //创建对象new
        var ldh = new Star('ldh');
        console.log(ldh.uname);
        ldh.sing();

这里需要注意,一般类名需要大写。

1.3 类的继承

这里举一个简单的例子。你的爷爷是一个百万富翁,你的爸爸继承了你爷爷的钱,并且他用这笔钱拿去赚钱翻倍成了千万富翁。你爸爸继承了你爷爷的东西,这是继承;他用这些东西,做了别的事情,这是继承的重要意义。

现在我们来学习具体定义:
父类/子类:已有类为父类,新建类为子类。
继承:一个类从另一个已有的类获得其特性,称为继承。
简单理解:某个类A具有某些特征,另一个类B,也具有A类的所有特征,并且还可能具有自己的更多的一些特征,此时,我们就可以实现:B类使用A的特征信息并继续添加自己的一些特有特征信息。

现规定,父类为A,子类为B,子类继承父类的格式:

class B extends A;

子类继承父类,可以使用父类中的方法,例子如下:

        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y); 
            }
        }
        class Son extends Father {
            constructor(x, y) {
                super(1, 2);//调用了父类中的构造函数
            }
        }
        var son = new Son(1,2);
        son.sum();

在这里插入图片描述

1.4 Super关键词

子类在继承父类时可以调用父类的方法,也可以给自己新增方法。倘若父类中有一个方法名字为say,子类也有一个方法名为say,会如何呢?
请看如下代码:
在这里插入图片描述
最后的结果是子类中的方法覆盖了父类中的同名方法
在这里插入图片描述
此时有一个需求,我们需要调用父类中的say,该如何调用?这里就需要关键词Super了。使用supper可调用父类中的原型方法:

super.方法名

在这里插入图片描述
结果:
在这里插入图片描述

1.5 几个注意点

点一:在ES6中没有变量提升,所以要先定义类再实例化对象;
点二:类里面的共有属性与方法一定加this使用;
点三:实例对象中的this指向的是this对象,方法中的this指向的是方法的调用者。

二,构造函数原型

在ES6类与对象之前,通过构造函数和原型实现类的机制。

2.1 创建对象的三种方法

法一:利用new object()创建:

var obj = new object()

法二:利用对象字面量创建:

var obj1 = {}

法三:构造函数创建对象:
构造函数创建类,类创建对象:

function Start(uname, age) {
	this.uname = uname;
	this.age = age;
	this.call = function() {
		console.log(this)
	}
}
var ldh = new Start('Ann', 12);
Obj.sex = '男'//静态成员

构造函数创建对象一共四步,首先,系统会在内存中创建一个空对象;让this指向这个对象;然后执行构造函数内部代码给新对象提供属性和方法;最后返回这个新对象。

2.2 静态成员和实例成员

实例成员是构造函数内部通过this添加的成员,上述代码中uname,age,call就是实例成员;

静态成员在构造函数本身上添加的成员,sex就是静态成员,静态成员只能通过构造函数访问,不能通过对象访问。

2.3 构造函数的弊端

构造函数方法好用,但是存在消费内存的行为:我们用构造函数的方法创建了一个类,但是在创建对象的时候,实际上是开辟了多个空间去存放多个对象的,如下图,比较的是两者的地址,地址是不一样的。在这里插入图片描述在这里插入图片描述

2.4 函数的共享-原型prototype

构造函数通过原型分配的函数时所有对象所共享的。

JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个prototype本身就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

那么我们现在来打印一下Star构造函数,看看它有没有prototype:
在这里插入图片描述
注意,console.dir能够显示一个对象所有的属性和方法。
在这里插入图片描述
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有的对象实例就可以共享这些方法。

所以,我们通常不把这些方法放在构造函数里面,而是放在原型对象上。
在这里插入图片描述
在这里插入图片描述
依旧可以正常输出,我们成功的实现了对象的共享。

2.5 对象原型___proto__

对象为什么能访问原型上的方法?

因为对象都有一个__proto__属性指向构造函数的prototype原型对象

如何证明:
在这里插入图片描述
在这里插入图片描述
这里需要了解方法的查找规则:先看看对象上有没有本身的方法,如果有就执行,如果没有,因为有__propo__的存在,就去构造函数原型对象prototype身上查找该方法。
在这里插入图片描述
proto只读不写。(这里就好像vue中的prop,只读不写)

2.6 constructor构造函数

对象原型(proto构造函数(prototype)原型对象里面都有一个属性,constructor属性,我们称为构造函数,因为它指回构造函数本身。
目的:记录用哪个构造函数创建出来的。
在这里插入图片描述
在这里插入图片描述
现在我们来查看下constructor是什么:
在这里插入图片描述
在这里插入图片描述
都指向了它们的构造函数。
这里注意,方法的添加不能用这种格式:
在这里插入图片描述
因为这样做会覆盖掉prototype,所以我们如果要修改原型对象,只能手动去添加。

2.7 构造函数、实例、原型对象三者之间的关系

在这里插入图片描述

2.8 原型链

从上面的讲述可知,只要是对象都有__proto__原型的存在,指向原型对象。原型对象也是对象,我们可以看看原型对象中有没有__proto__的存在:
原型对象:
在这里插入图片描述
在这里插入图片描述
里面依旧存在__proto__。并且,它等于Object.prototype:
在这里插入图片描述
在这里插入图片描述
那么,Object的原型对象的原型对象又是谁呢:
在这里插入图片描述
答案为空,已经到了最顶层。
在这里插入图片描述
所以,我们可以划出完整的原型链:
在这里插入图片描述

2.9 对象成员查找规则

当访问一个对象的属性(包括方法),首先查找这个对象自身有没有该属性;
如果没有就查找它的原型(__proto__指向的==prototype原型对象);
如果还没有就查找原型对象的原型(Object原型对象);
以此类推一直找到Object为止(null);
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

2.10 this的指向问题

在构造函数中this指的是对象实例;
原型对象函数里面的thi指向的是实例对象:
在这里插入图片描述
在这里插入图片描述

2.11 原型对象的运用

扩展内置对象,可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给主族增加自定义求和属性:

        Array.prototype.sum = function() {
            for(var i = 0; i < this.length; i++) {
                sum = sum + this[i];
            }
            return sum;
        }

数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式。

后记

本节主要讲述js原型的相关知识,逻辑可能稍微有些混乱,如果有问题希望大家能帮我指出,欢迎关注!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值