最近深入学习javascript后,有个体会:面向对象的方式编程才是高效灵活的编程,也是现在唯一可以让代码更加健壮的编程方式。如果我们抛开那些玄乎的抽象出类等等思想,我自己对面向对象的从写程序的角度理解就是:复用和封装。复用具体就是让你尽量少写重复代码,封装就是将一些耦合度很高的逻辑放到一个程序块里,而且尽量让里面内容不受外界影响。最后的结论是:优秀的javascript代码都是面向对象的。
如何构建javascript对象?ECMA-262对对象的定义是:无序属性的集合,其属性可以包含基本值、对象或函数。javascript的对象其实就是java里的map,即键值对。
在javascript创建一个对象一共有三种方式:
- 通过Object对象
- 通过构造函数
- 对象初始化
1. 通过Object对象来构建对象,代码如下:
03 |
var obj = new Object(); |
05 |
obj.name = 'My name is obj' ; |
06 |
obj.teststring = 'Test obj' ; |
07 |
obj.sayHello = function () |
09 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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 |
15 |
obj[str](); //id:001@!@name:My name is obj@!@teststring:Test obj |
注意:我这里使用了两种访问对象属性的方式,一种是点运算符,一种是方括号运算符,二者是等价的,但是方括号运算似乎要更强大些,方括号里面我们可以放置变量。
这是最常用,最直观的一种创建对象方法,但是它的缺点太明显了,就是代码复用度很低,我们想到一个对象就创建一个对象,如是就会造成大量的重复代码,因此,javascript程序员将工厂模式引入到了javascrip编程里,请大家看下面的代码:
03 |
function createObj(id,name,teststring) |
08 |
o.teststring = teststring; |
09 |
o.sayHello = function () |
11 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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 |
19 |
obj[str](); //id:002@!@name:My Name is obj2@!@teststring:Test Obj2 |
工厂模式解决了创建相似对象的问题,如果抛开它构造对象的对象识别问题,工厂模式挺完美的,如果你做的javascript应用不是太复杂,建议使用工厂模式构造对象,这种写法可读性很高。
2. 通过构造函数
几乎所有使用构造函数方式构建对象都会使用到new运算符,javascript里面的构造函数比较特别的,在javascript里没有类的概念,new 后面跟的直接是构造函数,大家看下面的代码:
03 |
function Obj(id1,name1,teststring1) |
07 |
this .teststring = teststring1; |
08 |
this .sayHello = function () |
10 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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 |
18 |
obj[str](); //id:003@!@name:My Name is obj3@!@teststring:Test Obj3 |
构造函数式和工厂模式从代码角度而言很像,我在学习javascript初期,单独看一种方式,思维总是惯性的把二者混为一谈,如果两个放在一起还是觉得有差异,主要是javascript里面function被赋予的功能太多,如果在一个大型程序里面二者交替使用,不晕头才怪了。其实通过两者构造对象的不同,我可以把function的使用分为构造函数式和函数式。下面我将重点分析下两种方式,这里的见底或许不全面,要是哪位高手看到了可以补充和指点哈。
首先我去掉方法里各个属性的this指针,而sayHello里面引用的this指针不去掉。代码如下:
02 |
function Obj(id1,name1,teststring1) |
06 |
teststring = teststring1; |
09 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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,代码如下:
03 |
function Obj(id1,name1,teststring1) |
07 |
teststring = teststring1; |
10 |
console.log( 'id:' + id + '@!@name:' + name + '@!@teststring:' + teststring); |
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 |
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 |
由上面内容我得出了下面的结论:
- javascript是可以做面向对象编程的,我们不能把它当做面向过程的语言;
- javascript里面对象的创建是特别的,特别在于它和传统的面向对象语言比较起来做了简化,简化到直接使用构造函数来创建对象;
- function在javascript里面既可以当做函数式使用,又可以作为构造函数的标示(你也可以直接当做类来看待),而区别构造函数式和函数式的区别就是new;
- 如果我们用到了构造函数式才创建对象,那么这里和其他面向对象语言一样,也是有封装的,换种说法是里面定义的属性或者方法是属于该对象的,而让其属于该对象的方式就是用this指针,否则属性和方法将属于window对象。
如果我们不用new运算符,直接使用函数,例如下面代码:
03 |
function Obj(id1,name1,teststring1) |
07 |
this .teststring = teststring1; |
08 |
this .sayHello = function () |
10 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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 |
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都是指向全局的。这个不论哪里直接调用函数就有学问了啊,看下面代码:
03 |
function OuterObj(id1,name1,teststring1) |
07 |
this .teststring = teststring1; |
08 |
this .sayHello = function () |
10 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
13 |
function InnerObj(id2,name2,teststring2) |
17 |
this .teststring = teststring2; |
18 |
this .sayHello = function () |
20 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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 |
28 |
InnerObj( '102' , 'InnerObj0' , 'Test InnerObj0' ); |
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 |
34 |
window.sayHello(); //id:102@!@name:InnerObj0@!@teststring:Test InnerObj0 |
36 |
console.log(name); //InnerObj0 |
37 |
console.log(teststring); //Test InnerObj0 |
由此可见只要是函数调用this指向的方法和函数都是指向window的,即全局对象。
3. 对象初始化
对象初始化方式在有些书上也叫字面量方式构建对象,但是我更喜欢对象初始化方式,这样更直观,代码如下:
05 |
name: 'My Name is obj8' , |
06 |
teststring: 'Test Obj8' , |
08 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
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 |
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的代码:
2 |
public static String staticParams = "静态属性,它是属于类的" ; |
3 |
private String objId = "编号是对象私有的" ; |
4 |
public String objName = "名称是对象公有的" ; |
javascript对象的属性或方法也可以使用java里的思想分为:属于类的属性和方法,属于对象的属性或方法,还有公有属性和方法以及私有属性和方法。
- 属于类的属性和方法:用对象初始化的方式都可以当做是属于类的属性和方法,这种定义在jQuery里面大量运用;
- 属于对象的属性或方法:用构造函数的方式构建对象,里面用this指针指向的属性和方法都是属于对象的;
- 公有属性和方法:javascript里面属性和方法天生就是公有的;
- 私有属性和方法:这个在javascript里面要通过模拟才能实现,这种模拟一般是使用作用域的原理,比如:在一个function内部用var来定义对象,对象作用域属于改function内部,那么在函数外部是不能访问的,这就是私有变量和方法了。
以上对象的创建,里面的属性和方法操作都是对象专有的,用相同方式构建的类似对象不能做到信息的共享,那如何才能让类似的对象能共享信息了,这就的使用prototype原型链了。
在javascript里面每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途包含可以由特定类型所有实例共享的属性和方法。
代码如下:
03 |
function Obj(id1,name1,teststring1) |
07 |
this .teststring = teststring1; |
08 |
this .sayHello = function () |
10 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
14 |
Obj.prototype.objflag = '自建的Obj对象' ; |
15 |
Obj.prototype.proSayHello = function () |
17 |
console.log( 'Hello World and Obj' ); |
20 |
var obj1 = new Obj( '008' , 'My Name is obj8' , 'Test Obj8' ); |
21 |
var obj2 = new Obj( '009' , 'My Name is obj9' , 'Test Obj9' ); |
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里学到的,代码如下:
03 |
function Obj(id1,name1,teststring1) |
07 |
this .teststring = teststring1; |
08 |
this .sayHello = function () |
10 |
console.log( 'id:' + this .id + '@!@name:' + this .name + '@!@teststring:' + this .teststring); |
15 |
proSayHello: function (){ |
16 |
console.log( 'Hello World and Obj' ); |
19 |
var obj1 = new Obj( '010' , 'My Name is obj10' , 'Test Obj10' ); |
20 |
var obj2 = new Obj( '011' , 'My Name is obj11' , 'Test Obj11' ); |
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对象 |
- 【JavaScript学习】JavaScript对象创建
1.最简单的方法,创建一个对象,然后添加属性 var person = new Object(); person.age = 23; person.name = "David"; ...
- JavaScript 对象创建
tips: JavaScript 除了null和undefined之外,其他变量都可以当做对象使用. JavaScript 的基本数据类型有:number boolean string null u ...
- JavaScript对象创建的几种方式
1 工厂模式 1.1 创建 function createFruit(name,colors) { var o = new Object(); o.name = name; o.colors = co ...
- Javascript 对象创建多种方式 原型链
一.对象创建 1.new Object 方式 直接赋上属性和方法 var obj = new Object(); obj.name = '娃娃'; obj.showName = function(){ ...
- javascript对象创建方式
工厂模式 在ECMAscript中无法创建类,所以开发人员就发明了一种函数,用函数来封装,以特定接口创建对象的细节,如下面的例子所示: function createPerson(name,age,j ...
- Javascript对象创建
一.概述 虽然对象字面量可以用来创建对象,但在创建多个类似的对象时,不够优雅,不符合DRY原则. 二.创建对象 有以下几种模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.组合构造函数和原型模 ...
- JavaScript对象创建,继承
创建对象 在JS中创建对象有很多方式,第一种: var obj = new Object(); 第二种方式: var obj1 = {};//对象直面量 第三种方式:工厂模式 function Per ...
- JavaScript—对象创建方式
JavaScript 也是面向对象的语言(oop) 之前学JavaScript 没有学对象.现在做下笔记 创建对象的方式: 1. 对象字面量 const hero = { name: '吕布', w ...
- [JavaScript]对象创建方法
1.使用Object或对象字面量创建对象 (1)使用Object创建对象 var cat= new Object(); cat.name = "Tom"; cat.color= & ...
随机推荐
- Linux命令应用大词典-第46章 其他命令
46.1 mkfontdir:创建X字体文件的索引 46.2 dumpiso:转储IEEE 1394同步信道的数据包 46.3 iconv:转换文件编码 46.4 hash:显示和删除哈希表 46.5 ...
- hackerrank Project Euler #210: Obtuse Angled Triangles
传送门 做出一个好几个星期屯下来的题目的感觉就是一个字: 爽! 上图的黄点部分就是我们需要求的点 两边的部分很好算 求圆的地方有一个优化,由于圆心是整数点,我们可以把圆分为下面几个部分,阴影部分最难算 ...
- lintcode 二叉树中序遍历
/** * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; * Tr ...
- jQuery用unbind方法去掉hover事件及其他方法介绍
近日项目开发十分的繁忙,其中一个需求是实现响应式导航.(响应式的问题我们在css相关的博客中再交流) 大家都知道导航是需要下来菜单效果的,必然就会用到 jQuery的 hover() 方法.若是导航放 ...
- c字符指针与字符数组的区别
1.定义 char *pchar; //定义了指针,没赋值之前不能使用.如果:printf("*pchar:%c\n",*pchar); 出现段错误Segmentation fa ...
- 【转载】JAVA常见面试题及解答(精华)
JAVA常见面试题及解答(精华) 1)transient和volatile是java关键字吗?(瞬联) 如果用transient声明一个实例变量,当对象存储时,它的值不需要维持.例如: ...
- 3.hadoop完全分布式搭建
3.Hadoop完全分布式搭建 1.完全分布式搭建 配置 #cd /soft/hadoop/etc/ #mv hadoop local #cp -r local full #ln -s full ha ...
- [leetcode-784-Letter Case Permutation]
Given a string S, we can transform every letter individually to be lowercase or uppercase to create ...
- httpd 2.2.15 添加流媒体模块
项目中使用的一直都是 httpd 2.2.15 用于播放视频资源,近期有个新产品上线发现快进视频会出现卡顿情况,因此添加了流媒体模块.(怀疑是新产品中的播放器进行了更改) 原文:http://li ...
- android入门 — ListView的优化
ListView的运行效率是比较低的,因为在getView()中每次都会将整个布局重新加载一遍,当ListView快速滚动的时候就会成为性能瓶颈. 调用View中的findViewById()方法获取 ...