javascript的面向对象详解

每次说到javascript到面向对象,总感受本身内心懂,可是殊不知道该怎么说,这就是似懂非懂到表现,因而乎,每次一说,就要处处去查找资料,零零碎碎到看了一些,感受有懂了,可是过段时间,好像又不知道是怎么回事了,因而乎,又处处找资料,然道是我原本就缺对象?才不理解对象是啥,以致于现实中找找对象,javascript中也在找对象!哎,好尴尬啊!直到我看到了一个妹纸写到“不可不知的javascript面向对象”,我才明白面向对象是什么,这是否是说我要找到对象就是这个妹纸呢😄,先记录一下备忘吧,下面是妹纸写到主要内容:javascript

对象的建立:

1 建立一个面向对象
var obj = new Object(); 
obj.name = 'haha';
obj.showName = function(){ 
  alert(obj.name);
}
obj.showName();

 

缺点:当咱们想建立多个面向对象的时候,重复代码过多,须要封装,因此有了工厂方法。java

2 工厂方式app

function CreatePerson(name){ 
   var obj = new Object();   //原料
   obj.name = name;         //加工
   obj.showName = function(){
     alert(this.name);
 } 
   return obj;//出厂
}
var p1 = CreatePerson('haha');
p1.showName();
var p2 = CreatePerson('hehe');
p2.showName();
//其实就是简单的封装函数,整个过程像工厂的流水线,因此叫工厂方式

 

缺点:没法识别建立的对象的类型。由于所有都是Object,没有区分度,不像Date、Array等,所以出现了构造函数模式。函数

3 构造函数模式测试

function CreatePerson(name){ 
   this.name = name; 
   this.showName = function(){ 
     alert(this.name);
   } 
} 
var p1 =new CreatePerson('haha'); 
p1.showName();
var p2 = new CreatePerson('hehe'); 
p2.showName();

 

咱们经过这二个方面来改变:
1 函数名首字母大写
这是为了区别于普通的函数,构造函数自己其实就是普通的函数,只是咱们专门用它来实现了构造的功能,因此专门起了一个名字叫构造函数,任何函数均可以成为构造函数,这取决于你调用函数的方式,当使用了New的方式调用就成了构造函数。
2 New 关键字调用
调用函数的时候用了 New关键字,那么New到底作了什么?用不用New有什么区别?再来看下面的例子网站

function CreatePerson(name){
   this.name = name;
   this.showName = function(){
     alert(this.name); 
   }; 
    console.log(this);
} 
new CreatePerson('haha'); //CreatePerson
CreatePerson('haha');  //window

 

咱们会发现当用New去调用一个函数的时候,this的指向会不同。其实New主要作了下面这些事,不过下面写的只是大概的行为,并非内部源码。this

function CreatePerson(name){ 
   var obj = {}; //声明一个空对象obj 
   obj._proto_= CreatePerson.prototype;
   //把这个对象的_proto_属性指向构造函数的原型对象,这样obj就能够调用CreatePerson原型对象下的全部方法 ,这里原型先知道结论,下面会讲。
    CreatePerson.apply(obj);   //用apply方法让this指向obj对象
    this.name = name;   //obj对象添加属性,方法
    this.showName = function(){ 
       alert(this.name);
      }; 
    return obj;//返回这个对象
}

 

函数构造模式存在的问题:spa

alert(p1.showName==p2.showName);//false

 

缺点:可见这两个对象并非共用一个方法,每new一次,系统都会新建立一个内存,这两个对象各自有各自的地盘,但他们具备相同的功能,还不共用,确定不是咱们所但愿的。因此就有了下一种方法,原型+构造模式prototype

4 原型+构造模式指针

原型:每一个函数都有一个prototype属性,它是一个对象,也称做原型对象,咱们能够把方法和属性写在它上面(不过原型对象不只仅有咱们写的属性和方法,还有别的,下面会介绍),而经过这个函数建立出来的实例对象,都能共享这个原型对象下的方法和属性。因此咱们只须要把想要共享的东西放在函数的prototype下,不想共享的东西经过构造函数来建立就能够了。
看个栗子(原型+构造)

function CreatePerson(name){ 
  this.name = name;
}
CreatePerson.prototype.showName = function(){ 
   alert(this.name);
}
var p1 =new CreatePerson('haha');
p1.showName();
var p2 = new CreatePerson('hehe');
p2.showName();
alert(p1.showName==p2.showName);//true

 

测试为true,可见showName()方法是共享的,也就是说他们共用一个内存,更进一步的说它们存在引用关系,也就是说你更改了p1的showName也会影响p2的showName。

_proto_属性:
同一个函数造出来的实例对象能共享这个函数的prototype下的方法和属性,可是它是如何作到的呢?这里要出场的就是_proto_属性.
每一个实例化对象都有_proto_属性,它是一个指针,指向函数的prototype,也就是保存了它的地址。(JS中任何对象的值都是保存在堆内存中,咱们声明的变量只是一个指针,保存了这个对象的实际地址,因此有了地址就能找到对象),
因此总得来讲,每一个实例化对象都有_proto_属性,保存了构造函数的原型对象的地址,经过这个属性就能够拥有原型对象下的全部属性和方法,_proto_属性实际就是实例化对象和原型对象之间的链接

原型链:
每一个函数均可以成为构造函数,每一个函数都有原型对象,每一个原型对象也能够是一个实例化对象,好比,你建立了一个函数fun,它是构造函数function的实例化对象,而function的原型对象,又是Object的实例对象。因此fun有个_proto_属性能够访问到function的原型对象,function原型对象也是个实例对象,也有个_proto_属性,能够访问到Object的原型对象,因此经过_proto_属性,就造成了一条原型链。每一个实例化对象均可以访问到链子上方的方法和属性,因此fun是能够访问Object原型对象下的方法和属性的。实际上全部对象均可以访问到Object的原型对象。

原型链的访问规则:先在自身的下面寻找,再去一级一级的往原型链上找。
以下:

function Aaa(){}
Aaa.prototype.num = 3;
var a1 = new Aaa();
a1.num =10;
alert(a1.num); //10

 

原型对象:
原型对象下可能有三种属性:
1 原型对象所带方法和属性 2 constructor 3_proto_属性
constructor:构造函数属性,每一个函数的原型对象都有的默认属性,指向函数。
每一个实例化对象自己是没有constructor属性的,他们下面默认只有一个_proto_属性,用来链接原型对象,而和构造函数自己是没有直接的联系的。因此它的constructor是访问的原型对象上的。因此当原型对象的constructor变化了,实例化对象的constructor也会改变。可是若是这个对象自己既是原型对象,又是实例化对象,那就拥有了constructor属性,无需从原型对象上面访问。**

看下面的例子,来验证咱们所说的:

function CreatePerson(name){ 
   this.name = name;
}
CreatePerson.prototype.showName = function(){ 
   console.log(this.name);
 };
var p1 =new CreatePerson('haha');
p1.showName();
console.log(p1.constructor); // CreatePerson 来自CreatePerson.prototype

console.log(CreatePerson.prototype); 
// {showName:{},constructor:CreatePerson,__proto__:Object.prototype}
//可见,原型对象保存了
      1 自身添加的方法,
      2 构造函数constructor 
      3 _proto_(和上一层构造函数原型对象的链接)

console.log(CreatePerson.prototype.__proto__===Object.prototype);
// true 这个原型对象自己又是object的实例化对象,全部_proto_指向Object的原型对象

console.log(CreatePerson.prototype.__proto__===Object);
// false 可见是和构造函数下原型对象的链接,不是构造函数

console.log(CreatePerson.prototype.constructor);
//CreatePerson CreatePerson.prototype是Object实例化对象,也是原型对象,因此自身拥有constructor属性

console.log(Object.prototype.__proto__); 
// null 原型链的终点是null

console.log(CreatePerson.__proto__); //function.prototype
// CreatePerson自己既是构造函数又是function的实例化对象,拥有_proto_属性,指向function的原型对象

console.log(CreatePerson.constructor); 
// function 继承自function.prototype

console.log(CreatePerson.prototype instanceof CreatePerson ) 
//验证是否在一条原型链上 false

 

字面量法定义原型:
为了建立对象的代码更方便,你必定见过这样的代码,就是字面量法:

function Aaa(){}
Aaa.prototype = { 
  showName:function(){},
  showSex:function(){}
}; 
var a1 = new Aaa();
console.log(Aaa.prototype);
//{showName:function(){},_proto_} 
//你会发现constructor不见了,由于这种方式至关于从新赋值了Aaa.prototype 

console.log(Aaa.prototype.constructor);
//Object 由于自身没有了constructor属性,就去上级原型对象找,找到了Object
console.log(a1.constructor );
//Object 也变了,验证了它是访问的原型对象上的

 

所以咱们在写的时候须要修正一下原型的指向:

function Aaa(){}
Aaa.prototype = { 
constructor:Aaa, 
num1:function(){alert(10);}
}
var a1 = new Aaa();
a1.constructor    // Aaa

 

理解了这些,之后的继承就很好理解了,未完待续。若有错误,欢迎纠正。

 

转载出处: http://www.jianshu.com/p/845ad9b78201

 

感受简书,掘金等这样的博客类网站看的舒服一点,我是否是要转移阵地了!你以为呢?

相关文章
相关标签/搜索