不少开发对java script实现面向对象编程存在一知半解,并且不少的在项目实践中写的都是面向过程编程的代码,因此,希望能从零入手介绍面向对象的一些概念到实现简单的面向对象的例子让大家包括我自己加深对面向对象的认知。硬文一篇,希望能对大家有帮助 ^v^
对象是一个包含相关数据和方法的集合,是通过变量和函数组成,通常被我们说成属性和方法,常用对象字面量的形式表示。
1.初始化对象
var person={}
2.添加属性(变量)和方法(函数)
var person={
name:'aaron',
say:function(){
alert('hello')
}
}
3.获取属性和执行方法
备注:获取属性和执行方法有两种方法,就是说我上面列举的其一:点表示法,还有一种就是括号表示法。如下:
person['name']
person['say']()
因此,有时对象也被叫做关联数组,即对象做了字符串到值的映射,而数组做的是数字到值的映射。
4.运行截图
5.设置对象成员
备注:有一点需要了解到的是,括号表示法能做到通过定义变量名的方式去设置对象成员,而这一点是点表示法没法实现的。
6.“this”的含义
this的指向其实是一直都让开发者头大的问题了,尤其是后端写JS时。其实说白了this就是指向了当前代码运行时的对象。
例如:
由于对象字面量执行的是当前对象,所以this指向了person。而像创建的构造函数等this的指向就是构造函数实例对象了
一目了然,对象字面量创建的对象的好处可以有效的把对象关联的属性和方法统一了起来,也减少了全局变量的污染,得到一定程度的安全(减少了定义全变量覆盖对象属性的危险)。
例如从现实世界的某个实例出发,对于一个人(person)来说,我们能在他们身上获取到很多信息(他们的住址,身高,鞋码等等),然后我们会基于这些信息介绍关于他们,并需要他们做出回应。而在面向对象的语言中,我们就可以通过类(class)的概念去描述一个对象,而这个类就是定义对象特质的模板。通过创建的class,我们就可以基于它来创建一些拥有class中属性和方法的对象,即实例对象。而这些实例对象一般是具体的人,例如老师,学生。在OOP中,我们也可以基于这个class,创建其他的新类,而这些新的子类(例如家长)可以继承它们父类的属性(数据)和方法(功能),来使用父对象共有的功能。
因此,通过对泛指人到具体的某个学生/老师的关系,我们就可以总结到面向对象的三个基本特性:封装,继承,多态。
通过了解面向对象编程(OOP)的基本概念,什么是对象和对象的属性,方法,并了解实现面向对象编程的基本特性。也了解常用的创建对象方法--对象字面量,我们已经对对象的基本概念有了了解。但是,通过对象字面量来创建的只是单一的实体类,并不能实现通用对象(现实模型)的封装,即真正的实现面向对象。
java script是通过构建函数的方式来定义对象和特征的,而构建的实例对象也有通过原型链的方式来继承某些特性。
1.Person()构造函数,创建实例对象并访问属性和方法:
2.其他创建对象实例的姿势
1.Object()构造函数
var person1=new Object();
person1.name='ace';
person1.age=30;
person1.greeting=function(){
alert('Hi! I\'m ' + this.name + '.'')
}
2.使用create():这样就可以基于person1创建与person1具有相同属性和方法的对象。
var person2=Object.create(person1);
java script的继承机制是有别于其他经典的面向对象编程语言的,是通过原型来实现从其他对象继承功能特性的。
因此,java script常被描述为基于原型的语言--每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性,原型对象也有可能拥有原型,从中继承方法和属性,以此类推。而这种关系被称为原型链。
我们需要知道的是,这些属性和方法是定义在实例的构造函数上的prototype属性,当然,实例对象也__proto__属性,是从构造函数的prototype属性派生的,即实例对象.__proto===构造函数.prototype。
从截图我们看到,person1实例除了具有Person()构造器中的属性方法外,还具有其他属性和方法,而这些则是Person()构造器原型对象Object上的成员。
通过调用valueOf,因此,我们也了解到了调用方法的过程:
1.浏览器首先检查,person1 对象是否具有可用的 valueOf() 方法。
2.如果没有,则浏览器检查 person1 对象的原型对象(即 Person)是否具有可用的 valueof() 方法。
3.如果也没有,则浏览器检查 Person() 构造器的原型对象(即 Object)是否具有可用的 valueOf() 方法。Object 具有这个方法,于是该方法被调用。
通过对valueOf方法的调用过程,我们也就了解到了那些能被继承的属性和方法(对象中存在不能被继承的属性方法,例is()/keys())是定义在prototype属性上的。因此,在构造函数是需要被子类继承的属性方法需要定义在prototype上。
每个实例对象都有constructor属性,它是指向创建该实例的构造函数。
而我们也可以通过在constructor后添加()形式实现创建新实例。
通过截图我们可以了解到了,虽然已经创建了实例对象person1,当时之后再像构造器Person()prototype中添加方法,person1还是能调用,这就说明了函数调用会通过上溯原型链,从上游对象中调用方法。
如图,若在prototype上定义属性的话,则this的当前执行环境为全局,返回的为undefined。并且,在对象继承上看,一般的做法是在构造器中定义属性,在prototype属性中定义方法。
function Person(first, last, age, gender, interests) {
t