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指向基类的实例对象函数(是一个函数原型),从而实现继承。

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信