首页 > 基础资料 博客日记
什么是面向对象?
2023-07-24 18:24:54基础资料围观235次
1什么是对象?
Everything is objec(万物皆对象),对象到底是什么,我们可以从两个层次来理解。
1.1对象是单个事物的抽象
一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。
1.2 对象是一个容器,封装了属性(property)和方法(method)
属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等) 在实际开发中,对象是一个抽象的概念,可以将其简单理解为 : 数据集或功能集。
2.什么是面向对象?
ECMAScript 有两种开发模式:1.面向过程(pop),2.面向对象(OOP)。面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但是,ECMAScript 没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
2.1面向过程编程POP(Process-oriented progamming)
面向过程就是分析解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个一次调用就可以了。
举个栗子:将大象装进冰箱(需要几步),面向过程做法
- 打开冰箱门
- 大象装进去
- 关山冰箱门
面向过程,就是按照我们分析好的步骤,按照步骤解决问题。
2.2面向对象编程OOP(Object oriented progamming)
面向对象是把事务分解成一个一个对象,然后对象之间分工与合作。
举个栗子:将大象装进冰箱(需要几步),面向对象做法。
先找出对象,并写出这些对象的功能:
- 打开冰箱门
*进去
- 冰箱对象
*打开
*关闭
- 使用大象和冰箱的功能
面向对象,是以对象功能来划分问题,而不是步骤。
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。面向对象编程具有灵活、代码可复用、容易维护和开发的优点,更适合多人合作的大型项目。
面向对象的特性:
- 封装性
- 继承性
- 多态性
3. 面向过程和面向对象比较
面向过程:
优点:性能比面向对象高,适合跟硬件联系很紧密的东西,列如单片机就采用的面向过程编程。
缺点:没有面向对象易维护、易复用、易扩展。
面对对象:
优点:易维护、易复用、易扩展。由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使用系统,使系统更加灵活、更加易于维护。
缺点:性能比面向过程低。
用面向过程写出来的方法写出来程序使一份炒面,而用面向对象写出来程序是一份蛋炒面。
4.概述
在典型的OOP语言中(如Java、PHP),都存在类的概念,类就对象模板,对象就是类的实例,但是在es6之前,js中并没有引入类的概念。
ES6,全程ECMAScript6.0,2015年.6月份发布,但是目前浏览器的js是ES5版本的,多数高版本浏览器也支持ES6,不过只实现了ES6的部分特性和功能。
在ES6的之前,对象不是基于类创建,而是用一种称为构建函数的特殊函数来定义对象和它门特征:
创建对象可以通过以下方式:
- 字面量创建对象
- New object()
- 自定义构造函数
5. 创建方法
5.2.1 字面量创建对象
<script type="text/javascript"> // 1.字面量创建对象; var obj={ name:"栗伟林", age:18, sex:"男", fn:function(a,b){ alert("我真帅") return a+b } } console.log(obj.name)// 第1种:访问属性 console.log(obj["age"])// 第2种:访问属性 console.log(obj.fn(1,2))// 访问方法 ,正常传递参数 </script> |
5.2.2 New object()
<body> <button id="btn" onclick="btn()">点点</button> </body> <script type="text/javascript"> var obj=new Object() //创建一个对象 obj.name="胖淇"; obj.age=89; obj.sex="不知道"; obj.fn=function(){ return this.name+ this.age+'运行中....' } </script> |
上面创建了一个对象,并且创建属性和方法,在run()方法里的this,就是代表box 对象本身。这种是JavaScript 创建对象最基本的方法,但有个缺点,想创建多个类似的对象,就会产生大量的代码。
为了解决多个类似对象声明的问题,我们可以使用一种叫做工厂模式的方法,这种方法就是为了解决实例化对象产生大量重复的问题。
5.2.3 工厂方法
function num(name,age){ //1. 原料 var obj=new Object(); //创建对象 //2.加工 obj.name=name; obj.age=age; obj.sun=function(){ alert("我的名字"+this.name) } //3.出厂 return obj; } var p1=num("栗伟林",18) var p2=num("张延涛",18) console.log(p1) console.log(p2) |
工厂模式:
原料
加工
出厂
[注]:凡是满足上诉三个步骤创建对象的函数,我们把它叫做工厂模式。
工厂模式解决了重复实例化的问题,但是它有许多问题,创建不同对象其中属性和方法都会重复建立,消耗内存;还有函数识别问题等等。
5.2.4 构造函数方法
//构造函数:就是把对象中的一些公共属性和方法抽取出来,封装到函数里面。 function fun(name,age){ // 在这里的this指向我们对象的调用者 this.fn=name; this.fn1=age; this.fn2=function(){ alert("我的名字叫"+this.fn+"我今年"+this.fn1+'') } } var zxy=new fun("张学友",78,"男","吻别") var ldh=new fun("刘德华",78,"男","冰雨") console.log(gfc.fn2()) |
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总是与new一起使用,我们可以把对象中的一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在js中,使用构造函数是要注意以下两点:
- 构造函数用于创造某一类对象,其首字母大写。
- 构造函数要和new一起使用才有意义。
New在执行时会做四件事情:
- 内存中创建一个新的对象。
- 让this指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象(所有构造函数里面不需要return)
构造函数中的属性和方法我们称为成员,成员可以添加。
function fun(name,age){ this.fn=name; this.fn1=age; this.fn2=function(){ alert("我的名字叫"+this.fn+"我今年"+this.fn1+'') } } var zxy=new fun("张学友",78,"男","吻别") var ldh=new fun("刘德华",78,"男","冰雨") console.log(gfc.fn2()) zxy.fn2() console.log(sun.fn) |
- 实例成员就是构造函数内部通过this添加的成员,fn、fn1、fn2就是实例成员。
- 不能通过构造函数来访问实例成员。
- 3.静态成员在构造函数添加的成员,sex 就是静态成员。
4.静态成员只能通过构造函数来访问。
[注]:
1)构造函数和普通函数的唯一区别,就是他们调用的方式不同。只不过,构造函数也是函数,必须用new 运算符来调用,否则就是普通函数。
2)this就是代表当前作用域对象的引用。如果在全局范围this 就代表window 对象,如果在构造函数体内,就代表当前的构造函数所声明的对象。
这种方法解决了函数识别问题,但消耗内存问题没有解决。同时又带来了一个新的问题,全局中的this 在对象调用的时候是sun 本身,而当作普通函数调用的时候,this 又代表window。即this作用域的问题。
5.2.5 构造函数原型(混合方式构造对象)
构造函数通过原型分配的函数是所有对象所共享的。
js规定每个构造函数都有一个prototype属性,指向一个对象,注意prototype就是一个对象,这个对象的所有方法和属性,都会被构造函数拥有。
我们可以把哪些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
问1:原型是什么?
答:一个对象,我们也称为prototype对象
问2:原型的作用?
答:共享方法。
function Box(){} //声明一个构造函数 Box.prototype.name="Lee" //在原型里添加属性 Box.prototype.age=20 Box.prototype.run=function(){ // 在原型里添加方法 return this.name+this.age+'运行中...'; } |
构造函数的声明方式和原型模式的声明方式存储情况如下:
所以,它解决了消耗内存问题。当然它也可以解决this作用域等问题。
我们经常把属性(一些在实例化对象时属性值改变的),定义在构造函数内;把公用的方法添加在原型上面,也就是混合方式构造对象(构造方法+原型方式)。
5.2.6 constructor 构造函数
1.对象原型__proto__和构造函数原型对象prototype都有一个属性,叫做constructor,称之为构造函数,主要记录该对象引用了哪个构造函数,可以让原型对象重新指向原来的构造函数,这就是为什么在实例对象中传递参数时,构造函数本身不需要写return返回结果的原因。
function Star(name,name){ this.name=name; this.name=name; } Star.prototype.uesrsing=function(){ console.log("prototype name") } var lxy=new Star(); console.log(lxy.__proto__) console.log(Star.prototype) |
为了更清晰的看到是否为构造函数本身:
console.log(lxy.__proto__.constructor) console.log(Star.prototype.constructor) |
构造函数原型对象中可以写多个方法
function Star(name,name){ this.name=name; this.name=name; } Star.prototype={ constructor:Star, uesrsing:function(){ console.log("uesrsing") }, uesrmove:function(){ console.log("uesrmove") } } var lxy=new Star(); |
注意以上代码,我在Star.prototype原型对象中虽然写了多个方法,但同时也添加了另一段代码constructor:Star,这是为了手动利用constructor指回原来的构造函数,如果不写此代码。则原来的构造函数就会被替代,如下代码和打印图。
console.log(lxy.__proto__.constructor) console.log(Star.prototype.constructor) |
如果添加了该代码,再次打印
console.log(lxy.__proto__.constructor) console.log(Star.prototype.constructor) |
再往上一层打印会更清晰
console.log(lxy.__proto__) console.log(Star.prototype) |
同时,就可以通过实例对象去调用构造函数的方法
lxy.uesrsing() lxy.uesrmove() |
构造函数、实例、原型对象三者之间的关系
5.2.7 JavaScript成员查找机制
对象成员查找规则
① 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
② 如果没有就查找它的原型(也就是 __proto__指向的
prototype 原型对象)。
③ 如果还没有就查找原型对象的原型(Object的原型对象)。
④ 依此类推一直找到 Object 为止(null)。
⑤ __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
栗1:
function Star(name,age){ this.name=name; this.age=age; } Star.prototype.sing=function(){ console.log("我会唱歌"); }; Star.prototype.sex='女'; var ldh=new Star(); ldh.sex='男'; console.log(ldh.sex)// 男 |
原型链:
分析:
1. 给ldh实例对象添加一个名叫sex的属性,并给它赋值为‘男’。
2. 给Star原型对象也添加一个名叫sex的属性,并给它赋值为‘女’。
3. 输出ldh实例对象的sex属性值,结果为‘男’。
栗2:
function Star(name,age){ this.name=name; this.age=age; } Star.prototype.sing=function(){ console.log("我会唱歌"); }; Star.prototype.sex='女'; Object.prototype.sex='男' var ldh=new Star(); ldh.sex='男'; console.log(ldh.sex)// 女 |
分析:
还是刚才那个例子。
给Star原型对象添加一个名叫sex的属性,并给它赋值为‘女’。给Object原型对象添加一个名叫sex的属性,并给它赋值为‘男’。输出ldh实例对象的sex属性值,结果为‘女’。
5.2.8 继承
继承是面向对象中一个比较核心的概念。其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript 只支持继承,不支持接口实现,而实现继承的方式依靠原型链完成。
在JavaScript 里,被继承的函数称为超类型(父类,基类也行,其他语言叫法),继承的函数称为子类型(子类,派生类)。
1.call+遍历
属性使用对象冒充(call)(实质上是改变了this指针的指向)继承基类,方法用遍历基类原型。
function A(){ this.abc=12; }; A.prototype.show=function(){ alert(this.abc) }; // 继承A function B(){ // 继承属性:this->new B() A.call(this) //有参数可以传参数A.call(this,name,age) }; //继承方法:B.prototype=A.prototype for(var i in A.prototype){ B.prototype[i]=A.prototype[i]; }; //添加自己的方法 B.prototype.fn=function(){ alert('abc') }; var objB=new B(); var objA=new A(); objB.show() |
可以实现多继承。
2.寄生组合继承
主要是Desk.prototype = new Box(); Desk 继承了Box,通过原型,形成链条。主要通过临时中转函数和寄生函数实现。
临时中转函数:基于已有的对象创建新对象,同时还不必因此创建自定义类型
寄生函数:目的是为了封装创建对象的过程。
// 临时中转函数 function obj(o){//o表示将要传递进入的一个对象 function F(){};//F构造是一个临时新建的对象,用来存储传递过来的对象 F.prototype=o;//将o对象实例赋值给F构造的原型对象 return new F();//最后返回这个得到传递过来对象的对象实例 }; //寄生函数 function create(box,desk){ var f=obj(box.prototype); f.constructor=desk; //调整原型构造指针 desk.prototype=f; }; function Box(name){ this.name=name; this.arr=['apple','pear','orange']; }; Box.prototype.run=function(){ return this.name }; function desk(name,age){ Box.call(this,name); this.age=age; } //通过寄生组合继承实现继承 create(Box,desk) var desk=new desk('lee',100); desk.arr.push('peach'); alert(desk,arr); alert(desk,run()); |
临时中转函数和寄生函数主要做的工作流程:
临时中转函数:返回的是基类的实例对象函数。
寄生函数:将返回的基类的实例对象函数的constructor指向派生类,派生类的prototype指向基类的实例对象函数(是一个函数原型),从而实现继承。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: