`
jickcai
  • 浏览: 238585 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javascript 笔记二 对象基础和继承

阅读更多
第三章 对象基础

面向对象术语
ECMA-262把对象(Object)定义为"属性的无序集合,每个属性存放一个原始值、对象或函数".
每个对象都由类定义,可以把类看作对象的配方,类不仅要定义对象的接口(interface)(开发者访问的属性和方法),
还要定义对象内部工作(使属性和方法发挥作用的代码)。编译器和解释程序都根据类的说明构建对象。
程序使用类创建对象时,生成的对象叫做类的实例(instance).对类生成的对象的个数的唯一限制来自于运行代码的
机器的物理内存。每个实例的行为相同,但实例处理一组独立的数据。由类创建实例的过程叫做实例化。
面向对象语言的要求
封装-------把相关的信息(无论数据或方法)存储在对象中的能力。
聚集-------把一个对象存储在另一个对象内的能力。
继承-------由另一个类(或多个类)得来的属性和方法的能力
多态-------编写能以多种方法运行的函数或方法的能力
对象的构成
在ECMAScript中,对象由特性(attribute)构成,特性可以是原始值,也可以是引用值。如果特性存放的是函数,它将被看作
对象的方法(method),否则该特性被看作属性(property)。
声明和实例化
var oObject=new Object();
var oStringObject=new String();
如果构造函数无参数,括号则不是必需的。
对象引用
在ECMAScript中,不能访问对象的物理表示,只能访问对象的引用。每次创建对象,存储在变量中的都是该对象的引用,而不是对象本省。
对象废除
ECMAScript有无用存储单元收集程序,意味着不必专门销毁对象来释放内存。当再没有对象的引用时,称该对象被废除(dereference)了。
运行无用存储单元收集程序时,所有废除的对象都被销毁。每当函数执行完它的代码,无用存储单元收集程序都会运行,释放所有的局部
变量,还有在一些其他不可预知的情况下,无用存储单元收集程序也会运行。
把对象的所有引用都设置成为null,可以强制性的废除对象。
例如:
var oObject=new Object;
oObject=null;
每用完一个对象后,就将其废除,来释放内存,这是个好习惯。这样还确保不再使用已经不能访问的对象,从而防止程序设计错误的出现。
注意:
废除对象的所有引用时要当心。如果一个对象有两个或更多引用,则要正确废除该对象,必须将其所有引用都设置为null.
早绑定和晚绑定
所谓绑定(binding),即把对象的接口与对象实例结合在一起的方法。
早绑定(early binding)是指在实例化对象之前定义它的特性和方法,这样编译器或解释程序就能提前转换机器代码。
晚绑定(late binding)指的是编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需要检查对象是否支持特性和方法即可。
在ECMAScipt中的所有变量都采用晚绑定方法,这样就允许执行大量的对象操作,而无任何惩罚。
对象的类型
在ECMAScript中,所有对象并非同等创建的。可以创建并使用的对象有三种。
本地对象
ECMA-262把本地对象(native object)定义为"独立于宿主环境的ECMAScript实现提供的对象"。简单说来,本地对象就是ECMA-262定义的类(引用类型).
它们包括:
Object Function Array String
Boolean Number Date RegExp
Error EvalError RangeError ReferenceError
SyntaxError TypeError URIError
Array类:
var aValues=new Array();
如果预先知道数据中项的个数,可以用参数传递数组的大小。
var aValues=new Array(20);
var aColors=new Array();
aColors[0]="red";
aColors[1]="green";
aColors[2]="blue";
如果知道数组应该存放的值,还可用参数声明这写值,创建大小与参数个数相等的Array对象。
例如: var aColors=new Array("red","green","blue");
aColors[3]="purple";
aColors[25]="k";//结果ECMAScript将把从4开始到24的所有位置都填上null,然后在
位置25处放上正确的值,并把数组大小增大为26.

var aColors=new Array("red","green","blue");
alert(aColors.length);
aColors[25]="purple";
alert(aColors.length);
注意: 数组最多可以存放4294967295项,这应该可满足大多数程序设计的需要。如果要添加更多的项,则会发生异常。
另外的形式:
var aColors=["red","green","blue"];
alert(aColors.length);
aColors[25]="purple";
alert(aColors.length);
Array对象覆盖了toString()方法和valueOf()方法,返回特殊的字符串。该字符串是通过对每项调用toString()方法,
然后用逗号把它们连接在一起构成的。
var aColors=["red","green","blue"];
alert(aColors.toString()); //outputs "red,green,blue"
alert(aColors.valueOf());//outputs "red,green,blue"
toLocaleString()方法返回的也是有数组项构成的字符串。唯一的区别是得到的值是通过调用每个数组项的toLocaleString()
方法得到的.许多情况下,该方法返回的值都与toString()方法返回的值相同。
var aColors=["red","green","blue"];
alert(aColors.toLocaleString()); //outputs "red,green,blue"

join()方法:
var aColors=["red","green","blue"];
alert(aColors.join(",")); //outputs "red,green,blue"
alert(aColors.join("-spring-"));//outputs "red-spring-green-spring-blue"
alert(aColors.join("]["));//outputs "red][green][blue"
split()方法:
var sColors="red,green,blue";
var aColors=sColors.split(",");
如果把空字符串声明为分割符,那么split()方法返回数组中的每个项是字符串的字符。
var sColors="green";
var aColors=sColors.split("");
alert(aColors.toString()); //outputs "g,r,e,e,n"
concat()方法:
var aColors=["red","green","blue"];
var aColors2=aColors.concat("yellow","purple");
alert(aColors2.toString());//outputs "red,green,blue,yellow,purple"
alert(aColors.toString());//outputs "red,green,blue"
slice()方法: 返回的是具有特定项的新数组
一个参数:返回从该位置到数据结尾的所有项。
二个参数:返回第一个位置和第二个位置间的所有项,不包括第二个位置处的项。
var aColors=["red","green","blue","yellow","purple"];
var aColors2=aColors.slice(1);
var aColors3=aColors.slice(1,4);
alert(aColors2.toString());//outputs "green,blue,yellow,purple"
alert(aColors3.toString());//outputs "green,blue,yellow"
push()和pop()方法:
var stack=new Array;
stack.push("red");
stack.push("green");
stack.push("yellow");
alert(stack.toString());//outputs "red,green,yellow"
var vItem=stack.pop();
alert(vItem);//outputs "yellow"
alert(stack.toString());//outputs "red,green"
shift()和unshift()方法:
shift()将删除数组中的第一个项,将其作为函数值返回。
unshift()它把一个项放在数组的第一个位置,然后把余下的项向下移动一个位置。
var aColors=["red","green","yellow"];
var vItem=aColors.shift();
alert(aColors.toString());//outputs "green,yellow"
alert(vItem);//outputs "red"
aColors.unshift("black");
alert(aColors.toString());//outputs "black,green,yellow"
reverse()方法:颠倒数组项的顺序
var aColors=["red","green","yellow"];
aColors.reverse();
alert(aColors.toString());//outputs "blue,green,red"
sort()方法:将根据数组项的值按升序为它们的排序。要进行这种排序,首先调用toString()方法,将
所有值转换成字符串,然后根据字符代码比较数组项.
var aColors=["red","green","blue","yellow"];
aColors.sort();
alert(aColors.toString());//outputs "blue,green,red,yellow"

var aColors=[3,32,2,5];
aColors.sort();
alert(aColors.toString());//"2,3,32,5" 数字是被转换成字符串,然后再按照字符代码比较。
splice()方法: 把数据项插入到数组的中部.
变体:
删除-只需要声明两个参数,就可以从数组中删除任意多个项,这连个参数是要删除的第一个项的
位置和要删除的项的个数。
var aColors=["red","green","blue","yellow"];
aColors.splice(0,2);
alert(aColors.toString());//outputs "blue,yellow"
替换而不删除--声明三个参数就可以把数据项插入指定的位置,这三个参数是起始位,0(要删除的数组项的个数)
和要插入的项。
var aColors=["red","green","blue","yellow"];
aColors.splice(2,0,"w","f");
alert(aColors.toString());//outputs "red,green,w,f,blue,yellow"
查找并删除---申明三个参数就可以把数据项插入到指定的位置,这三个参数是起始位置、
要删除的数组项的个数以及要插入的项。此外还可以指定要插入的更多的项。要插入的项的个数不必等于删除
的项的个数。
var aColors=["red","green","blue","yellow"];
aColors.splice(1,2,"w","f");
alert(aColors.toString());//outputs "red,w,f,yellow"

Data类
ECMAScript中的Date类基于Java中的java.util.Date类的早期版本。与Java一样,ECMAScript把日期存储为距离UTC时间1970年1月1日凌晨12点的
毫秒数。UTC是Universal Time Code,即通用时间代码(也叫做Greenwich Mean Time,格林尼治标准时间)的缩写。
创建新的Date对象:
var d=new Date();
只声明距离1970年1月1日凌晨12点的毫秒数
var d=new Date(0);
parse()和UTC()方法,可以与创建的方法一起使用。
parse()方法接受字符串为参数,把该字符串转换成日期值(即毫秒表示)。ECMA-262未定义parse()方法接受日期的格式。
例如:
为2004年5月25日创建Date对象,可以使用parse()方法获得它的毫秒表示,然后将该值传递给Date构造函数.
var d=new Date(Date.parse("May 25,2004"));
如果传递给parse()方法的字符串不能转换成日期,改函数返回NaN.
UTC()方法返回的也是日期的毫秒表示,但参数不同,是日期中的年、月、日、小时、分、秒和毫秒。使用该方法必须声明年和月。
其它参数可选。月份用0-11表示。

var d=new Date(Date.UTC(2004,1,5));// 2004年2月5号
var d=new Date(Date.UTC(2004,1,5,13,5));// 2004年2月5号下午1:05分
创建日期的第二种方法是直接声明UTC()方法接受的参数。
var d=new Date(2004,1,5);
声明参数的顺序相同,(除了年和月外)它们不必都出现。
创建特定日期的字符串表示方法:
toDateString()--以实现的特定格式显示Date的日期部分(即只有月、日和年)
toTimeString()--以实现的特定的格式显示Date的时间部分(即小时、分、秒和时区)
toLocaleString()--以地点特定的格式显示Date的日期和时间
toLocaleDateString()--以地点特定的格式显示Date的日期和时间
toLocaleTimeString()--以地点特定的格式显示Date的日期和时间
toUTCString()--以实现特定的格式显示Date的时间部分
----------------------------------------------------------------------------
方法 说明
getTime() 返回日期的毫秒表示
setTime(milliseconds) 设置日期的毫秒表示

getFullYear() 返回用四位数字表示的日期的年份
getUTCFullYear() 返回用四位数字表示的UTC日期的年份
setFullYear(year) 设置日期的年份,参数必须是四位数字的年份值
setUTCFullYear(year) 设置UTC日期的年份,参数必须是四位数字的年份值

getMonth() 返回日期的月份值,由数字0(1月)到11(12月)表示
getUTCMonth() 返回UTC日期的月份值,由数字0(1月)到11(12月)表示
setMonth(month) 设置日期的月份为大于等于0的数字。对于大于11的数字,开始累计年数
setUTCMonth(month) 设置UTC日期的月份为大于0的数字。 对于大于11的数字,开始累计年数

getDate() 返回该日期该月中的某天
getUTCDate() 返回该UTC日期该月中的某天
setDate(date) 设置该日期该月中的某天
setUTCDate(date) 设置该UTC日期该月中的某天

getDay() 返回该日期为星期几
getUTCDay() 返回该UTC日期为星期几
setDay(day) 设置该日期为星期几
setUTCDay(day) 设置该UTC日期为星期几

getHours() 返回日期中的小时值
getUTCHours() 返回UTC日期中的小时值
setHours(hours) 设置日期中的小时值
setUTCHours(hours) 设置UTC日期中的小时值

getMinutes() 返回日期中的分钟值
getUTCMinutes() 返回UTC日期中的分钟值
setMinutes(minutes) 设置日期中的分钟值
setUTCMinutes(minutes) 设置UTC日期中的分钟值

getSeconds() 返回日期中的秒值
getUTCSeconds() 返回UTC日期中的秒值
setSeconds(seconds) 设置日期中的秒值
setUTCSeconds(seconds) 设置UTC日期中的秒值

getMilliseconds() 返回日期中的毫秒值
getUTCMilliseconds() 返回UTC日期中的毫秒值
setMilliseconds(milliseconds) 设置日期中的毫秒值
setUTCMilliseconds(milliseconds) 设置UTC日期中的毫秒值

内置对象-87
ECMA-262把内置对象(built-in object)定义为"由ECMAScript实现提供的、独立于宿主环境的所有对象,在ECMAScript程序开始
执行时出现。"。ECMA-262只定义了两个内置对象,即Global和Math(它们也是本地对象,根据定义,每个内置对象都是本地对象)
1.Global对象
Global对象是ECMAScript中最特别的对象,因为实际上它根本不存在。
encodeURI()和encodeURIComponent()方法用于编码传递给浏览器的URI(统一资源标识符).有效的URI不能包含某些字符,
如空格。这两个方法用于编码URI,这样用专门的UTF-8编码替换所有的非有效字符,就可以使浏览器仍能够接受并理解它们。

encodeURI()方法用于处理完整的URI(例如:http://www.wrox.com/illegal value.html),而encodeURICommponent()用于处理URI的
一个片段(如前面的URI中的illegal value.html).这两个方法的主要区别是encodeURI()方法不对URI中的特殊字符进行编码。如
冒号、前斜杠、问号和英镑符号,而encodeURIComponent()则对它发现的所有非标准字符进行编码。
var sUri="http://www.wrox.com/illegal value.html#start";
alert(encodeURI(sUri));//outputs "http://www.rox.com/illegal%20value.html#start"
alert(encodeURIComponent(sUri));//outputs "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.html%23start"

decodeURI()和decodeURIComponent()解码编码过的URI.
var sUri="http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.html%23start";
alert(decodeURI(sUri));
alert(decodeURIComponent(sUri));
注意:
这些URI方法encodeURI()、encodeURIComponent()、decodeURI()和decodeURIComponent()代替了BOM的
escape()和unescape()方法。URI方法更可取,因为它们会对所有Unicode符号编码,而BOM方法只能对ASCII符号正确编码。
尽量避免使用escape()和unescape()方法。

eval()当解释程序发现eval()调用时,它将把参数解释为真正的ECMAScript语句,然后把它插入该函数所在的位置。这意味着
eval()调用内部引用的变量可在参数以外定义。
var msg="hello world";
eval("alert(msg)");

Global对象的属性:
------------------------------------------------------------
属性 说明
undefined Undefined类型的字面量
NaN 非数的专用数值
Infinity 无穷大值的专用数值
Object Object的构造函数
Array Array的构造函数
Function Function的构造函数
Boolean Boolean的构造函数
String String的构造函数
Number Number的构造函数
Date Date的构造函数
RegExp RegExp的构造函数
Error Error的构造函数
EvalError EvalError的构造函数
RangeRrror RangeError的构造函数
ReferenceError ReferenceError的构造函数
SyntaxError SyntaxError的构造函数
TypeError TypeError的构造函数
URIError URIError的构造函数
-----------------------------------------------------------------------

2。Math对象
Math对象有几个属性,主要是数学界的专用值。
-----------------------------------------
属性 说明
-----------------------------------------
E 值e,自然对数的底
Ln2 2的自然对数
LOG2E 以2为底E的对数
LOG10E 以10为底E的对数
PI 值pi
SQRT1_2 1/2的平方根
SQRT2 2的平方根
-----------------------------------------
方法:
Math.max()最大值
var iMax=Math.max(3,54,32,16);
alert(iMax);//outputs "54"
Math.min()最小值
var iMin=Math.min(3,54,32,16);
alert(iMin);//outputs "3"
Math.abs()绝对值
var iNegOne=Math.abs(-1);
alert(iNegOne);//outputs "1"
var iPosOne=Math.abs(1);
alert(iPosOne);//outputs "1"
Math.ceil()向上舍入函数,总是把数字向上舍入到最接近的值
alert(Math.ceil(25.5));//outputs "26"
Math.floor()表示向下舍入函数,总是把数字向下舍入到最接近的值
alert(Math.floor(25.5));//outputs "25"
Math.round()表示标准的四舍五入函数
alert(Math.round(25.5));//outputs "26"
Math.exp()用于把Math.E升到指定的幂

Math.log()用于返回特定数字的自然对数
alert(Math.log(Math.exp(10)));//outputs "10"
Math.pow()用于把指定的是数字升到指定的幂
alert(Math.pow(2,3));//outputs "8"
Math.sqrt()用于返回指定数字的平方根
alert(Math.sqrt(4)); //outputs "2"
Math.acos(x) 返回x的反余弦值
Math.asin(x) 返回x的反正弦值
Math.atan(x) 返回x的反正切值
Math.atan2(y,x) 返回y/x的反余弦值
Math.cos(x) 返回x的余弦值
Math.sin(x) 返回x的正弦值
Math.tan(x) 返回x的正切值
Math.random() 返回一个0到1之间的随机数,不包括0和1.
function selectFrom(iFirstValue,iLastValue){
var iChoices=iLastValue-iFirstValue+1;
return Math.floor(Math.random()*iChoices+iFirstValue);
}
//select from between 2 and 10
var iNum=selectFrom(2,10);

使用函数,可很容易的选择Array中的随机项
var aColors=["red","green","blue","yellow","blank","purple","brown"];
var sColor=aColors[selectFrom(0,aColors.length-1)];
宿主对象
所有非本地对象都是宿主对象(host object),即由ECMAScript实现的宿主环境提供的对象。所有的BOM和DOM对象都是宿主对象。

静态作用域并非静态的
静态作用域的属性和方法任何时候都能从同一位置访问。在Java中,类可具有静态的属性和方法,无需实例化该类的对象,
即可访问这些属性和方法。例如java.net.URLEncoder类,它的函数encode()即是静态方法。
严格说来,ECMAScript并没有静态作用域,不过,它可以给构造函数提供属性和方法。构造函数只是函数。函数是对象,对象可以有属性和方法。
function sayHi(){
alert("hi");
}
sayHi.alternate=function(){
alert("hola");
};

sayHi();//outputs "hi"
sayHi.alternate();//outputs "hola"
this总是指向调用该方法的对象
var oCar=new Object;
oCar.color="red";
oCar.showColor=function(){alert(this.color);};
oCar.showColor();//outputs "red"

工厂方式
function createCar(){
var oTempCar=new Object;
oTempCar.color="red";
oTempCar..doors=4;
oTempCar.mpg=23;
oTempCar.showColor=function(){alert(this.color);};
}
var oCar1=createCar();
oCar1.showColor();

var oCar2=createCar();
oCar2.showColor();
功能问题在于用这种方式必须创建对象的方法。前面的例子中,每次调用函数createCar(),都要创建新函数showColor(),
意味着每个对象都有自己的showColor()版本。事实上,每个对象都共享了同一个函数。

有些开发者在工厂函数外定义对象的方法,然后通过属性指向该方法,从而避开这个问题。
function showColor(){
alert(this.color);
}

function createCar(sColor,iDoors,iMpg){
var oTempCar=new Object;
oTempCar.color=sColor;
oTempCar.doors=iDoors;
oTempCar.mpg=iMpg;
oTempCar.showColor=showColor;
return oTempCar;
}

var oCar1=createCar("red",4,23);
var oCar2=createCar("blue",4,23);
oCar1.showColor();//outputs "red"
oCar2.showColor();//outputs "blue"

在这段重写的代码中,在函数createCar()前定义了函数showColor().在createCar()内部,赋予对象一个指向已经存在的
showColor()函数指针。从功能上讲,这样解决了重复创建函数对象的问题,但该函数看起来不像对象的方法。
构造函数方式
function Car(sColor,iDoors,iMpg){
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.showColor=function(){alert(this.color);};
}

var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);
oCar1.showColor();
oCar2.showColor();
原型方式
该方式利用了对象的prototype属性,可把它看成创建新对象所依赖的原型。这里,用空构造函数来设置类名。然后所有的属性和方法都被
直接赋予prototype属性。
function Car(){

}

Car.prototype.color="red";
Car.prototype.doors=4;
Car.prototype.mpg=23;
Car.prototype.showColor=function(){
alert(this.color);
}

var oCar1=new Car();
var oCar2=new Car();
oCar1.showColor();
oCar2.showColor();
在这段代码中,首先定义构造函数Car,其中无任何代码。接下来的几行代码,通过给Car的prototype属性添加属性取
定义Car对象的属性。调用new Car()时,原型的所有属性都被立即赋予要创建的对象,意味着所有Car实例存放的都是指向
showColor()函数的指针。从语义上讲,所有属性看起来都属于一个对象,因此解决了前面两种方式存在的两个问题。
此外,使用该方法,还能用instanceof运算符检查给定变量指向的对象的类型。因此,下面的代码将输出true:
alert(oCar1 instanceof Car); //outputs "true"
看起来是个非常好的解决方案,遗憾的是,它并不尽如人意。
首先这个构造函数没有参数。使用原型方式时,不能通过给构造函数传递参数初始化属性的值。
例:
function Car(){

}

Car.prototype.color="red";
Car.prototype.doors=4;
Car.prototype.mpg=23;
Car.prototype.drivers=new Array("Mike","Sue");
Car.prototype.showColor=function(){
alert(this.color);
}

var oCar1=new Car();
var oCar2=new Car();

oCar1.drivers.push("Matt");

alert(oCar1.drivers);//outputs "Mike,Sue,Matt"
alert(oCar2.drivers);//outputs "Mike,Sue,Matt"
这里,属性drivers是指向Array对象的指针,改数组中包含两个名字"Mike"和"Sue".由于drivers是引用值,Car的两个实例都
指向同一个数组.这意味着给car1.drivers添加值"Matt",在car2.drivers中也能看到,输出这两个指针中的任何一个,如果都是显示字符串"Mike,Suc,Matt".
混合的构造函数/原型方式
联合使用构造函数和原型方式,就可像用其他程序设计语言一样创建对象。这种概念非常简单,即用构造函数定义对象的所有非函数属性,用原型方式定义对象的
函数属性(方法).结果所有函数都只创建一次,而每个对象都具有自己的对象属性实例。
function Car(sColor,iDoors,iMpg){
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
this.drivers=new Array("Mike","Sue");
}

Car.prototype.showColor=function(){alert(this.color);};
var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);

oCar1.drivers.push("Matt");
alert(oCar1.drivers);//outputs "Mike,Sue,Matt"
alert(oCar2.drivers);//outputs "Mike,Sue"
动态原型方式
对于习惯使用其他语言的开发者来说,使用混合的构造函数/原型方式感觉不那么和谐。
下面用动态原型方式重写Car类:
function Car(sColor,iDoors,iMpg){
this.color=sColor;
this.doors=iDoors;
this.mpg=iMpg;
if(typeof Car._initialized=="undefined"){
Car.prototype.showColor=function(){alert(this.color);};
Car._initialized=true;
}
}

var oCar1=new Car("red",4,23);
var oCar2=new Car("blue",3,25);
oCar1.showColor();
oCar2.showColor();

混合工厂方式
这种方式通常是在不能应用前一种方式时的变通方式。它的目的是创建假构造函数,只放回了另一种对象的新实例。
function Car(){
var oTempCar=new Object;
oTempCar.color="red";
oTempCar.doors=4;
oTempCar.mpg=23;
oTempCar.showColor=function(){alert(this.color);};
return oTempCar;
}

var car=new Car();
car.showColor();
注意:
除非万不得已,要避免使用这种方式。
采用哪种方式
目前最广泛的是混合的构造函数/原型方式。此外,动态原型方式也很流行,在功能上与构造函数/原型方式等价。可以采用这两种方式中的任何一种。
不过不要单独使用经典的构造函数或圆形方式,因为这样会给代码引入问题。
实例
对象令人感兴趣的一点是用它们解决问题的方式。ECMAScript中最常见的一个问题是字符串连接的性能。与其他语言类似,ECMAScript的字符串是不可
变的,即他们的值不能改变。考虑下面的代码:
var str="hello";
str+="world";
实际上,这段代码在幕后的执行的步骤如下:
(1)创建存储"hellow"的字符串.
(2)创建存储"world"的字符串。
(3)创建存储连接结果的字符串。
(4)把str的当前内容复制到结果中。
(5)把"world"复制到结果中。
(6)更新str,使它指向结果。
每次完成字符串连接都会执行步骤2到6,使得这种操作非常消耗资源。如果重复这一过程几百次,甚至几千次,就会造成性能问题。解决方法使用
Array对象存储字符串,然后用join()方法创建最后的字符串。想像用下面的代码代替前面的代码。
var arr=new Array;
arr[0]="hello ";
arr[1]="world";
var str=arr.join("");
alert(str);
这样,无论在数组中引入多少字符串都不成问题,因为只在调用join()方法时才会发生连接操作。此时,执行的步骤如下:
(1)创建存储结果的字符串。
(2)把每个字符串复制到结果中的合适位置。
虽然这种解决方法很好,但还有更好的方法。问题是这段代码不能确切的反映出它的意图。要使它更容易理解,可以用StringBuffer类
打包该功能:
function StringBuffer(){
this._strings_=new Array;
}
StringBuffer.prototype.append=function(str){
this._strings_.push(str);
};

StringBuffer.prototype.toString=function(){
return this._strings_.join("");
};

var buffer=new StringBuffer();
buffer.append("hello ");
buffer.append("world");
var result=buffer.toString();
//alert(result);
/**********测试StringBuffer对象和传统的字符串连接方法的性能***********/
var d1=new Date();
var str="";
for(var i=0;i<10000;i++){
str+="text";
}
var d2=new Date();

document.write("Concatenation with plus:"+(d2.getTime()-d1.getTime()))+"milliseconeds";
var oBuffer=new StringBuffer();
d1=new Date();
for(var i=0;i<10000;i++){
oBuffer.append("text");
}
var sResult=buffer.toString();
d2=new Date();
document.write("<br/>Concatenation with plus:"+(d2.getTime()-d1.getTime()))+"milliseconeds";
创建新方法
判断某个项在数组中的位置。
Array.prototype.indexOf=function(vItem){
for(var i=0;i<this.length;i++){
if(vItem==this[i]){
return i;
}
}
return -1;
};

var aColors=new Array("red","green","yellow");
alert(aColors.indexOf("green"));//outputs "1"
重定义已有方法
就像能给已有的类定义新方法一样,也可以重新定义已有的方法。
例:
Function.prototype.toString=function(){
return "Function code hidden";
};

function sayHi(){
alert("hi");
}

alert(sayHi.toString());
通过这种方法重新定义以后,toString()指向的原始函数将被无用存储单元回收程序回收,因为它被完全废弃了。
没能够恢复原始函数的办法,所以在覆盖原始方法前,比较安全的做法是存储它的指针,一边以后的使用。
Function.prototype.originalToString=Function.prototype.toString;
Function.prototype.toString=function(){
if(this.originalToString().length>100){
return "Function too long to diaplay.";
}else{
return this.originalToString();
}
}
极晚绑定
从技术上来说,根本不存在极晚绑定。本书采用该术语描述ECMAScript中的一种现象,即能够在对象实例化后再定义它的方法。
var o=new Object;
Object.prototype.sayHi=function(){
alert("hi");
}

o.sayHi();
注意:
不建议使用极晚绑定方法,因为很难对其跟踪和记录。不过,还是应该了解这种可能。
----------------------------------------------------------------------------------------------
第四章 继承
真正的面向对象语言必须支持继承机制,即一个类能够重用(继承)另一个类的方法和属性。

继承机制的实现
要用ECMAScript实现继承机制,首先从基类入手。所有开发者定义的类都可作为基类。出于安全原因,本地类和
宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。

继承的方式
和其他功能一样,ECMAScript中实现继承的方式不止一种。这是因为JavaScript中的继承机制并不是明确规定的,而是
通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。
1.对象冒充
原理:构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,
所以可使ClassA的构造函数成为ClassB的方法,然后调用它。ClassB就会收到ClassA的构造函数中定义的属性和方法。
例:
function ClassA(sColor){
this.color=sColor;
this.sayColor=function(){
alert(this.color);
};
}

function ClassB(sColor,sName){
this.newMethod=ClassA;
this.newMethod(sColor);
delete this.newMethod;

this.name=sName;
this.sayName=function(){
alert(this.name);
};
}

var objA=new ClassA("red");
var objB=new ClassB("blue","Nicholas");
objA.sayColor();//outputs "red"
objB.sayColor();//outputs "blue"
objB.sayName();//outputs "Nicholas"
对象冒充可以支持多重继承。也就是说,一个类可以继承多个超类。
例如,如果存在两个类ClassX和ClassY,ClassZ想继承这两个类,可以实用下面的代码:
function ClaooZ(){
this.newMethod=ClassX;
this.newMethod();
delete this.newMethod;

this.newMethod=ClassY;
this.newMethod();
delete this.newMethod;
}
这里存在一个弊端,如果ClassX和ClassY具有同名的属性或方法,ClassY具有高优先级,因为它从后面的类继承。
除这点问题之外,用对象冒充实现多重继承机制轻而易举。
由于继承方法的流行,ECMAScript的第三版为Function对象加入了两个新方法,即call()和apply().
2.call()方法
call()方法是与经典对象冒充方法最相似的方法。它的第一个参数用作this的对象。其他参数都直接传递给函数自身。
function sayColor(sPrefix,sSuttix){
alert(sPrefix+this.color+sSuttix);
}
var obj=new Object();
obj.color="red";

sayColor.call(obj,"The color is ",",a very nice color indeed");

function ClassA(sColor){
this.color=sColor;
this.sayColor=function(){
alert(this.color);
};
}

function ClassB(sColor,sName){
ClassA.call(this,sColor);

this.name=sName;
this.sayName=function(){
alert(this.name);
};
}

var objA=new ClassA("red");
var objB=new ClassB("blue","Nicholas");
objA.sayColor();//outputs "red"
objB.sayColor();//outputs "blue"
objB.sayName();//outputs "Nicholas"
3.apply()方法
apply()方法有两个参数,用作this的对象和要传递给函数的参数的数组。
function sayColor(sPrefix,sSuttix){
alert(sPrefix+this.color+sSuttix);
}
var obj=new Object();
obj.color="red";
sayColor.apply(obj,new Array("The color is ",",a very nice color indeed."));

function ClassA(sColor){
this.color=sColor;
this.sayColor=function(){
alert(this.color);
};
}

function ClassB(sColor,sName){
ClassA.apply(this,new Array("sColor"));

this.name=sName;
this.sayName=function(){
alert(this.name);
};
}

var objA=new ClassA("red");
var objB=new ClassB("blue","Nicholas");
objA.sayColor();//outputs "red"
objB.sayColor();//outputs "blue"
objB.sayName();//outputs "Nicholas"
同样的,第一个参数仍是this.第二个参数是只有一个值color的数组。可以把ClassB的整个arguments对象作为第二个参数传递
给apply()方法:
function ClassB(){
ClassA.apply(this,arguments);
this.name=sName;
this.sayName=function(){
alert(this.name);
};
}
  当然,只有超类中的参数顺序与子类中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,
  按照正确的顺序放置参数。
4.原型链
继承这种形式在ECMAScript中原本是用于原型链的。原型链扩展了原型方式,以一种有趣的方式实现继承机制。
function ClassA(){}
ClassA.prototype.color="red";
ClassA.prototype.sayColor=function(){
alert(this.color);
};

function ClassB(){

}

ClassB.prototype=new ClassA();
ClassB.prototype.name="";
ClassB.prototype.sayName=function(){
alert(this.name);
};

var objA=new ClassA();
var objB=new ClassB();
objA.color="red";
objB.color="blue";
objB.name="Nicholas";
objA.sayColor();
objB.sayColor();
objB.sayName();

注意,调用ClassA的构造函数是,没有给它传递餐宿。这在原型链中试标准做法。要确保构造函数没有任何参数。原型的弊端是不
支持多重继承。
5.混合方式
在前一章,你学过创建建类的最好方式是用构造函数方式定义属性,用原型方式定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的
属性,用原型链继承prototye对象的方法。
function ClassA(sColor){
this.color=sColor;
}

ClassA.prototype.sayColor=function(){alert(this.color);};
function ClassB(sColor,sName){
ClassA.call(this,sColor);
this.name=sName;
}
ClassB.prototype=new ClassA();
ClassB.prototype.sayName=function(){
alert(this.name);
}

var objA=new ClassA("red");
var objB=new ClassB("blue","Nicholas");
objA.sayColor();//outputs "red"
objB.sayColor();//outputs "blue"
objB.sayName();//outputs "Nicholas"
一个更实际的例子
function Polygon(iSides){
this.sides=iSides;
}

Polygon.prototype.getArea=function(){
return 0;
};

function Triangle(iBase,iHeight){
Polygon.call(this,3);
this.base=iBase;
this.height=iHeight;
}

Triangle.prototype=new Polygon();
Triangle.prototype.getArea=function(){
return 0.5*this.base*this.height;
};

function Rectangle(iLength,iWidth){
Polygon.call(this,4);
this.length=iLength;
this.width=iWidth;
}

Rectangle.prototype=new Polygon();
Rectangle.prototype.getArea=function(){
return this.length*this.width;
}

var triangle=new Triangle(12,4);
var rectangle=new Rectangle(22,10);
alert(triangle.sides);
alert(triangle.getArea());

alert(rectangle.sides);
alert(rectangle.getArea());
其他继承方式
zInherit XbObjects
分享到:
评论

相关推荐

    javascript面向对象学习笔记

    javascript面向对象学习笔记,包含类、对象、实现继承

    javascript学习笔记.docx

    f) 继承:只是在查询一个属性时自动发生,而不会在写属性时发生,就是说单写一个父类的属性时,JavaScript环境会为对象本身创建一个同名的属性,从此该属性就覆盖了父类中的属性。 12) 创建一个数组可用 new Array()...

    《javascript设计模式》学习笔记二:Javascript面向对象程序设计继承用法分析

    百度百科对继承的解释是:继承是指一个对象直接使用另一对象的属性和方法。 (话说百科对于计算机概念的继承的解释也太简略了) 继承的好处,优点学过面向对象的同学都知道,就不赘述了。 javascript中的继承有三种...

    JavaScript高级资料JavaScript代码 素材 笔记 作业资料.zip

    JavaScript高级资料JavaScript代码 素材 笔记 作业资料,学习资料 01-创建类和对象.html 02-类中添加方法(1).html 03-类的继承(1).html 04-super关键字调用父类普通函数(1).html 05-子类继承父类方法同时扩展自己...

    JavaScript 笔记二 Array和Date对象方法

    JavaScript的对象基础 本篇主要讲解本地对象Array和Date的各种方法。 一种面向对象语言需要向开发者提供四种基本能力: (1) 封装——把相关的信息(无论数据或方法)存储在对象中的能力。 (2) 聚集——把一个对象...

    JavaScript面向对象笔记

    无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含...

    javascript学习笔记(十) js对象 继承

    javascript学习笔记之js对象 继承介绍,需要的朋友可以参考下

    Javascript经典学习笔记

    javascript经典学习笔记总结,介绍几本数据类型,Function对象原理,js中没有类的概念,类继承的多种方式的总结,很不错,欢迎下载。

    Javascript 读书笔记索引贴

    基础篇 Javascript学习笔记1 数据类型 Javascript学习笔记2 函数 Javascript学习笔记3 作用域 Javascript学习笔记4 Eval函数 Javascript学习笔记5 类和对象 Javascript学习笔记6 prototype的提出 Javascript学习...

    Javascript学习笔记5 类和对象

    面向对象语言三大特点:继承,多态,封装,这三点虽然Javascript没有提供天然的语法实现,但是我们都可以通过prototype等技巧来实现,因此这种说法似乎不过分。 在Javascript中,构造对象有三种方式: 1. 首先,我们...

    ExtJS4中文教程2 开发笔记 chm

    JavaScript对象与继承教程之内置对象(下) JavaScript对象及继承教程(上) javascript正则表达式(一) javascript正则表达式(二) JavaScript的10种跨域共享方法 JavaScript类型总览 JavaScript获取文本框光标的像素...

    JavaScript-学习笔记.docx

    自己学习笔记。视频:腾讯课堂:渡一教育 内容大纲: 预编译: 函数 闭包问题 对象 原型、原型链、call、apply 继承模式 对象克隆 数组 类数组 数组去重 自定义type方法(类似于typeof) try-catch es5.0严格模式 ...

    Javascript学习笔记之 对象篇(三) : hasOwnProperty

    // Poisoning Object.prototype Object.prototype.bar = 1; var foo = {goo: undefined}; foo.bar;...Javascript 中没有其他方法能判断一个属性是定义在对象本身还是继承自原型链。 hasOwnProperty 作为

    Javascript 学习笔记之 对象篇(二) : 原型对象

    Javascript 并没有类继承模型,而是使用原型对象 prototype 进行原型式继承。尽管人们经常将此看做是 Javascript 的一个缺点,然而事实上,原型式继承比传统的类继承模型要更加强大。举个例子,在原型式继承顶端构建...

    apuntes-javascript-avanzado:Javascript 笔记 II - 高级

    “面向对象”Javascript(原型,继承) 如何更好地构建 JS 代码(设计和代码模式) 如何测试你的 js 代码(单元测试) 本书中涵盖的概念将使您的 JS 代码面向对象、经过测试、可扩展、更易于理解和更有条理。 ...

    Extjs学习笔记之八 继承和事件基础

    这里接口的意思是Observable实际上...我前面的一篇文章 补点基础:Javascript中的类和闭包 也是为这篇做准备。另外,博客园内还有一个写的很好的系列 JavaScript继承详解. 他主要是根据Douglas Crockford的两篇文章写的

    JavaScript高级程序设计 阅读笔记(十四) js继承机制的实现

    继承是面向对象语言的必备特征,即一个类能够重用另一个类的方法和属性。在JavaScript中继承方式的实现方式主要有以下五种:对象冒充、call()、apply()、原型链、混合方式

Global site tag (gtag.js) - Google Analytics