本文已参与「新人创作礼」活动,一起开启掘金创作之路。
面向对象
这就不用多说了吧,Java中都学过。
ES6中的类和对象
创建类
类constructor构造函数
constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor(),就是无参构造器。
注意点:
1、通过class关键字来创建类,类名我们习惯性首字母大写 2、类里面有个constructor函数,可以接收传递过来的参数,同时返回实例对象 3、constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数 4、生成实例 new 不能省略 5、最后注意语法规范,创建类 类名后面不能跟小括号,生成实例 类名后面加小括号,构造函数不需要加function
类添加方法
类的继承
继承
super关键字
super关键字用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数。
调用构造函数示例:
调用普通函数示例:
注意:
super关键字必须在this关键字之前调用 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象。 类里面的属性和方法一定要加this使用 注意this指向问题
构造函数和原型
概述
在典型的OOP语言当中(比如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没有引入类的概念。 在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
创建对象在以前可以有下面三种方式(复习一下): 1、对象字面量 2、new Object() 3、自定义构造函数
如下:
构造函数
JavaScript的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。通过这两张方式添加的成员,就分别称为静态成员和实例成员。
静态成员:在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问。 实例成员:在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问。
注意静态成员不能通过对象来访问,如上图中ldh.sex就是错误的!
构造函数存在的问题
构造方法很好用,但是存在浪费内存的问题:
如上图中uname和age属性都还好说,因为每个对象都不一样,但是每个对象的sing方法都是一样的呀!可它每回创建新对象时都要再额外开辟一个相同的sing函数空间,就造成了内存的极大浪费。
所以我们提出了构造函数原型prototype的概念。
构造函数原型prototype
构造函数通过原型分配的函数是所有对象所共享的。 JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
所以我们不应该把每个对象都相同的方法定义在构造函数内,而是应该定义在原型对象上:
总结一下: 原型是啥?
一个对象,我们也称为prototype为原型对象。
原型的作用是什么?
存储共享方法的地方。
对象原型_proto_
有一个问题,比如上面的程序,为什么我们调用原型对象中的方法可以直接使用对象.的方式呢?明明共同方法是定义在原型对象prototype上的呀。
这是因为对象原型_proto_的存在,对象都会有一个属性_proto_指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有_proto_原型的存在。
比如我们打印下面代码中的ldh对象:
对象身上系统会自动添加一个_proto_,它指向我们构造函数的原型对象prototype
去控制台查看结果是否是这样:
方法的查找规则:
首先先看ldh对象身上是否有sing方法,如果有就执行这个对象上的sing方法,如果没有sing这个方法,因为有_proto_的存在,就会去构造函数原型对象prototype身上去查找sing这个方法。
注意:
_proto_对象和原型对象prototype是等价的 _proto_对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype。
constructor构造函数
对象原型(_proto_)和构造函数(prototype)原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。
我们运行上述代码,可以看到:
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
比如我们打印如下语句,查看它们的constructor:
可以看到:
但是很多情况下,我们需要手动的利用constructor这个属性指回原来的构造函数:
比如我们共享的方法在prototype原型对象中也可以按对象方式存储:
这时我们再运行查看它们的构造器属性:
输出:
发现它们不再指向我们原来的Star构造函数了,而是指向了Object,为啥呢?其实也很好理解。
在我们原来的写法当中:
我们这样做相当于在Star这个构造函数中添加一些共享方法,所以我们的原型对象prototype指向Star。
但当我们写成下面这样时:
这相当于新创建了一个对象(字面量对象创建方式),这个对象中包含了两个方法。这个新对象会覆盖掉我们原来原型对象指向的Star构造函数,所以此时再度打印它们的constructor属性,它们会指向Object而不是Star。
所以我们现在需要手动的利用constructor这个属性去让原型对象prototype指回Star这个构造函数:
这就是constructor属性。
构造函数、实例、原型对象之间的三者关系
原型链
只要是个对象,就都存在原型_proto_,然后它指向原型对象prototype。
JavaScript的成员查找机制(规则)
1、当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性 2、如果没有,就查找它的原型(也就是_proto_指向的prototype原型对象) 3、如果还没有就查找原型对象的原型(Object的原型对象) 4、依次类推一直找到Object为止(null)
拓展内置对象
我们可以通过原型对象,对原来的内置对象进行拓展自定义的方法。比如给数组增加自定义求偶数和的功能。
我们可以打印一下数组的原型对象中拥有哪些方法:
运行上面程序可以看到:
其中有非常多的方法可供我们使用。
但是里面没有求和方法,那么我们可以自定义一个方法放到这里面去,以后就可以直接调用。