浅析面向对象

大家都知道,用面向对象进行编程可以使我们的代码更加结构化,具有更强的可读性和维护性,然而对于很多小伙伴们来说,面向对象一直是一个头疼的问题,当被问及面向对象特点,都知道是封装、继承和多态,但是具体原理不是很清晰,这里我将在解析的过程帮大家一一认识。

首先,我们来顺一下面向对象的逻辑:

a.编写一个可以创建实例对象的构造器;

b.调用构造器进行创建;

c.具体实例的创建。

下边进行一一讲解:

a.创建一个构造器(构造函数模式)

我们讲的第一种方式利用js特有的构造函数来编写构造器;

function Person(){
this.name="xiaowang";
this.weight="120";
this.height="170";
this.sayHello=function(){
alert("hello");
}
}
var person1=new Person();
console.log(person1.name);//xiaowang
person1.sayHello();//hello

上述代码中,Person函数就是构造函数,创建方法与普通函数是一样一样的,只是内部结构有些不同,其实就是函数;person1就是就是我们创建的实例对象;new大家应该不陌生,是js调用构造函数的特殊关键词,例如obj=new Object(),arr=new Array();这样person1就具有了Person的所有属性及方法;我们就可以通过person1来调用他们;这一段代码通过Person函数很好的封装了起来,这就是面向对象的第一个特点:封装;

这里可能很多小伙伴看出来了,我们通过创建调用Person创建的实例都是一样的,如果我们想要创建多个不同的实例该怎么办呢,这是一个很好的问题,上代码

function Person(name,weight,height){
this.name=name;
this.weight=weight;
this.height=height;
this.sayHello=function(){
alert("hello"+name);
}
}
var person1=new Person("xiaowang",120,170);
var person2=new Person("xiaozhang",130,180);
console.log(person1.name);//xiaowang
person1.sayHello();//hello xiaowang
console.log(person2.name);//xiaozhang
person2.sayHello();hello xiaozhang

这里我们通过传入不同的参数动态改变了构建函数的属性,从而构建了不同的实例对象;因此,通过传入不同的参数,创建不同的实例对象,这就是面向对象编程的第二个特点:多态;

大家看到上面的代码是不是感觉面向对象好简单,那就太天真了,接下来才是重中之重;首先我们来了解一下,创建实例对象时都实现了那些逻辑:

(1)创建了一个实例对象的作用域;

(2)构造函数内部的this指针指向实例对象的作用域;

(3)然后执行构造函数的逐行代码,也就是this赋值;

(4)最后分别再实例对象上创建属性和方法;

这样我们就完成了实例对象的创建;但是构造函数是有缺点的,这种做法非常费内存,原因是当你创建一个实例对象时,构造函数就会执行一遍,当你创建很多实例对象时就会执行很多遍;然而里边的赋属性,赋方法的操作都是一模一样的,这样就会造成非常严重的内存浪费,以及代码的重复执行,非常不环保;

因此,为了解决上述问题,我们的大牛们就推出了面向对象的原型模式:

例子:

 function Person(){
}
Person.prototype.name="xiaowang";
Person.prototype.weight="120";
Person.prototype.height="170";
Person.prototype.sayHello=function(){
alert("hello"+this.name);
}
var person1=new Person();
console.log(person1.name);
person1.sayHello();

这就是原型模式;这里我简单介绍一下原型规则,有助于大家理解原型;

a.所有的引用类型(对象、数组、函数),都有对象特性,即可自由扩展属性(null除外)。

b.所有的引用类型,都有一个_proto_(隐式原型)属性,属性值是一个普通的对象。

C.所有的引用类型,都有一个prototype(显式原型)属性,属性值是一个普通的对象。

d.所有的引用类型,__proto__的属性值指向他的构造函数的prototype的值。

e.当试图得到一个引用类型的一个属性时,如果这个对象本身没有这个属性,那么会去它的__proto__(即他的构造函数的prototype)中寻找;

是不是有点儿眉目了:构造函数模式创建实例与原型模式创建实例的区别就在于,一个是值传递,一个是引用传递;也就是说,原型模式再创建实例对象时会给实例对象赋上一个指向构造函数的显式原型属性(prototype)的指针,实例对象本身可以用__proto__属性来查看构造函数的原型属性;

也许单单这样我们还看不出他们的区别,下边上例子:

function Person(){
this.name="xiaozhang";
this.like="foot";
}
var person1=new Person();
var person2=new Person();
person1.like="jump";
console.log(person1.liek);//jump
console.log(person2.like);//foot;
我们可以看到,我们对person1的like属性改为了jump,如果我们需要对person2的like属性也改为jump,就需要重新赋值,如果我们有很多实例都需要改为这一属性值jump呢,岂不是要改好多遍,这样显然是不合理的;原型模式就很好的解决了这个问题

function Person(){
}
Person.prototype.name="xiaowang";
Person.prototype.like="foot";
var person1=new Person();
var person2=new Person();
person1.__proto__.like="jump";
console.log(person1.liek);//jump
console.log(person2.like);//jump
通过上边原型规则就应该知道,通过改变__proto__属性就相当于改变了实例的构造函数的原型属性(prototype);这样即使我们有很多实例,只要我们修改一个,就会映射到所有的实例对象中。这时你可能发现给构造函数添加很多属性时,就有些麻烦,于是我们可以这样:
function Person(){
}
Person.prototype={
constructor:Person,
name:"xiaowang",
like:"foot",
sayHello:function(){
alert("hello")
}
}

通过上述方法,给prototype重新赋值一个对象时,就可以快速添加很多属性与方法,但是这时我们的prototype已经不再指向老的原型对象而是新的原型对象(参见js高级程序设计第三版p154-156).

有的小伙伴看到这里是不是已经跃跃欲试了,但是细心的小伙伴可能发现了由于原型模式搭建的构造器是引用传递,也就是说所有的实例对象共用所有的原型属性与方法,当我们想要创建不一样的实例时,这样就行不通了,所以原型模式也不是我们最终的面向对象模式。

这里大胆的小伙伴可能会这样想,我们能不能上边两种方式一起用呢,恭喜你,答对了,这就是我们现在最常用的混合模式:

上例子:

function Person(name,age,like){
this.name=name;
this.age=age;
this.like=like;
}
Person.prototype={
constructor:Person,
like:"sleep",
sayLike:function(){
return ("my like is"+this.like);
}
}
var person1=new Person("wang",20,"foot");
var person2=new Person("zhang",21,"jump");
console.log(person1.like);
console.log(person2.like);
console.log(person1.sayLike());
console.log(person2.saylike());

以上就是我们的混合模式了,也是我们在实际开发中要用的模式,喜欢的可以收藏哦,有不对的地方希望指正

猜你喜欢

转载自blog.csdn.net/weixin_39141802/article/details/79529946
今日推荐