zoukankan      html  css  js  c++  java
  • 深入JavaScript对象创建的细节

    最近深入学习javascript后,有个体会:面向对象的方式编程才是高效灵活的编程,也是现在唯一可以让代码更加健壮的编程方式。如果我们抛开那些玄乎的抽象出类等等思想,我自己对面向对象的从写程序的角度理解就是:复用和封装。复用具体就是让你尽量少写重复代码,封装就是将一些耦合度很高的逻辑放到一个程序块里,而且尽量让里面内容不受外界影响。最后的结论是:优秀的javascript代码都是面向对象的。

    如何构建javascript对象?ECMA-262对对象的定义是:无序属性的集合,其属性可以包含基本值、对象或函数。javascript的对象其实就是java里的map,即键值对。

    在javascript创建一个对象一共有三种方式:

    1. 通过Object对象
    2. 通过构造函数
    3. 对象初始化

    1. 通过Object对象来构建对象,代码如下:

    01 //直接使用Object创建对象
    02  
    03 var obj = new Object();
    04 obj.id = '001';
    05 obj.name = 'My name is obj';
    06 obj.teststring = 'Test obj';
    07 obj.sayHello = function()
    08 {
    09     console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    10 }
    11  
    12 obj.sayHello();//id:001@!@name:My name is obj@!@teststring:Test obj
    13 obj['sayHello']();//id:001@!@name:My name is obj@!@teststring:Test obj
    14 var str = 'sayHello';
    15 obj[str]();//id:001@!@name:My name is obj@!@teststring:Test obj

    注意:我这里使用了两种访问对象属性的方式,一种是点运算符,一种是方括号运算符,二者是等价的,但是方括号运算似乎要更强大些,方括号里面我们可以放置变量。

    这是最常用,最直观的一种创建对象方法,但是它的缺点太明显了,就是代码复用度很低,我们想到一个对象就创建一个对象,如是就会造成大量的重复代码,因此,javascript程序员将工厂模式引入到了javascrip编程里,请大家看下面的代码:

    01 // 用工厂模式创建对象
    02  
    03 function createObj(id,name,teststring)
    04 {
    05     var o = new Object();
    06     o.id = id;
    07     o.name = name;
    08     o.teststring = teststring;
    09     o.sayHello = function()
    10     {
    11         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    12     }
    13     return o;
    14 }
    15 var obj = createObj('002','My Name is obj2','Test Obj2');
    16 obj.sayHello();//id:002@!@name:My Name is obj2@!@teststring:Test Obj2
    17 obj['sayHello']();//id:002@!@name:My Name is obj2@!@teststring:Test Obj2
    18 var str = 'sayHello';
    19 obj[str]();//id:002@!@name:My Name is obj2@!@teststring:Test Obj2

    工厂模式解决了创建相似对象的问题,如果抛开它构造对象的对象识别问题,工厂模式挺完美的,如果你做的javascript应用不是太复杂,建议使用工厂模式构造对象,这种写法可读性很高。

    2. 通过构造函数

    几乎所有使用构造函数方式构建对象都会使用到new运算符,javascript里面的构造函数比较特别的,在javascript里没有类的概念,new 后面跟的直接是构造函数,大家看下面的代码:

    01 // 使用构造函数创建对象
    02  
    03 function Obj(id1,name1,teststring1)
    04 {
    05     this.id = id1;
    06     this.name = name1;
    07     this.teststring = teststring1;
    08     this.sayHello = function()
    09     {
    10         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    11     }   
    12 }
    13  
    14 var obj = new Obj('003','My Name is obj3','Test Obj3');
    15 obj.sayHello();//id:003@!@name:My Name is obj3@!@teststring:Test Obj3
    16 obj['sayHello']();//id:003@!@name:My Name is obj3@!@teststring:Test Obj3
    17 var str = 'sayHello';
    18 obj[str]();//id:003@!@name:My Name is obj3@!@teststring:Test Obj3

    构造函数式和工厂模式从代码角度而言很像,我在学习javascript初期,单独看一种方式,思维总是惯性的把二者混为一谈,如果两个放在一起还是觉得有差异,主要是javascript里面function被赋予的功能太多,如果在一个大型程序里面二者交替使用,不晕头才怪了。其实通过两者构造对象的不同,我可以把function的使用分为构造函数式和函数式。下面我将重点分析下两种方式,这里的见底或许不全面,要是哪位高手看到了可以补充和指点哈。

    首先我去掉方法里各个属性的this指针,而sayHello里面引用的this指针不去掉。代码如下:

    01 // 深入分析构造函数1
    02 function Obj(id1,name1,teststring1)
    03 {
    04     id = id1;
    05     name = name1;
    06     teststring = teststring1;
    07     sayHello = function()
    08     {
    09         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    10     }   
    11 }
    12 var obj = new Obj('004','My Name is obj4','Test Obj4');
    13 sayHello();//id:undefined@!@name:@!@teststring:undefined
    14 window.sayHello();//id:undefined@!@name:@!@teststring:undefined
    15 obj.sayHello();//obj.sayHello is not a function  [在此错误处中断] obj.sayHello();

    this永远指向obj的,而sayHello方法里的this.id等等属性的值为undefined,表明Obj的function对象没有id,name,teststring属性,就连sayHello方法在Obj里面也没有定义好,但是直接sayHello();可以找到sayHello方法,最后我们使用window.sayHello();才知道sayHello是被定义在window里面了。如是我去掉Obj里面所有this,代码如下:

    01 // 深入分析构造函数2
    02  
    03 function Obj(id1,name1,teststring1)
    04 {
    05     id = id1;
    06     name = name1;
    07     teststring = teststring1;
    08     sayHello = function()
    09     {
    10         console.log('id:' + id + '@!@name:' + name + '@!@teststring:' + teststring);
    11     }   
    12 }
    13 var obj = new Obj('005','My Name is obj5','Test Obj5');
    14 sayHello();//id:005@!@name:My Name is obj5@!@teststring:Test Obj5
    15 window.sayHello();//id:005@!@name:My Name is obj5@!@teststring:Test Obj5
    16 console.log(id);//005
    17 console.log(name);//My Name is obj5
    18 console.log(teststring);//Test Obj5
    19 console.log(window.id);//005
    20 console.log(window.name);//My Name is obj5
    21 console.log(window.teststring);//Test Obj5

    由上面内容我得出了下面的结论:

    1. javascript是可以做面向对象编程的,我们不能把它当做面向过程的语言;
    2. javascript里面对象的创建是特别的,特别在于它和传统的面向对象语言比较起来做了简化,简化到直接使用构造函数来创建对象;
    3. function在javascript里面既可以当做函数式使用,又可以作为构造函数的标示(你也可以直接当做类来看待),而区别构造函数式和函数式的区别就是new;
    4. 如果我们用到了构造函数式才创建对象,那么这里和其他面向对象语言一样,也是有封装的,换种说法是里面定义的属性或者方法是属于该对象的,而让其属于该对象的方式就是用this指针,否则属性和方法将属于window对象。

    如果我们不用new运算符,直接使用函数,例如下面代码:

    01 // 深入分析函数式1
    02  
    03 function Obj(id1,name1,teststring1)
    04 {
    05     this.id = id1;
    06     this.name = name1;
    07     this.teststring = teststring1;
    08     this.sayHello = function()
    09     {
    10         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    11     }   
    12 }
    13 Obj('006','My Name is obj6','Test Obj6');
    14 sayHello();//id:006@!@name:My Name is obj6@!@teststring:Test Obj6
    15 window.sayHello();//id:006@!@name:My Name is obj6@!@teststring:Test Obj6
    16 console.log(id);//006
    17 console.log(name);//My Name is obj6
    18 console.log(teststring);//Test Obj6
    19 console.log(window.id);//006
    20 console.log(window.name);//My Name is obj6
    21 console.log(window.teststring);//Test Obj6

    直接调用function,this指针都是指向window,也就是全局对象,我以前看到过一句话:不论哪里直接调用函数,里面的this都是指向全局的。这个不论哪里直接调用函数就有学问了啊,看下面代码:

    01 // 深入分析函数式 2
    02  
    03 function OuterObj(id1,name1,teststring1)
    04 {
    05     this.id = id1;
    06     this.name = name1;
    07     this.teststring = teststring1;
    08     this.sayHello = function()
    09     {
    10         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    11     }   
    12      
    13     function InnerObj(id2,name2,teststring2)
    14     {
    15         this.id = id2;
    16         this.name = name2;
    17         this.teststring = teststring2;
    18         this.sayHello = function()
    19         {
    20             console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    21         }   
    22     }
    23      
    24     var innerVal = new InnerObj('101','InnerObj','Test InnerObj');//true
    25     console.log(innerVal instanceof InnerObj);
    26     innerVal.sayHello();//id:101@!@name:InnerObj@!@teststring:Test InnerObj
    27      
    28     InnerObj('102','InnerObj0','Test InnerObj0');
    29 }
    30  
    31 var outObj = new OuterObj('007','My Name is obj7','Test Obj7');
    32 outObj.sayHello();//id:007@!@name:My Name is obj7@!@teststring:Test Obj7
    33 sayHello();//
    34 window.sayHello();//id:102@!@name:InnerObj0@!@teststring:Test InnerObj0
    35 console.log(id);//102
    36 console.log(name);//InnerObj0
    37 console.log(teststring);//Test InnerObj0

    由此可见只要是函数调用this指向的方法和函数都是指向window的,即全局对象。

    3. 对象初始化

    对象初始化方式在有些书上也叫字面量方式构建对象,但是我更喜欢对象初始化方式,这样更直观,代码如下:

    01 // 对象初始化
    02  
    03 var obj = {
    04     id:'008',
    05     name:'My Name is obj8',
    06     teststring:'Test Obj8',
    07     sayHello:function(){
    08         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    09     }
    10 };
    11 obj.sayHello();//id:008@!@name:My Name is obj8@!@teststring:Test Obj8
    12 obj['sayHello']();//id:008@!@name:My Name is obj8@!@teststring:Test Obj8
    13 var str = 'sayHello';
    14 obj[str]();//id:008@!@name:My Name is obj8@!@teststring:Test Obj8

    这种方式是我比较喜欢的一种构建对象的方式,有位javascript大师级的人物曾经建议构建空对象,最好是var obj = {},这句话就表明var obj = {}和var obj = new Object();是等价的。var obj = {}方式更加简洁,避免了o.id;o.name等等繁琐的写法,jQuery源码里面大量使用这样的构造对象的方式。

    以上就是我熟知的三种在javascript里面构建对象的方式。下面我将要换个角度来理解javascript构建对象的知识。首先看一段java的代码:

    1 public class Obj {
    2     public static String staticParams = "静态属性,它是属于类的";
    3     private String objId = "编号是对象私有的";
    4     public String objName = "名称是对象公有的";
    5      
    6     //构造函数
    7     public Obj(){}
    8 }

    javascript对象的属性或方法也可以使用java里的思想分为:属于类的属性和方法,属于对象的属性或方法,还有公有属性和方法以及私有属性和方法。

    1. 属于类的属性和方法:用对象初始化的方式都可以当做是属于类的属性和方法,这种定义在jQuery里面大量运用;
    2. 属于对象的属性或方法:用构造函数的方式构建对象,里面用this指针指向的属性和方法都是属于对象的;
    3. 公有属性和方法:javascript里面属性和方法天生就是公有的;
    4. 私有属性和方法:这个在javascript里面要通过模拟才能实现,这种模拟一般是使用作用域的原理,比如:在一个function内部用var来定义对象,对象作用域属于改function内部,那么在函数外部是不能访问的,这就是私有变量和方法了。

    以上对象的创建,里面的属性和方法操作都是对象专有的,用相同方式构建的类似对象不能做到信息的共享,那如何才能让类似的对象能共享信息了,这就的使用prototype原型链了。

    在javascript里面每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途包含可以由特定类型所有实例共享的属性和方法。

    代码如下:

    01 //原型模式
    02  
    03 function Obj(id1,name1,teststring1)
    04 {
    05     this.id = id1;
    06     this.name = name1;
    07     this.teststring = teststring1;
    08     this.sayHello = function()
    09     {
    10         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    11     }   
    12 }
    13  
    14 Obj.prototype.objflag = '自建的Obj对象';
    15 Obj.prototype.proSayHello = function()
    16 {
    17     console.log('Hello World and Obj');
    18 }
    19  
    20 var obj1 = new Obj('008','My Name is obj8','Test Obj8');
    21 var obj2 = new Obj('009','My Name is obj9','Test Obj9');
    22  
    23 obj1.sayHello();//id:008@!@name:My Name is obj8@!@teststring:Test Obj8
    24 obj2.sayHello();//id:009@!@name:My Name is obj9@!@teststring:Test Obj9
    25 obj1.proSayHello();//Hello World and Obj
    26 obj2.proSayHello();//Hello World and Obj
    27 console.log(obj1.objflag);//自建的Obj对象
    28 console.log(obj2.objflag);//自建的Obj对象

    prototype写起来太繁琐了,而且又不好记,一般许多实例公用的属性和方法都是固定的不容易改变了,那么我们可以用初始化对象的方式来写原生链,这个思路我是从jQuery里学到的,代码如下:

    01 //简化的原型模式
    02  
    03 function Obj(id1,name1,teststring1)
    04 {
    05     this.id = id1;
    06     this.name = name1;
    07     this.teststring = teststring1;
    08     this.sayHello = function()
    09     {
    10         console.log('id:' this.id + '@!@name:' this.name +'@!@teststring:' this.teststring);
    11     }   
    12 }
    13 Obj.prototype = {
    14     objflag:'自建的Obj对象',
    15     proSayHello:function(){
    16         console.log('Hello World and Obj');
    17     }
    18 };
    19 var obj1 = new Obj('010','My Name is obj10','Test Obj10');
    20 var obj2 = new Obj('011','My Name is obj11','Test Obj11');
    21  
    22 obj1.sayHello();//id:010@!@name:My Name is obj10@!@teststring:Test Obj10
    23 obj2.sayHello();//id:011@!@name:My Name is obj11@!@teststring:Test Obj11
    24 obj1.proSayHello();//Hello World and Obj
    25 obj2.proSayHello();//Hello World and Obj
    26 console.log(obj1.objflag);//自建的Obj对象
    27 console.log(obj2.objflag);//自建的Obj对象
  • 相关阅读:
    黑马程序员——指针的应用
    黑马程序员——C语言基础常量、运算符、函数
    黑马程序员——数组
    黑马程序员——循环结构for,while,do..while
    webView去掉右侧导航条
    使用Eclipse构建Maven的SpringMVC项目
    win7 自动登录
    eclipse 自动提示
    apache+php+mysql 环境配置
    KMP子串查找算法
  • 原文地址:https://www.cnblogs.com/xiaoyang002/p/4083981.html
Copyright © 2011-2022 走看看