5.5.5 函数属性和方法

前面曾经提到过,ECMAScript中的函数是对象,因此函数也有属性和方法。每个函数也都包含两个属性:length和prototype。其中,length属性表示函数希望接收的参数的个数,也可能为0.

在ECMAScript核心所定义的全部属性中,最耐人寻味的就要数prototype属性了。对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。在ES5中,prototype属性是不可枚举的,因此使用for-in无法发现。

每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。

	function sum(num1, num2){
	    return num1 + num2;
	}

	function callSum1(num1, num2){
	    return sum.apply(this, arguments);//传入arguments对象
	}

	function callSum2(num1, num2){
	    return sum.apply(this,[num1, num2]);//传入数组
	}

	alert(callSum1(10,10));//20
	alert(callSum2(10,10));//20

由以上代码可知,第二个参数可以是Array的实例,也可以是arguments对象。

call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来,如下所示:

	function sum(num1, num2){
	    return num1 + num2;
	}

	function callSum(num1, num2){
	    return sum.call(this, num1, num2);
	}

	alert(callSum(10,10));//20

事实上,传递参数并非apply()和call()真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域

使用call()或apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。

ECMAScript5还定义了一个方法:bind()。这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

	window.color = "red";
	var o = {
	    color:"blue"
	};

	function sayColor(){
	    alert(this.color);
	}
	var objectSayColor = sayColor.bind(o);
	objectSayColor();//blue

在这里,sayColor()调用bind()并传入对象o,创建了objectSayColor()函数。objectSayColor函数的this值等于o,因此即使是在全局作用域中调用这个函数,也会看到”blue”。

IE8并不支持bind()

5.6 基本包装类型

为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型:Boolean,Number和String。每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象。

	var s1 = "some text";
	var s2 = s1.substring(2);

其实,为了让我们事先这种直观的操作,后台已经自动完成了一系列的处理。经过此番处理,基本的字符串值就变得跟对象一样了。

引用类型与基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这以为着我们不能再运行时为基本类型值添加属性和方法。来看下面的例子:

	var s1 = "some text";
	s1.color = "red";
	alert(s1.color);//undefined

问题的原因就是第二行创建的String对象在执行第三行代码时已经被销毁了。第三行代码又创建了自己的String对象,而该对象没有color属性。

对基本包装类型的实例调用typeof会返回”object”,而且所有基本包装类型的对象都会被转换为布尔值true。

Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。

要注意的是,使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。例如:

	var value = "25";
	var number = Number(value);//转型函数
	alert(typeof number);//"number"

	var obj = new Number(value);//构造函数
	alert(typeof obj);//"object"

在这个例子中,变量number中宝尊的是基本类型的值25,而变量obj中保存的是Number的实例。

5.6.1 Boolean类型

Boolean类型是与布尔值对应的引用类型。Boolean类型的实例重写了valueOf()和toString()方法,返回基本类型值true或false;

最常见的问题是在布尔表达式中使用Boolean对象。例如:

	var falseObject = new Boolean(false);
	var result = falseObject && true;
	alert(result);//true

	var falseValue = false;
	result = falseValue && true;
	console.log(result);//false

在这个例子中,我们使用false值创建一个Boolean对象。然后,将这个对象与基本类型值true构成了逻辑与表达式。在布尔运算中,false && true 等于false。可是,示例中的这行代码是对falseObject而不是对它的值(false)进行求值。前面讨论过,布尔表达式中的所有对象都会被转换为true,因此falseObject对象在布尔表达式中代表的是true。结果便是true了。

基本类型与引用类型的布尔值还有两个区别。首先typeof操作符对基本类型返回”boolean”,而对引用类型返回”object”。例如:

	alert(typeof falseObject);//object
	alert(typeof falseValue);//boolean
	alert(falseObject instanceof Boolean);//true
	alert(falseValue instanceof Boolean);//false

由于Boolean对象是Boolean类型的实例所以使用instanceof操作符测试Boolean对象会返回true,而测试基本类型的布尔值则返回false。

理解基本类型的布尔值与Boolean对象之间的区别非常重要——当然,我们的建议是永远不要使用Boolean对象

5.6.2 Number 类型

Number是与数字值对应的引用类型。

可以为toString()方法传递一个表示基数的参数,告诉它返回几进制数值的字符串形式。

Number类型还提供了一些用于将数值格式化为字符串的方法。其中,toFixed()方法会按照指定的小数位返回数值的字符串表示。

如果数值本身包含的小数位比指定还多,那么接近指定的最大小数位的值就会舍入,而这个自动舍入的特性,使得toFixed()方法很适合处理货币值。但需要注意的是,不同浏览器给这个方法设定的舍入规则可能会有所不同。

另外一个可用于格式化数值的方法是toExponential(),该方法返回以指数表示法表示的数值的字符串形式。

对一个数值来说,toPrecision()方法可能会返回固定大小格式,也可能返回指数格式,具体规则是看哪种格式最合适。

与此同时,我们仍然不建议直接实例化Number类型,语言与显示创建Boolean对象一样。具体来讲,就是在使用typeof和instanceof操作符测试基本类型数值与引用类型数值时,得到的结果完全不同,如下面的例子所示:

	var numberOject = new Number(10);
	var numberValue = 10;
	alert(typeof numberOject);//object
	alert(typeof numberValue);//number
	alert(numberOject instanceof Number);//true
	alert(numberValue instanceof Number);//false

instanceof用来判断一个变量是否是某个对象的实例。类似地,Number对象时Number类型的实例,而基本类型的数值则不是。

5.6.3 String类型

String类型是字符串的对象包装类型。String类型的每个实例都有一个length属性,表示字符串中包含多个字符。应该注意的是,即使字符串中包含双字节字符,每个字符仍然算一个字符。

1.字符方法

两个用于访问字符串中特定字符的方法是:charAt()和charCodeAt()。其中charAt()方法以单字符串的形式返回给定位置的那个字符。如果你想得到的不是字符而是字符编码,那么就要使用charCodeAt()了。

2.字符串操作方法

下面介绍与操作字符串有关的几个方法。第一个就是concat(),用于将一或多个字符串拼接起来,返回拼接得到的新字符串。

虽然concat()是专门用来拼接字符串的方法,但时间中使用更多的还是加号操作符(+)。而且使用加号操作符在大多数啊情况下都比使用concat()方法要简便易行。

ECMAScript还提供了三个基于子字符串创建新字符串的方法:slice(),substr()和substring()。与concat()方法一样,这三个方法返回一个基本类型的字符串值。

在传递给这些方法的参数是负值的情况下,它们的行为就不尽相同了。

3.字符串位置方法

有两个可以从字符串中查找子字符串的方法:indexOf()和lastIndexOf()。这两个方法都是从一个字符串中搜索给定的子字符串,然后返回子字符串的位置(如果没有找到,则返回-1)。

4.trim()方法

ECMAScript5为所有字符串定义了trim()方法。这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。

由于trim()返回的是字符串的副本,所以原始字符串中的前置及后缀空格会保持不变。IE8不支持

5.字符串大小写转换写法

ECMAScript中涉及字符串大小写转换的方法有4个:toLowerCase(),toLocaleLowerCase(),toUpperCase()和toLocaleUpperCase()。

6.字符串的模式匹配方法

String类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是match(),在字符串上调用这个方法,本质上与调用RegExp的exec()方法相同。match()方法只接受一个参数,要么是正则表达式,要么是一个RegExp对象。

另一个用于查找模式的方法是search()。此方法返回字符串中第一个匹配项的索引

为了简化替换子字符串的操作,ECMAScript提供了replace()方法

最后一个与模式匹配有关的方法是split(),这个方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。split()方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。

对split()中正则表达式的支持因浏览器而异。

7.localeCompare()方法

与操作字符串有关的最后一个方法是localeCompare(),这个方法比较两个子字符串,并返回结果值。

8.fromCharCode()方法

另外,String构造函数本身还有一个静态方法:fromCharCode()。这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串。从本质上来看,这个方法与实例方法charCodeAt()执行的是相反的操作。

9.HTML方法

早期的Web浏览器提供商觉察到了使用JavaScript动态格式化HTML的需求。于是,这些提供商就扩展了标准,实现了一些专门用于简化常见HTML格式化任务的方法。不过,应该尽量不使用这些方法,因为它们创建的标记通常无法表达语义。

5.7 单体内置对象

ECMA-262对内置对象的定义是:“由ECMAScript实现提供的,不依赖与宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。”例如Object,Array和String。ECMA-262还定义了两个单体内置对象:Global和Math。

5.7.1 Global对象

Global(全局)对象可以说是ECMAScript中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。Global对象还包含一些其他方法。

1.URI编码方法

Global对象的encodeURI()和encodeURIComponent()方法可以对URI进行编码,以便发送给浏览器。前者主要用于整个URI,而后者主要用于对URI中的某一段进行编码。

它们的主要区别在于,前者不会对本身属于URI的特殊字符进行编码,例如冒号,正斜杠,问号和井字号;而后者则会对它发现的额任何非标准字符进行编码。

与上面两个方法对应的还有两个解码方法。

2.eval()方法

现在,我们介绍最后一个——大概也是整个ECMAScript语言中最强大的饿一个方法:eval()。eval()方法就像是一个完整的ECMAScript解析器,它只接受一个参数,即要执行的ECMAScript(或JS)字符串。

能够解释代码字符串的能力非常强大,但也非常危险。因此在使用eval()时必须极为谨慎,特别是在用它执行用户输入数据的情况下。否则,可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入).

3.Global对象的属性

Global对象还包含一些属性,例如,特殊的值undefined,NaN以及Infinity都是Global对象的属性。此外,所有原生引用类型的构造函数,想Object和Function,也都是Global对象的属性。

ES5明确禁止给undefined,Nan和Infinity赋值

4.window对象

ECMAScript虽然没有指出如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。

5.7.2 Math对象

ECMAScript还为保存数学公式和信息提供了一个公共位置,即Math对象。Math对象提供的计算功能执行起来要快得多。

1.Math对象的属性

比如Math.PI

2.min()和max()方法

可接受任意多参数,可使用apply()方法

3.舍入方法

有三个,Math.ceil(),Math.floor()和Math.round()

4.random()方法

可从数组中随机取出字符串

5.其他方法

比如Math.sqrt(num),不同实现可能会对这些方法采用不同的算法,所以,这些方法在不同的实现中可能会有不同的精度。

5.8 小结