JavaScrpt的面向对象全面解析


Posted in Javascript onMay 09, 2017

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

对象的创建:

1 创建一个面向对象

var obj = new Object(); 
obj.name = 'haha';
obj.showName = function(){ 
 alert(obj.name);
}
obj.showName();

缺点:当我们想创建多个面向对象的时候,重复代码过多,需要封装,所以有了工厂方法。

2 工厂方式

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主要做了下面这些事,不过下面写的只是大概的行为,并不是内部源码。

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;//返回这个对象
}

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

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

缺点:可见这两个对象并不是共用一个方法,每new一次,系统都会新创建一个内存,这两个对象各自有各自的地盘,但他们具有相同的功能,还不共用,肯定不是我们所希望的。所以就有了下一种方法,原型+构造模式

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

JavaScrpt的面向对象全面解析

原型对象:

原型对象下可能有三种属性:

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

 以上所述是小编给大家介绍的JavaScrpt的面向对象全面解析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
OfflineSave离线保存代码再次发布使用说明
May 23 Javascript
动态加载script文件的两种方法
Aug 15 Javascript
jqGrid读取选择的多行的某个属性代码
May 18 Javascript
js实现鼠标点击文本框自动选中内容的方法
Aug 20 Javascript
JavaScript实现字符串与日期的互相转换及日期的格式化
Mar 07 Javascript
jQuery根据表单name获取值的方法
May 24 Javascript
AngularJs concepts详解及示例代码
Sep 01 Javascript
vue :src 文件路径错误问题的解决方法
May 15 Javascript
vue2.0自定义指令示例代码详解
Apr 25 Javascript
layui实现form表单同时提交数据和文件的代码
Oct 25 Javascript
原生js实现贪吃蛇游戏
Oct 26 Javascript
vue 动态添加的路由页面刷新时失效的原因及解决方案
Feb 26 Vue.js
ES6正则表达式的一些新功能总结
May 09 #Javascript
Vuex和前端缓存的整合策略详解
May 09 #Javascript
基于JS实现限时抢购倒计时间表代码
May 09 #Javascript
js使用i18n实现页面国际化的方法
May 09 #Javascript
Angular中$state.go页面跳转并传递参数的方法
May 09 #Javascript
Vue 2.0中生命周期与钩子函数的一些理解
May 09 #Javascript
JavaScript中splice与slice的区别
May 09 #Javascript
You might like
PHP中的CMS的涵义
2007/03/11 PHP
PHP性能优化工具篇Benchmark类调试执行时间
2011/12/06 PHP
带密匙的php加密解密示例分享
2014/01/29 PHP
php中file_get_contents与curl性能比较分析
2014/11/08 PHP
PHP动态柱状图实现方法
2015/03/30 PHP
jQuery+Ajax+PHP“喜欢”评级功能实现代码
2015/10/08 PHP
PHP微信开发之微信消息自动回复下所遇到的坑
2016/05/09 PHP
PHP receiveMail实现收邮件功能
2018/04/25 PHP
cssQuery()的下载与使用方法
2007/01/12 Javascript
初学JavaScript_03(ExtJs Grid的简单使用)
2008/10/02 Javascript
juqery 学习之三 选择器 子元素与表单
2010/11/25 Javascript
Javascript Throttle & Debounce应用介绍
2013/03/19 Javascript
jQuery之排序组件的深入解析
2013/06/19 Javascript
浅析JS刷新框架中的其他页面 && JS刷新窗口方法汇总
2013/07/08 Javascript
禁止IE用右键的JS代码
2013/12/30 Javascript
JS版的date函数(和PHP的date函数一样)
2014/05/12 Javascript
Node.js开发之访问Redis数据库教程
2015/01/14 Javascript
jquery实现submit提交表单
2015/02/03 Javascript
JavaScript实现基于十进制的四舍五入实例
2015/07/17 Javascript
基于PHP和Mysql相结合使用jqGrid读取数据并显示
2015/12/02 Javascript
通过隐藏iframe实现无刷新上传文件操作
2016/03/16 Javascript
JavaScript使用键盘输入控制实现数字验证功能
2016/08/19 Javascript
Bootstrap模态窗口源码解析
2017/02/08 Javascript
使用原生js+canvas实现模拟心电图的实例
2017/09/20 Javascript
详解node child_process模块学习笔记
2018/01/24 Javascript
angularjs下ng-repeat点击元素改变样式的实现方法
2018/09/12 Javascript
jQuery事件多次绑定与解绑问题实例分析
2019/02/19 jQuery
一个Java程序猿眼中的前后端分离以及Vue.js入门(推荐)
2019/04/19 Javascript
Pyramid将models.py文件的内容分布到多个文件的方法
2013/11/27 Python
常用python数据类型转换函数总结
2014/03/11 Python
Python中使用wxPython开发的一个简易笔记本程序实例
2015/02/08 Python
django创建最简单HTML页面跳转方法
2019/08/16 Python
Python3与fastdfs分布式文件系统如何实现交互
2020/06/23 Python
Qoo10马来西亚:全球时尚和引领潮流的购物市场
2016/08/25 全球购物
争先创优心得体会
2014/09/12 职场文书
vue3引入highlight.js进行代码高亮的方法实例
2022/04/08 Vue.js