`
jickcai
  • 浏览: 246985 次
  • 性别: 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学习资源,旨在帮助开发者和初学者深入理解这门广泛应用于Web开发的脚本语言。这份笔记涵盖了JavaScript的基础语法、核心概念以及高级特性,旨在构建一个...

    韩顺平javascript笔记(最全整理 dom编程 oop 基础语法)

    韩顺平老师的JavaScript笔记全面涵盖了基础语法、面向对象编程以及DOM编程,这些都是学习JavaScript时至关重要的知识点。 首先,基础语法是JavaScript学习的基石。包括变量声明(var、let、const)、数据类型(如...

    JavaScript基础教程笔记

    ### JavaScript基础教程笔记知识点 #### 一、JavaScript简介 - **定义**:JavaScript是一种轻量级的编程语言,主要用于Web浏览器中的网页交互控制。 - **发展历史**:1995年由Netscape公司的Brendan Eich设计并...

    韩顺平 javascript 笔记 js面向对象 笔记 韩顺平 完整版

    韩顺平的JavaScript面向对象笔记详细地介绍了JavaScript中面向对象编程的关键概念和技术,包括变量作用域、对象引用、this的使用、对象的方法以及对象的构造和继承,这些都是理解和编写高效JavaScript代码的基础。

    javascript入门学习笔记

    二、函数与对象 函数是JavaScript中的重要概念,允许我们将代码封装起来以供重复使用。对象则是一种数据结构,可以存储键值对,它们之间可以通过原型链实现继承。理解闭包和作用域对于掌握JavaScript的高级特性至关...

    《李炎恢Javascript笔记》+源码

    《李炎恢Javascript笔记》是一本深入浅出的JavaScript学习资料,它涵盖了JavaScript的基础到实践应用的诸多方面。这本书的特点是将复杂的编程概念分解为易于理解的小知识点,并且提供了源码示例,使得读者能够更好地...

    JavaScript基础笔记

    另外,JavaScript的对象还支持原型继承和构造函数,这是实现面向对象编程的基础。 Date对象用于处理日期和时间,提供了许多方法来获取或设置日期,如`getFullYear()`,`getMonth()`,`getDate()`等。String对象提供...

    javascript学习笔记.docx

    这篇学习笔记主要涵盖了JavaScript的基础语法和客户端JavaScript的相关知识。 1. **JavaScript基本语法**: - **变量声明**:未声明的变量尝试读取时会产生错误,而写入未声明的变量会创建一个全局变量。 - **...

    Javascript入门笔记和一些代码

    在"03_Advanced_JavaScript"中,你将接触到更高级的主题,比如作用域(全局、局部、闭包)、原型链和继承机制、异步编程(回调函数、Promise、async/await)、模块化(CommonJS、ES6模块)、以及错误处理。...

    JavaScript基础学习笔记

    JavaScript 基础学习笔记 本资源摘要信息基于黑马视频记录的学习笔记,涵盖了 JavaScript 基础知识点,包括 HTML、CSS、JavaScript、DOM、事件处理等内容。 HTML 和 CSS 基础 * HTML 结构:head、body、title、...

    JavaScript的笔记

    JavaScript是一种广泛应用于Web开发的脚本语言,它主要在...以上就是JavaScript笔记的主要内容,通过深入学习和实践,你可以掌握这门强大的脚本语言,无论是客户端交互还是服务器端开发,JavaScript都能发挥重要作用。

    JavaScript核心笔记

    ### JavaScript核心笔记精要 #### 一、定义变量与类型转换 **1.1 定义变量的方法** ...以上内容涵盖了《JavaScript核心笔记》的主要知识点,希望能够帮助读者更好地理解和掌握JavaScript的基础知识。

    javascript面向对象学习笔记

    JavaScript是一种基于原型和函数式编程的动态类型语言,面向对象编程是其核心概念之一。本文将深入探讨JavaScript中的面向对象编程,主要关注类、对象、以及实现继承的几种模式。 首先,我们来看对象的创建。在...

    前端开发必备JavaScript(含源码课件笔记总结)

    JavaScript是基于原型的面向对象语言,你需要了解构造函数、原型链、对象属性与方法、继承机制(原型链继承、借用构造函数、组合继承、寄生组合继承、原型式继承、ES6的类)。此外,还要掌握闭包和作用域,这是...

    JavaScript_Demo,文章《JavaScript笔记》配套代码

    这篇文章《JavaScript笔记》的配套代码提供了丰富的实例,旨在帮助读者深入理解和掌握JavaScript的基本概念、语法以及应用技巧。 首先,我们来看看“JS笔记”部分。在学习JavaScript时,理解变量的声明、数据类型...

    JavaScript 入门 新手学习笔记

    9. **面向对象编程**:JS支持基于原型的面向对象,包括构造函数、原型链、继承等概念。 10. **JavaScript框架和库**:如jQuery简化了DOM操作,React、Vue、Angular等现代框架则帮助构建大型单页应用。 这些笔记...

    JavaScript笔记 第二天

    这篇笔记主要涵盖了JavaScript的第二天课程内容,旨在深入理解并掌握JavaScript的基础知识。以下是对这些知识点的详细解释: 1. **变量与数据类型** - 变量:JavaScript中的变量是用于存储数据的容器。使用`var`, ...

    张孝祥JAVASCRIPT笔记图片总结

    【张孝祥JAVASCRIPT笔记图片总结】是一份由知名IT讲师张孝祥编写的JavaScript学习资料,主要以图片的形式呈现,旨在帮助学习者更直观、清晰地理解和记忆JavaScript的关键概念与技术点。这份笔记涵盖了JavaScript的...

    前端JavaScript基础学习笔记案例

    个人在学习前端JavaScript时的学习笔记,内含JavaScript的中基础知识点,以及案例。里面有兼容市面上浏览器的方法 1.知识点包括:基础语法,字符串操作,object对象,DOM对象,BOM对象,offset,client,scroll对象...

    JavaScript学习笔记

    本学习笔记旨在帮助初学者快速掌握JavaScript的核心概念和技术,实现从入门到精通的过渡。 1. **基础语法** - 变量声明:JavaScript支持var、let和const关键字声明变量,理解它们的作用域和提升特性至关重要。 - ...

Global site tag (gtag.js) - Google Analytics