当前位置: 首页 > 科技观察

《JavaScript闯关记》的单一内置对象

时间:2023-03-12 10:58:49 科技观察

ECMA-262将内置对象定义为“不依赖于宿主环境的JavaScript实现提供的对象,并且这些对象在JavaScript程序运行之前已经存在被执行了。”这意味着开发人员不必显式实例化内置对象,因为它们已经实例化了。我们之前已经介绍了大部分内置对象,例如Object、Array和String。ECMA-262还定义了两个单例内置对象:Global和Math。Global对象Global对象可以说是JavaScript中最特殊的对象,因为无论从哪个角度看,这个对象都不存在。全局对象在某种意义上被定义为绝对的“口袋对象”。换句话说,不属于任何其他对象的属性和方法最终成为它的属性和方法。在全局范围内定义的所有属性和函数都是Global对象的属性。本书前面描述的函数,如isNaN()、isFinite()、parseInt()和parseFloat(),实际上是Global对象的方法。除此之外,Global对象还包含一些其他方法。URI编码方法Global对象的encodeURI()和encodeURIComponent()方法可以对URI(UniformResourceIdentifiers,通用资源标识符)进行编码,发送给浏览器。某些字符(例如空格)不能包含在有效的URI中。而这两种URI编码方式都可以对URI进行编码。它们用特殊的UTF-8编码替换所有无效字符,以便浏览器能够接受和理解它们。其中,encodeURI()主要用于整个URI,encodeURIComponent()主要用于对URI中的某一段进行编码。它们的主要区别在于encodeURI()不会对本身就是URI的特殊字符进行编码,例如冒号、正斜杠、问号和井号;而encodeURIComponent()将对它找到的任何非标准字符进行编码。看看下面的例子。varuri="http://shijiajie.com/illegalvalue.htm#start";console.log(encodeURI(uri));//"http://shijiajie.com/illegal%20value.htm#start"console.log(encodeURIComponent(uri));//"http%3A%2F%2Fshijiajie.com%2Fillegal%20value.htm%23start"使用encodeURI()编码结果除空格外所有字符都完好无损,只有空格被替换成%20.encodeURIComponent()方法用相应的编码替换所有非字母数字字符。这就是为什么您可以对整个URI使用encodeURI(),但只能对附加到现有URI的字符串使用encodeURIComponent()。通常,我们更多地使用encodeURIComponent()方法而不是encodeURI(),因为在实践中更常见的是对查询字符串参数而不是基本URI进行编码。encodeURI()和encodeURIComponent()方法对应的两个方法分别是decodeURI()和decodeURIComponent()。其中decodeURI()只能对encodeURI()替换的字符进行解码。例如,它将%20替换为空格,但对%23不执行任何操作,因为%23表示井号#,它不会使用encodeURI()替换。同样,decodeURIComponent()能够解码所有用encodeURIComponent()编码的字符,即它可以解码任何特殊字符的编码。看下面的例子:varuri="http%3A%2F%2Fshijiajie.com%2Fillegal%20value.htm%23start";console.log(decodeURI(uri));//http%3A%2F%2Fshijiajie.com%2Fillegalvalue.htm%23startconsole.log(decodeURIComponent(uri));//http://shijiajie.com/illegalvalue.htm#start这里,变量uri包含了一个经过encodeURIComponent()编码的字符串。在第一次调用decodeURI()的输出中,只有%20被替换为空格。在第二次调用decodeURIComponent()的结果中,所有特殊字符的编码被替换为原始字符,并得到一个未转义的字符串(但这个字符串不是有效的URI)。eval()方法eval()方法就像一个完整的JavaScript解析器,它只接受一个参数,即要执行的JavaScript字符串。看下面的例子:eval("console.log('hi')");这行代码相当于下面这行代码:console.log("hi");当解析器发现代码调用了eval()方法时,会将传入的参数解析为实际的JavaScript语句,然后将执行结果插入到原来的位置。通过eval()执行的代码被视为包含调用的执行环境的一部分,因此具有与该执行环境相同的作用域链。这意味着eval()执行的代码可以引用包含环境中定义的变量,例如:varmsg="helloworld";eval("console.log(msg)");//"helloworld"是可见的,变量msg在eval()调用的环境之外定义,但在其中调用的console.log()仍然能够显示“helloworld”。这是因为上面的第二行代码最终被替换为实际行。同样,我们也可以在eval()调用中定义一个函数,然后在调用的外部代码中引用这个函数:eval("functionsayHi(){console.log('hi');}");sayHi();//"hi"显然,函数sayHi()定义在eval()内部。但是由于对eval()的调用最终会被定义该函数的实际代码所取代,因此可以在下一行调用sayHi()。变量也是如此:eval("varmsg='helloworld';");控制台日志(消息);//任何由“helloworld”在eval()中创建的变量或函数都不会被提升,因为解析代码,它们包含在一个字符串中;它们仅在执行eval()时创建。在严格模式下,外部无法访问在eval()中创建的任何变量或函数,因此前面两个示例会出错。同样,在严格模式下,给eval赋值也会报错:"usestrict";eval="hi";//causeerror对代码串的解释能力很强大,但也很危险。因此,在使用eval()时必须格外小心,尤其是当它用于执行用户输入的数据时。否则,恶意用户可能会输入威胁您站点或应用程序安全的代码(所谓的代码注入)。全局对象的属性全局对象还包含一些属性,其中一些在本书前面已经介绍过。比如特殊值undefined、NaN、Infinity都是Global对象的属性。此外,所有原生引用类型的构造函数,如Object和Function,也是Global对象的属性。下表列出了全局对象的所有属性。属性描述属性描述undefined特殊值undefinedDate构造函数DateNaN特殊值NaNRegExp构造函数RegExpInfinity特殊值InfinityError构造函数Error对象构造函数ObjectEvalError构造函数EvalError数组构造函数ArrayRangeError构造函数RangeError函数构造函数StringConstructorStringTypeErrorConstructorTypeErrorNumber构造函数编号URI错误构造函数URIErrorECMAScript5明确禁止给undefined、NaN和Infinity赋值,即使在非严格模式下也会出错。JavaScript虽然没有指出如何直接访问Global对象,但是Web浏览器总是将这个global对象作为window对象的一部分来实现。因此,在全局范围内声明的所有变量和函数都成为窗口对象的属性。看看下面的例子。varcolor="red";functionsayColor(){console.log(window.color);}window.sayColor();//"red"JavaScript中的window对象不仅起到指定Global对象的作用,还承担许多其他任务。Math对象JavaScript还提供了一个用于存储数学公式和信息的公共位置,即Math对象。与我们直接用JavaScript编写的计算函数相比,Math对象提供的计算函数执行起来要快得多。Math对象还提供属性和方法来协助这些计算。Math对象的属性Math对象的属性大多是数学计算中可能用到的特殊值。下表列出了这些属性。属性说明属性说明Math.E自然对数的底,即常数e的值Math.LN1010的自然对数Math.LN22的自然对数Math.LOG2E以2为底的e的对数以e为底的对数Math.PIπ的值Math.SQRT1_21/2的平方根(即2的平方根的倒数)Math.SQRT22的平方根min()和max()methodsMath对象也包含了很多方法,使用它来辅助完成简单和复杂的数学计算。其中,min()和max()方法用于确定一组值中的最小值和最大值。两种方法都可以接受任意数量的数字参数,如以下示例所示。varmax=Math.max(3,54,32,16);console.log(max);//54varmin=Math.min(3,54,32,16);console.log(min);//3到要查找数组中的最大值或最小值,可以使用如下所示的apply()方法。varvalues=[1,2,3,4,5,6,7,8];varmax=Math.max.apply(Math,values);console.log(max);//8这个技巧的关键是把Math对象作为apply()的第一个参数,从而正确设置该值。然后,任何数组都可以作为第二个参数传递。舍入方法这里有几种将小数值舍入为整数的方法:Math.ceil()、Math.floor()和Math.round()。这三种方法分别遵循以下舍入规则:Math.ceil()执行舍入,即总是将值向上舍入到最接近的整数;Math.floor()执行向下舍入,即总是将值向下舍入到最接近的整数;Math.round()执行标准舍入,即它总是将值舍入到最接近的整数。下面是使用这些方法的示例:console.log(Math.ceil(25.9));//26console.log(Math.ceil(25.5));//26console.log(Math.ceil(25.1));//26console.log(Math.round(25.9));//26console.log(Math.round(25.5));//26console.log(Math.round(25.1));//25console.log(Math.floor(25.9));//25console.log(Math.floor(25.5));//25console.log(Math.floor(25.1));//25random()方法Math.random()方法返回0到1之间的一个不包括0到1之间的随机数。对于一些站点来说,这种方法非常有用,因为它可以用来随机显示一些名言和新闻事件。应用下面的公式,Math.random()可用于从整数范围内随机选择一个值。value=Math.floor(Math.random()*可能值总数+***可能值)公式中使用了Math.floor()方法,因为Math.random()总是返回一个分数值。并将这个十进制值乘以一个整数,再加上另一个整数,最后的结果还是一个小数。例如,如果你想选择一个介于1和10之间的值,你可以这样写代码:varnum=Math.floor(Math.random()*10+1);一共有10个可能的值(1到10),而第一个可能的值是1。如果你想选择一个介于2和10之间的值,你应该将上面的代码更改为:varnum=Math.floor(Math.random()*9+2);从2数到109个数,所以可能值的总数是9,第一个可能值是2。大多数情况下,其实可以用一个函数来计算可能值的总数和第最后一个可能的值,例如:functionselectFrom(lowerValue,upperValue){varchoices=upperValue-lowerValue+1;returnMath.floor(Math.random()*choices+lowerValue);}varnum=selectFrom(2,10);console.log(num);//一个介于2和10之间(包括2和10)的数值函数selectFrom()接受两个参数:应该返回的最小值和最大值。并用最大值减去最小值加1得到可能取值的总数,然后它把这些值应用到前面的公式中。这样调用selectFrom(2,10)就可以得到一个2到10之间(包括2和10)的值。使用此函数,您可以轻松地从数组中随机取出一项,例如:varcolors=["red","green","blue","yellow","black","purple","brown"];varcolor=colors[selectFrom(0,colors.length-1)];console.log(color);//可以是数组中包含的任意字符串其他方法Math对象还包含完成各种简单或复杂相关的其他方法与计算相关的方法,但对这些方法中的每一种方法的详细讨论以及它们的适用范围超出了本书的范围。下面我们给出一个表格,列出了Math对象中没有介绍的方法。方法说明Math.abs(num)返回num的绝对值Math.asin(x)返回x的反正弦值Math.exp(num)返回Math.E的num次方Math.atan(x)返回反正切值x值Math.log(num)返回num的自然对数Math.atan2(y,x)返回y/x的反正切值Math.pow(num,power)返回num的幂Math.cos(x)返回x的余弦Math.sqrt(num)返回num的平方根Math.sin(x)返回x的正弦Math.acos(x)返回x的反余弦Math.tan(x)返回x的正切x尽管ECMA-262指定了这些方法,但不同的实现可能对这些方法使用不同的算法。毕竟,有许多不同的方法可以计算一个值的正弦、余弦和正切。也正因为如此,这些方法在不同的实现中可能会有不同的精度。Level//如何高效地生成n个范围内的m个不重复随机数(m<=n)vargetRandomNumber=function(n,m){//要实现的方法体}console.log(getRandomNumber(20,3));//8,4,19