JS面向对象,创建,继承
很开心,最近收获了很多知识,而且发现很多东西,以前理解的都是错的,或者是肤浅的,还以为自己真的就get到了精髓,也很抱歉会影响一些人往错误的道路上走,不过这也告诉了我们,看任何一篇文章都不能盲目的去相信,要实践验证再验证。今天就重新整理一下,我对面向对象的理解,当然也不保证完全正确的,但绝对是在进步的,抛砖引玉,希望能带来一些新的感悟。
对象,通俗的来说,就是属性和方法。定义就不再多说,下面说对象的创建:
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 构造函数模式
我们要通过这二个方面来改变:1 函数名首字母大写 2 New 关键字调用
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有什么区别?再来看下面的例子
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 res = {}; //声明一个空对象res
res._proto_= CreatePerson.prototype;//这个对象的_proto_属性指向构造函数的原型对象,这样res就可以调用CreatePerson原型对象下的所有方法
CreatePerson.apply(res);//把this指向改为res对象
this.name = name; //res对象添加属性,方法
this.showName = function(){
alert(this.name);
};
return res;//返回这个对象
}
关于New做时候都是内部的行为,看不到但确实存在,关于上面原型可以先大概知道结论,下面会说原型,接着看就懂了。
函数构造模式存在的问题:
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); //
原型对象下的方法和属性:原型对象下面可能有三大类: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:function(){},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 默认有的 指向创造它的函数 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(){alert(10);}
};
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
理解了这些,继承就很好理解了,下回继续补充。有错误,欢迎纠正。
JS面向对象,创建,继承的更多相关文章
- js面向对象之继承那点事儿根本就不是事
继承 说道这个继承,了解object-oriented的朋友都知道,大多oo语言都有两种,一种是接口继承(只继承方法签名):一种是实现继承(继承实际的方法) 奈何js中没有签名,因而只有实现继承,而且 ...
- 捋一捋js面向对象的继承问题
说到面向对象这个破玩意,曾经一度我都处于很懵逼的状态,那么面向对象究竟是什么呢?其实说白了,所谓面向对象,就是基于类这个概念,来实现封装.继承和多态的一种编程思想罢了.今天我们就来说一下这其中继承的问 ...
- JS——面向对象、继承
创建对象的方式: 1)单体 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...
- JS 面向对象之继承 -- 原型链
ECMAScript只支持实现继承,其实现继承主要是靠原型链来实现. 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾下构造函数.原型和实例的关系: 每个构造函数都有 ...
- JS面向对象组件 -- 继承的其他方式(类式继承、原型继承)
继承的其他形式: •类式继承:利用构造函数(类)继承的方式 •原型继承:借助原型来实现对象继承对象 类 : JS是没有类的概念的 , 把JS中的构造函数看做的类 要做属性和方法继承的时候,要分开继 ...
- JS 面向对象之继承---多种组合继承
1. 组合继承:又叫伪经典继承,是指将原型链和借用构造函数技术组合在一块的一种继承方式. 下面来看一个例子: function SuperType(name) { this.name = name; ...
- Js面向对象构造函数继承
构造函数继承 <!-- 创建构造函数 --> function Animal(){ this.species= '动物'; } function Dog(name,color){ this ...
- js面向对象之继承-原型继承
//animal 父类 超类 var Animal = function(name) { this.name = name; this.sayhello = function() { alert(&q ...
- JS面向对象(封装,继承)
在六月份找工作中,被问的最多的问题就是: js面向对象,继承,封装,原型链这些,你了解多少? 额,,,我怎么回答呢, 只能说,了解一些,不多不少,哈哈哈哈,当然,这是玩笑话. 不过之前学过java,来 ...
- JS面向对象笔记二
菜单导航,<JS面向对象笔记一>, 参考书籍:阮一峰之<JavaScript标准参考教程> 一.构造函数和new命令 二.this关键字 三.构造函数和new命令 四.构造函 ...
随机推荐
- Eclipse 中svn 分支,主干 合并与同步:
Eclipse 中svn的合并与同步: 1. 从主干拉取到分支: 然后一直下一步,到完成就OK了. 2. 从分支代码合并到主干: 2.1.先将本地需要提交更新的代码提交更新到svn分支去 2.2. ...
- iPhone 设置铃声
每次设置后,长时间不玩就忘记了,把写成博客:好记性不如烂笔头: 第一步:下载喜欢的音乐: 第二步,通过iTunes 文件 下的资料库 导入刚才下载的歌曲: 第三步:截取喜欢的部分,铃声最好设置在30秒 ...
- 细心!SQL语句进行运算时使用字符串时缺失精度的细节!
昨天没有更新,特此来说明下原因,昨天回到家时已经甚晚,正逢公司这几天项目比较紧张(bug多,赶需求,看着bug单齐刷刷的转过来,心都颤抖了一下),没有及时准备素材,今天又加了一天班(现在还在公司,偷个 ...
- 深入理解ajax系列第九篇——jQuery中的ajax
前面的话 jQuery提供了一些日常开发中需要的快捷操作,例如load.ajax.get和post等,使用jQuery开发ajax将变得极其简单.这样开发人员就可以将程序开发集中在业务和用户体验上,而 ...
- 20. Valid Parentheses - 括号匹配验证
Description: Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determin ...
- pg_config executable not found
Error: pg_config executable not found. Please add the directory containing pg_config to the PATH or ...
- 关于View Link
当需要表格之间的父子结构的时候需要展示时,这个时候就需要建立View Link来实现Table之间的关联.在建立ViewLink时需要现将JDev关闭然后再进行创建自己需要的ViewLink.
- SPOJ - VISIBLEBOX [multiset的使用]
tags:[STL][sort][贪心]题解:做法:先对数组a进行排序,再将数组a从头到尾扫一遍,使用multiset维护最小值,如果,即将放入集合的数字>=最小值的两倍,那我们就删除掉多重集合 ...
- php获取checkbox数组的表单数据
提交表单的时候,对于checkbox多选框,name="field[]",此时php获取的数组为:从0开始的索引数组:如果name="field[n]" 有数字 ...
- 整合初步--------->SSH(注解版)
上面的一篇博客已经介绍了 Spring和Hibernate之间的整合,没看过的童鞋可以去看看,这篇博客讲解Spring+Hibernate+Struts2注解版......... 个人觉得使用注解可能 ...