javascript定义类和对象 - [技术]
我不是鱼
![]()
|
1#
我不是鱼 发表于2007-12-13
javascript定义类和对象 - [技术]
//用Emeditor 以js 方式显示更为美观
/* 宿主对象 所有非本地对象都是宿主对象(host object),即由ECMAScript 实现的宿主环境提供的对象。所有BOM 和DOM 对象都是宿主对象 作用域 任何程序设计语言的程序员都懂得作用域的概念,即某些变量的适用范围 公用、受保护和私有作用域 在传统的面向对象程序设计中,主要关注于公用和私有作用域。公用作用域中的对象属 性可以从对象外部访问,即开发者创建对象的实例后,就可使用它的公用属性。而私有作用 域中的属性只能在对象内部访问,即对于外部世界来说,这些属性并不存在。这也意味着如 果类定义了私有属性和方法,则它的子类也不能访问这些属性和方法。 最近,另一种作用域流行起来,即受保护作用域。虽然在不同语言中,受保护作用域的 应用的规则不同,但一般说来,它都用于定义私有的属性和方法,只是这些属性和方法还能 被其子类访问。 对ECMAScript 讨论这些作用域几乎毫无意义,因为ECMAScript 中只存在一种作用域— —公用作用域。ECMAScript 中的所有对象的所有属性和方法都是公用的。因此,定义自己的 类和对象时,必须格外小心。记住,所有属性和方法默认都是公用的。 许多开发者都在网上提出了有效的属性作用域模式,解决了ECMAScript 的这种问题。由 于缺少私有作用域,开发者们制定了一个规约,说明哪些属性和方法应该被看作私有的。这 种规约规定在属性名前后加下划线 obj.__color__="red"; 这段代码中,属性color 是私有的。记住,这些下划线并不改变这些属性是公用属性的事 实,它只是告诉其他开发者,应该把该属性看作私有的。 有些开发者还喜欢用单下划线说明私有成员 静态作用域并非静态的 静态作用域定义的属性和方法任何时候都能从同一个位置访问。在Java 中,类可具有静 态属性和方法,无需实例化该类的对象,即可访问这些属性和方法,例如java.net.URLEncoder 类,它的函数encode()即是静态方法。 严格说来,ECMAScript 并没有静态作用域。不过,它可以给构造函数提供属性和方法。 还记得吗,构造函数只是函数。函数是对象,对象可以有属性和方法 关键字this 在ECMAScript 中,要掌握的最重要的概念之一是关键字this 的用法,它用在对象的方法 中。关键字this 总是指向调用该方法的对象 那么为什么使用this 呢?因为在实例化对象时,总是不能确定开发者会使用什么样的变量 名。使用this,即可在任意多个地方重用同一个函数 如果不用对象或this 关键字引用变量,ECMAScript 就会把它看作局部变量或全局变量 */ /*定义类和对象 使用预定义对象的能力只是面向对象语言的能力的一部分。真正的强大之处在于能够创 建自己专用的类和对象。与ECMAScript 中的许多特性一样,可以用各种方法实现这一点 */ //原始方式 var ocar=new Object; ocar.color="red"; ocar.doors=4; ocar.mpg=23; ocar.showcolor=function(){alert(this.color);} //工厂方式 function createcar(){ var ocar=new Object; ocar.color="red"; ocar.doors=4; ocar.mpg=23; ocar.showcolor=function(){alert(this.color);} return ocar; } var ocar1=createcar(); var ocar2=createcar(); ocar1.showcolor(); //ocar1.color="blue"; //ocar1.showcolor(); ocar2.showcolor(); //不会变成blue //带传入值 function createcar(incolor,indoors,inmpg){ var ocar=new Object; ocar.color=incolor; ocar.doors=indoors; ocar.mpg=inmpg; ocar.showcolor=function(){alert(this.color);} return ocar; } var ocar1=createcar("yellow",4,20); var ocar2=createcar(); ocar1.showcolor(); //ocar1.color="blue"; //ocar1.showcolor(); ocar2.showcolor(); //不会变成blue /* 每次创建一个car对象都会重新创建一个showcolor()函数 从工厂方法外定义一个函数,然后通过属性指向该方法可以避免此问题 */ function showcolor(){ alert(this.color); } function createcar(incolor,indoors,inmpg){ var ocar=new Object; ocar.color=incolor; ocar.doors=indoors; ocar.mpg=inmpg; ocar.showcolor=showcolor; return ocar; } var ocar1=createcar("yellow",4,20); ocar1.showcolor(); //构造函数方式 function car(incolor,indoors,inmpg){ this.color=incolor; this.doors=indoors; this.mpg=inmpg; this.showcolor=function(){ alert(this.color); } } var ocar1=new car("red",4,5); var ocar2=new car("blue",3,4); //和工厂函数一样,构造函数会重复生成函数,为每个对象都创建独立的函数版本。也可以用外部函数重写构造函数 // 原型方式,利用对象的prototype属性,可看成创建新对象所依赖的原型。用空构造函数来设置类名。然后所有的属和方法都被赋予prototype属性 function car(){ } car.prototype.color="red"; car.prototype.doors=4; car.prototype.mpg=20; car.prototype.showcolor=function(){ alert(this.color); }; var ocar1=new car(); var ocar2=new car(); /* ocar1.showcolor(); ocar1.color="black"; //不会变black car.prototype.color="green"; ocar1.showcolor(); //就会变了 ocar2.showcolor(); */ //可以使用instanceof检查类型 /* 这个构造函数没有参数。使用原型方式时,不能通过给构造函数传递参数初始化 属性的值,因为ocar1 和ocar2 的color 属性都等于"red",doors 属性都等于4,mpg 属性都等于20。 这意味必须在对象创建后才能改变属性的默认值,这点很令人讨厌,但还不至于是世界末日。 真正的问题出现在属性指向的是对象,而不是函数时。函数共享不会造成任何问题,但对象 却很少被多个实例共享的。考虑下面的例子: */ function car(){ } car.prototype.color="red"; car.prototype.doors=4; car.prototype.mpg=20; car.prototype.drivers=new Array("Mike","Sue"); car.prototype.showcolor=function(){ alert(this.color); }; var ocar1=new car(); var ocar2=new car(); ocar1.color="blue"; ocar1.showcolor(); ocar2.showcolor(); ocar1.drivers.push("dd"); //后面所有的drivers都是三个值了 alert(ocar1.drivers); alert(ocar2.drivers); /* 原型生成出来的对象是按原型链来找值,如果本身没有就往上一级找。当ocar1 的color属性改掉了, 就等于是在ocar1对象本身添加了一个color属性而把父类继承给它的color属性给覆盖了。 而对ocar1的drivers数组属性进行了push操作的话,drivers实际上是一个array,也就是个引用值,会直接把引用给改掉。 这里,属性drivers 是指向Array 对象的指针,该数组中包含两个名字"Mike"和"Sue"。由于drivers 是引用值,Car 的两个实例都指向同一个数组。这意味着给ocar1.drivers 添加值"Matt",在ocar2.drivers 中也能看到。输出这两个指针中的任何一个,结果都是显示字符串"Mike,Sue,dd"。 由于创建对象时有这么多问题,你一定会想,是否有种合理的创建对象的方法呢?答案 是联合使用构造函数和原型方式。*/ //混合的构造函数/原型方式 /* 联合使用构造函数和原型方式,就可像用其他程序设计语言一样创建对象。这种概念非 常简单,即用构造函数定义对象的所有非函数属性,用原型方式定义对象的函数属性(方法)。 结果所有函数都只创建一次,而每个对象都具有自己的对象属性实例 */ function car(incolor,indoors,inmpg){ this.color=incolor; this.doors=indoors; this.mpg=inmpg; this.drivers=new Array("Mike","Sue"); car.prototype.showcolor=function(){ alert(this.color); }; } var ocar1=new car(); var ocar2=new car(); ocar1.drivers.push("dd"); alert(ocar1.drivers); alert(ocar2.drivers); //动态原型法 /* Java 很好的打包了Car类的所有属性和方法,因此看见这段代码就知道它要实现什么功能, 它定义了一个对象的信息。批评混合的构造函数/原型方式的人认为,在构造函数内存找属性, 在其外部找方法的做法不合逻辑。因此,他们设计了动态原型方法,以提供更友好的编码风 格。 动态原型方法的基本想法与混合的构造函数/原型方式相同,即在构造函数内定义非函数 属性,而函数属性则利用原型属性定义。唯一的区别是赋予对象方法的位置 */ function car(incolor,indoors,inmpg){ this.color=incolor; this.doors=indoors; this.mpg=inmpg; this.drivers=new Array("Mike","Sue"); if(typeof car._initialized=="undefined") { car.prototype.showcolor=function() { alert(this.color); } color._initialized=true; } } /* 假设我们向重复抽取字符串的第三个字符,我们可以更改 String 对象的原型,以便所有的字符串都有我们定义的方法: String.prototype.getThirdChar = function() { return this.charAt(2); } 这样我们就可以在任何字符串对象中调用该方法了: var c = "Example".getThirdChar(); // c 的值为 'a' */ //混合工厂方式 /* 这种方式通常是在不能应用前一种方式时的变通方法。它的目的是创建假构造函数,只 返回另一种对象的新实例。 只不过在创建时用 var car=new car(); 由于在Car()构造函数内部调用了new 运算符,所以将忽略第二个new 运算符(位于构造函 数之外)。在构造函数内部创建的对象被传递回变量var。 这种方式在对象方法的内部管理方面与经典方式有着相同的问题。强烈建议:除非 万不得已 还是避免使用这种方式。 */ /* 采用哪种方式 目前使用最广泛的是混合的构造函数/原型方式。此外,动态原型方法也很流 行,在功能上与构造函数/原型方式等价。可以采用这两种方式中的任何一种。不过不要单独 使用经典的构造函数或原型方式,因为这样会给代码引入问题 对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript 中最常见的一个问题是字 符串连接的性能。与其他语言类似,ECMAScript 的字符串是不可变的,即它们的值不能改变 */ var str="hello"; str+=" world!"; /* 实际上,这段代码在幕后执行的步骤如下: (1) 创建存储"hello"的字符串。 (2) 创建存储"world"的字符串。 (3) 创建存储连接结果的字符串。 (4) 把str 的当前内容复制到结果中。 (5) 把"world"复制到结果中。 (6) 更新str,使它指向结果。 每次完成字符串连接都会执行步骤2 到6,使得这种操作非常消耗资源。如果重复这一过 程几百次,甚至几千次,就会造成性能问题。解决方法是用Array 对象存储字符串,然后用join() 方法(参数是空字符串)创建最后的字符串。 */ for(var i=0;i<99999;i++) { var str="hello"; str+=" world!"; document.write(str+"<br>"); } alert("finish"); /* for(var i=0;i<99999;i++) { var arr=new Array(); arr[0]="hello"; arr[1]=" world!"; var str=arr.join(""); document.write(str+"<br>"); } */
如履薄冰
|