JavaScript OOP你现在还不懂,真OUT了!

原文出处:http://bbs.ibeifeng.com/read.php?tid=65315

  前段时间,认识的一学生,已经工作了近三年,Java,.net都有做过开发,去淘宝面试,回来后跟我聊,很受打击,说面试他的人其他的问的不多,就是对他的JavaScript语言感兴趣,但问他的即不是Jquery,也不蔭syUI,ExtJs等富客户端的东西,直接问JS最原生的特性及JS OOP相关的东西,他搞了这么多年开发,平时对jquery用的很熟,对JS基本会用,遇到效果了,网上找找,拿过来修修改改,凑合能用就行,至于JS的OOP,什么封装,闭包,原型链,JS的继承等几乎是一点也不懂,结果面试的结果可想而知了。 
 
      其实,听完他讲这些,我一点都不觉得奇怪,现在的软件开发逐渐向B/S端,移动开发方向发展,但不管哪个方向,如果要想做出一些高性能,高质量的程序,几乎对JS都会有着很高的要求,会一点JS对于普通 做网站可能就够用了,但是如果涉及到用户对体验要求很高,浏览器的兼容性要求高及一些特殊的领域如:GIS,自主开发工作流,绘制线路图等方面应用时,对JS的要求肯定会是相当高,而且这类人员的工资待遇也会相当高,做一个Web开发高手,或者是一个Web开发的老鸟的话,也一定会是要求对JS非常精通,而精通的前提就是要求理解并会使用JS OOP进行客户端技术开发。 
 
        本人有幸在10年前就从事过纯客户端产品的开发,很早接触JS及JS OOP,对这方面有一些自己的研究和心得,感觉网上这方面的资料虽然很多,但不系统,不完整,不成体系,接下来,我想快速的整理一下网上资料,让很多从来没有接触过JS OOP概念的朋友们,快速了解什么是JS OOP, 它的封装、继承、多态是怎么实现的,做一个扫盲。 
 
一、你必须知道的 
1) 字面量 
2) 原型 
3) 原型链 
4) 构造函数 
5) 稳妥对象(没有公共属性,而且其方法也不引用this的对象。稳妥对象适合用在安全的环境中和防止数据被其它程序改变的时候) 
 
二、开始创建对象吧 
<1>: 首先来看两种最基本的创建对象的方法 
1> 使用Object创建对象
 

复制代码
  1. var o = new Object();
  2. o.sname = 'JChen___1';
  3. o.showName = function(){
  4.     return this.sname;
  5. }
 
2> 使用对象字面量创建对象  
复制代码
  1. var o = {
  2.     name: 'JChen___2',
  3.     getName: function(){
  4.         return this.name;
  5.     }
  6. }
 
但是这两个方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。  
 
<2> 接下来看看几种创建对象的模式吧 
1>工厂模式
 
 
复制代码
  1. function create(name){
  2.     var o = new Object();
  3.     o.name = name;
  4.     o.sayName = function(){
  5.         return this.name;
  6.     };
  7.     return o;
  8. }
  9. var p1 = create('JChen___');
 
 
工厂模式也有一个缺点:就是没有解决对象识别的问题(即怎样知道一个对象的类型)。  
 
2> 构造函数模式  
 
复制代码
  1. function create2(name){
  2.     this.name = name;
  3.     this.sayName = function(){
  4.         return this.name;
  5.     };
  6.     //this.sayName = sayName;
  7. }
  8. //function sayName(){ return this.name};
  9. var p1 = new create2('JChen___4');
 
 
构造函数模式也有一个缺点:就是每个方法都要在每个实例上创建一遍。  
当然我们可以用上面的两行注释掉了代码来屏蔽上面那个缺点。  
但是……,我们又产生了一个新问题——全局变量。如果有很多方法,我们岂不是要定义很多个全局变量函数。这是个可怕的问题。  
 
3> 原型模式 
1) 普通方法
 
复制代码
  1. function create3(){}
  2. create3.prototype.name = 'JChen___5';
  3. create3.prototype.sayName = function(){
  4.     return this.name;
  5. };
  6. var p1 = new create3();
 
 
2) 原型字面量方法——我姑且这么称吧  
 
复制代码
  1. function create3(){}
  2. create3.prototype = {
  3.     constructor: create3, //我们要设置它的constructor,如果它很重要
  4.     name: 'JChen___5',
  5.     sayName: function(){
  6.         return this.name;
  7.     }
  8. };
  9. var p1 = new create3();
 
 
原型的缺点:  
1): 不能传参  
2): 共享了变量  
 
4> 构造+原型(模式)  
 
复制代码
  1. function create4(name){
  2.     this.name = name;
  3. }
  4. create4.prototype.sayName = function(){
  5.     return this.name;
  6. }
  7. var p1 = new create4('JChen___6');
 
这种模式是目前使用最广泛、认同度最高的一种创建自定义类型的方法。  
 
5> 动态原型模式  
 
复制代码
  1. function create5(name){
  2.     this.name = name;
  3.     if(typeof this.sayName != 'function'){
  4.         create5.prototype.sayName = function(){
  5.             return this.name;
  6.         }
  7.     }
  8. }
  9. var p1 = new create5('JChen___7');
 
 
这种方法确实也是十分完美的一种方法。  
 
6> 寄生构造函数模式  
 
复制代码
  1. function create6(name){
  2.     var o = new Object();
  3.     o.name = name;
  4.     o.sayName = function(){
  5.         return this.name;
  6.     }
  7.     return o;
  8. }
  9. var p1 = new create6('JChen___8');
 
 
注意那个return o。构造函数在不返回值的情况下,会返回新对象实例。而通过在构造函数的末尾加入return 语句,可以重写调用构造函数时返回的值。  
这个种用法可以用在,假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array的构造函数,因此可以使用这个模式。  
 
复制代码
  1. function specialArray(){
  2.     var values = new Array();
  3.     values.push.apply(values, arguments);
  4.     values.join2 = function(){
  5.         return this.join('|');
  6.     };
  7.     return values;
  8. }
  9. var colors = new specialArray('red', 'blue', 'green');
  10. colors.join2();//returned: red|blue|green
 
 
7>稳妥构造函数模式  
稳妥构造函数遵循与寄生构造函数类似的模式,但是有两点不同:  
一是新创建对象的实现方法不引用this  
二是不使用new操作符调用构造函数。  
 
复制代码
  1. functioncreate7(name){
  2.     var o = new Object();
  3.     var age = 12;                //私有变量
  4.     o.sayName = function(){      //私有方法
  5.         return name + ' ' + age;
  6.     }
  7.     returno;
  8. }var p1 = create7('JChen___9');
 
 
二、JS继承的6种方法  
1> 原型链继承
 
原型链继承是通过创建Super的实例,并将该实例赋值给Sub.prototype来实现的。  
实现的本质是:重写子类型的原型对象,代之以超类型的实例。  
 
复制代码
  1. function Super(){
  2.     this.name = 'JChen___';
  3. }
  4. Super.prototype.getSuperName = function(){
  5.     return this.name;
  6. }
  7. function Sub(){
  8.     this.subname = 'JChen___son';
  9. }
  10. Sub.prototype = new Super(); //原型继承体现在这里
  11. Sub.prototype.getSubName = function(){
  12.     return this.subname;
  13. }
  14. var instance = new Sub();
 
 
注意:此时instance.constructor现在指向的是Super的,这是因为Sub.prototype指向了Super.prototype,而Super.prototype.constructor = Super。  
原型链的问题:类似于利用原型创建对象,原型共享的特性也是原型链继承的最大问题。  
 
2> 借用构造函数继承  
在解决原型中包含引用类型值所带来的问题的过程中,我们开始使用一种叫做借用构造函数的技术。  
这种技术的基本思想相当简单:在子类型构造函数的内部调用超类型构造函数。  
这样一来,就会在新子类对象上执行超类函数中定义的所有对象初始化代码。结果,每个子类的实力都会有自己的超类中属性的副本了。  
 
复制代码
  1. function Super2(name){
  2.     this.colors = ['red', 'blue'];
  3.     this.name = name;
  4. }
  5. function Sub2(){
  6.     Super2.call(this, 'JChen___2'); //借用构造函数技术体现在这里
  7.     this.age = 29;
  8. }
  9. var instance1 = new Sub2();
  10. instance1.colors.push('black');
  11. var instance2 = new Sub2();
  12. instance2.colors.push('green');
 
 
借助构造函数继承的问题:  
1): 方法都在构造函数中定义,无法复用。  
2): 在超类型的原型中的方法对子类是不可见的。  
 
3> 组合继承(原型+借用构造)  
组合继承指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。  
组合继承的思路:使用原型链实现对方法和属性的继承,通过借用构造函数实现对实例属性的继承。  
 
复制代码
  1. function Super3(name){
  2.     this.name = name;
  3.     this.colors = ['red', 'blue'];
  4. }
  5. Super3.prototype.sayName = function(){
  6.     return this.name;
  7. }
  8. function Sub3(name, age) {
  9.     Super3.call(this, name);
  10.     this.age = age;
  11. }
  12. Sub3.prototype = new Super3(); //解决借用构造函数技术的缺点
  13. Sub3.prototype.constructor = Sub3; //纠正原型继承改变了的构造函数
  14. Sub3.prototype.sayAge = function(){
  15.     return this.age;
  16. }
 
 
组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,成为JavaScript中最常用的继承模式。  
组合继承的问题:两次调用超类构造函数。  
 
4> 原型式继承  
原型式继承的思路:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。  
 
复制代码
  1. function object(o){ //原型式继承的关键
  2.     function F(){}
  3.     F.prototype = o;
  4.     return newF();
  5. }
  6. var person = {
  7.     name: 'JChen___4',
  8.     colors: ['blue']
  9. }
  10. var person1 = object(person);
  11. person1.name = 'JChen___4___2'person1.colors.push('red');
  12. var person2 = object(person);
  13. person2.name = 'JChen___4___3';
  14. person2.colors.push('green');
 
 
原型式继承的问题:同原型链一样,他也有共享的劣势。  
 
5> 寄生式继承  
寄生式继承的思路:创建一个仅用于封装继承过程的函数,该函数内部以某种方式来增强对象,最后再返回该对象  
 
复制代码
  1. function createAnother(origin){ //寄生式继承的关键
  2.     var clone = object(origin);
  3.     clone.sayHi = function(){
  4.         return 'Hi';
  5.     };
  6.     return clone;
  7. }
  8. var person = {
  9.     name: 'JChen___4',
  10.     colors: ['blue']
  11. }
  12. var person1 = createAnother(person);
 
 
寄生式继承的问题:像构造函数一样,由于不能做到函数的复用而降低效率。  
 
6> 寄生组合式继承  
寄生组合式继承:通过借用构造函数来借用属性,通过原型链的混成形式来继承方法。  
其背后的思想是:不必为了指定子类型的原型而调用超类型的构造函数,我们需要的无非就是超类型的一个副本而已。  
 
复制代码
  1. function object(o){
  2.     function F(){}
  3.     F.prototype = o;
  4.     return new F();
  5. }
  6. function inheritProto(subType, superType){ //避免第一调用构造函数的关键
  7.     var proto = object(superType.prototype);
  8.     proto.constructor = subType;
  9.     subType.prototype = proto;
  10. }
  11. function Super6(name){
  12.     this.name = name;
  13.     this.colors = ['red', 'blue'];
  14. }
  15. Super6.prototype.sayName = function(){
  16.     return this.name;
  17. }
  18. function Sub6(name, age){
  19.     Super6.call(this, name);
  20.     this.age = age;
  21. }
  22. inheritProto(Sub6, Super6);
  23. Sub6.prototype.sayAge = function(){
  24.     return this.age;
  25. }
  26. var instance1 = new Sub6('JChen___6', '12');
  27. instance1.colors.push('black');
 
 
开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。  
三、总结  
这就是JavaScript中的6种继承方式,如果大家能够画出每个继承的原型链关系图,那么继承就是小菜一碟了  
了解了这些,你基本上对OOP有了一个入门,补充说明,以上内容均来自互联网,非本人原创。不过可能对于JS OOP重来没有接触过的同学看起来还是会觉得有些吃力,我最近也在整理这方面的资料并录制相关方向的课程, 目录列下:  
复制代码
  1. 1. part1_c05_01_01_前言-为什么你要学习JavaScript2. part1_c05_01_02_JS开发神器-WebStorm高级使用技巧
  2. 3. part1_c05_01_02_使用方括号([ ])引用对象的属性和方法
  3. 4. part1_c05_01_03_Web浏览器中JavaScript调试技巧
  4. 5. part1_c05_02_01_用定义函数的方式定义类
  5. 6. part1_c05_02_02_使用new操作符获得一个类的实例
  6. 7. part1_c05_02_03_动态添加、修改、删除JS对象的属性和方法
  7. 8. part1_c05_02_04_使用大括号({ })语法创建无类型对象
  8. 9. part1_c05_02_05_prototype原型对象
  9. 10. part1_c05_03_01_初识javascript函数对象
  10. 11. part1_c05_03_02_函数对象和其他内部对象的关系
  11. 12. part1_c05_03_03_将函数作为参数传递
  12. 13. part1_c05_03_04_传递给函数的隐含参数_arguments
  13. 14. part1_c05_03_05_函数的apply、call方法的运用
  14. 15. part1_c05_03_06_深入认识JavaScript中的this指针
  15. 16. part1_c05_04_01_理解javascript中类的实现机制
  16. 17. part1_c05_04_02_使用prototype对象定义类成员
  17. 18. part1_c05_04_03_JavaScript类的设计模式优化
  18. 19. part1_c05_05_01_JavaScript类的公有成员与私有成员
  19. 20. part1_c05_05_02_JavaScript类的静态成员
  20. 21. part1_c05_06_01_在JavaScript中利用for(…in…)语句实现反射
  21. 22. part1_c05_06_02_JS中利用反射动态设置CSS样式高级技巧
  22. 23. part1_c05_07_01_利用共享prototype实现继承的用法与缺陷
  23. 24. part1_c05_07_02_利用反射机制和prototype实现JS继承
  24. 25. part1_c05_07_03_参考prototype.js框架自实现JS中的类的继承
  25. 26. part1_c05_07_04_Prototype.js源码剖析与使用示例
  26. 27. part1_c05_08_01_在JavaScript中实现抽象类与虚方法
  27. 28. part1_c05_08_02_JavaScript中使用抽象类的示例
  28. 29. part1_c05_09_01_自定义实现JS中最简单的事件设计模式
  29. 30. part1_c05_09_02_重构自定义JavaScript事件处理程序解决事件传参问题
  30. 31. part1_c05_09_03_重构自定义JavaScript事件处理程序多事件绑定机制
  31. 32. part1_c05_10_01_JavaScript面向对象综合示例
  32. 33.part1_c05_11_01_JS压缩与混淆工具(JSA、JSCompressor、Google Closure Compiler )
  33. 34. part1_c05_11_02_JS高级调式工具(FireBugLite)
 
学完这套课程你能得到东西:  
想学习JS的调试,性能优化,及一些优秀前端工具的使用  
对JS的应用有更深层次了解  
对JS OOP思想有一定程度的了解  
自己能动手写一个基于JS OOP的简单框架  
对于后继jquery,easyui,extjs等富客户端的学习打好基础  
能用于Java,PHP,.NET开发,移动开发工作中  
上述课程的录制正在进行中,欢迎大家多对课程提宝贵意见,以改进内容,早日出来,以飨读者。  
另附:已经讲完的部分内容,大家可以下载下来看下,以加深对JS OOP的理解  
附下载:  
5. part1_c05_02_01_用定义函数的方式定义类  
6. part1_c05_02_02_使用new操作符获得一个类的实例  
7. part1_c05_02_03_动态添加、修改、删除JS对象的属性和方法  
8. part1_c05_02_04_使用大括号({ })语法创建无类型对象  
9. part1_c05_02_05_prototype原型对象  
10. part1_c05_03_01_初识javascript函数对象  
11. part1_c05_03_02_函数对象和其他内部对象的关系  
12. part1_c05_03_03_将函数作为参数传递  
13. part1_c05_03_04_传递给函数的隐含参数_arguments  
14. part1_c05_03_05_函数的apply、call方法的运用  
15. part1_c05_03_06_深入认识JavaScript中的this指针  
链接:  http://pan.baidu.com/s/1pJv09ib  
密码: lo92

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值