我的文章会在我的Blog同步更新,Blog刚搭了几天,用于记录我的学习过程:Million
<h2>5.5Function类型</h2>
作者说ECMAScript中最有意思的是函数,有意思的根源在于,函数实际上是对象。每个函数都是Function类型的实例。
与其他引用类型一样,函数类型也有属性和方法。由于函数是对象,因此函数名实际上只是一个指向函数对象的指针,不会与某个函数绑定。
函数通常是使用函数声明语法定义的,如下面的例子所示:
function sum(num1,num2){
return num1+num2;
}
这与下面使用函数表达式定义函数的方法几乎相差无几:
var sum = function(num1,num2){
return num1+num2;
}; // ← 注意最后的分号,声明完变量必加的分号
第二段代码定义了变量sum并将其初始化为一个函数(注意这句话的顺序,把一个定义好的变量初始化为一个函数)。我们可以发现用函数表达式创建的函数,function关键字后面没有函数名。因为没有必要。通过变量sum即可以引用到函数(注意,你不能说变量sum是函数名)。另外,注意函数末尾有一个分号,因为说到底sum还是个变量,只是变量的值是一个函数!
最后一种定义函数的方式,因为函数是对象,所以可以使用Function构造函数。Function可以接收任意个数量的参数,最后一个参数始终被当作函数体,前面的都是函数的参数。例子:
var sum = new Function("num1","num2","num3","return num1+num2"); //不推荐
从技术的角度讲,这是一个函数表达式,但是不推荐使用。因为这种语法会导致解析两次代码(第一次是解析常规ECMAScript,第二次是解析传入构造函数中的字符串),从而影响性能。不过这种语法有利于理解“函数是对象,函数名是指针”这个概念。
由于函数名仅仅是指针(好唏嘘,函数名以为用它的名字创建的函数就是它的,结果,它只是一个指针。)一个对象可以有很多指针,所以一个函数也可以有多个名字。例子:
function sum(num1,num2){
return num1+num2;
}
var anotherSum = sum;
alert(anotherSum(10,10)); //20
sum = null;
alert(anotherSum(10,10)); //20
即使把sum给设置为null,但因为这个指针已经复制了一份给anotherSum,所以这个函数对象还是可以被引用到,不会因为sum不见了,函数就跟着没了。(亲测PHP似乎就不能用这种方法“转换函数名”)注意,使用不带圆括号的函数名是访问函数指针,使用带括号的函数名就表示要调用这个函数。
<h3>5.5.1没有重载</h3>
把函数名想象为指针,就能理解为什么ECMAScript没有重载。例子:
function sum(num1,num2){
return num1+100;
}
function sum(num1){
return num1+200;
}
console.log(sum(100,100)); // 300
显然,例子中声明了两个同名函数,而结果是后面的函数覆盖了前面的函数。因为在创建第二个函数时,实际上覆盖了引用第一个函数的变量addSomeNumber。
<h3>5.5.2函数声明与函数表达式</h3>
实际上,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明并使其在执行任何代码之前可用(可以访问);至于函数表达式,就必须等到解析器执行到它所在的代码行,才会真正被解释执行。(所以函数声明有函数声明提升(function declaration hoisting),函数表达式没有)函数表达式不能写在要调用这个函数的代码之后。
函数声明提升:Javascript引擎在第一遍会声明函数并将它们放到源代码树的顶部。所以即使声明函数的代码在调用它的代码后面。JavaScript也能把函数声明提升到顶部。
除了什么时候可以通过变量访问函数这一点区别之外,函数声明与函数表达式的语法其实是等价的。再无其他差别。
✎ 有人觉得不妨在使用函数表达式的同时给函数命名,like this :
var sum = function sum(){ }
但这种语法在 Safari 中会出错。
<h3>5.5.3作为值的函数</h3>
因为ECMAScript中的函数名本身就是变量,所以函数也可以作为一个值来使用。也就是说,不仅可以像传递参数一样把一个函数当作参数传递给一个函数,而且可以将一个函数作为另一个函数的结果返回。例子:
function callSomeFunction(someFunc,someArgument){
return someFunc(someArgument)
}
这个函数接收两个参数,一个是函数,一个是普通参数,这个普通参数给传进来的函数使用。然后,就可以像下面这样传递函数了:
function add10(num){
return num+10;
}
var result1 = callSomeFunction(add10,10); //注意函数作为参数传递是不带括号的,带括号的函数会执行,参数就变成这个函数执行完成返回的那个值了
alert(result1); //20
function getGreeting(name){
return "Hello " + name;
}
var result2 = callSomeFunction(getGreeting,"Nicholas");
alert(result2); // Hello Nicholas
注意,要访问函数的指针而不执行函数的话,必须去掉函数名后面的括号。因此上面例子给callSomeFunction()传递的是add10 和 getGreeting。而不是执行它们之后的结果。
可以从一个函数中返回一个函数,是一项极为有用的技术。例如,假设有一个对象数组(由对象组成的数组),我们想要根据对象中的某一个属性对数组进行排序,而传递给sort()方法的比较函数要接收两个参数,即要比较的值。可是,我们需要一种方式来告诉sort()按照哪个对象属性排序。要解决这个问题,可以定义一个函数,它接收一个属性名,然后根据属性名来创建一个匿名比较函数。下面就是这个函数的定义:
function creatComparisonFunction(propertyName){
return function(obj1,obj2){
var val1 = obj1[propertyName]; //细节,用方括号访问保存在变量propertyName中的属性,终于知道用处了
var val2 = obj2[propertyName];
if(val1<val2){
return -1;
}else if(val1>val2){
return 1;
}else{
return 0;
}
};
}
看起来很复杂,其实就是嵌套了一个函数,而且内部函数前面加了一个return操作符。在内部函数接收到propertyName参数后,它会用方括号表示法来取得给定属性的值。取得这个对象属性后,就用这个属性进行比较。完成对象数组的排序。用法:
var data = [{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(creatComparisonFunction("name"));
console.log(data[0].name); //Nicholas
data.sort(creatComparisonFunction("age"));
console.log(data[0].name); //Zachary
<h3>5.5.4函数内部的属性</h3>
在函数内部,有两个特殊的对象(函数对象中的对象):arguments和this。arguments在第三章介绍过,是一个类数组对象,包含这个传入函数的所有参数。虽然arguments的主要用途是保存所有函数参数,但arguments对象还有一个名叫callee的属性,该属性是一个指针。arguments.callee( )可以指代这个函数本身。用处是可以消除函数的执行与函数名之间的耦合。还是那个阶乘的例子:
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}
如果我把factorial这个函数名改为另一个函数的函数名:
var trueFactorial = factorial;
function factorial(){
return 0;
}
console.log(trueFactorial(5)) // 0
这个时候trueFactorial()函数就出错了,要降低这种耦合,使用arguments.callee( )是最好方法。
function factorial(num){
if(num<=1){
return 1;
}else{
return num*arguments.callee(num-1); //即使factorial被改写,arguments.callee()仍代表当前函数。
}
}
函数内部的另一个特殊对象是this,其行为与Java和C#中的this大致类似。换句话说,this引用的是函数据以执行的环境对象。当在网页的全局作用域中调用函数时,this对象引用的就是window。例子:
indow.color = "red";
var o = {color:"blue"};
function sayColor(){
console.log(this.color);
}
sayColor(); //red
o.sayColor=sayColor;
o.sayColor(); //blue
函数sayColor( )首先是在全局作用域中定义的,它引用了this对象。但在调用它之前,并不确定是在哪个作用域中调用。当在全局作用域中调用sayColor( ),this引用的是全局对象,所以this.color = window.color。结果返回red。当把这个函数赋给对象o,并调用o.sayColor( )时,this引用的是对象o,因此this.color = o.color。结果返回blue。(亲测把sayColor()函数放在另一个函数中去调用,最后得到的结果还是window,再嵌多一层函数也还是window,说明this引用的不是作用域,而是函数据以执行的环境对象,还有一个原因是当你把函数嵌套进另一个函数的时候,返回是window的原因是你此时调用的实际上已经不是一个方法,而是一个函数。)
ECMAScript5还规范化了另一个函数对象的属性caller。除了Opera早期版本不支持,其他浏览器IE,Firefox,Chrome,和Safari的所有版本及Opear9.6都支持caller属性。这个属性保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。例如:
function outer(){
inner();
}
function inner(){
console.log(inner.caller); //会打印出outer()的源代码
}
outer();
以上代码会打印出outer( )的源代码,因为outer()调用了inner(),所以inner.caller就指向outer()。当然不能试图用inner.caller( )来调用outer。否则会陷入无限回调使代码出错。为了更松散的耦合,也可以通过arguments.callee.caller()来访问相同的信息。
function outer(){
inner();
}
function inner(){
console.log(arguments.callee.caller); //会打印出outer()的源代码
}
outer();
arguments在严格模式下无法使用,arguments对象也有一个caller属性,但这个值永远是undefined。定义这个属性是为了分清arguments.caller和函数的caller属性,这个变化是出于增强这门语言的安全性。
严格模式还有一个限制:不能为函数的caller属性赋值,否则会导致错误。
<h3>5.5.5函数属性和方法</h3>
前面提过ECMAScript中函数是对象,所以函数就有属性和方法,上面介绍了一个caller。还有两个是length和prototype。
length属性表示函数希望接收的命名参数的个数。命名参数有1个,length的值就是1,参数有两个,length的值就是2。不解释。
作者说,在ECMAScript核心所定义的全部属性中,最耐人寻味的就要数prototype属性了(不止函数的prototype,其他对象的prototype属性也耐人寻味!)对与ECMAScript中的引用类型而言,prototype是保存他们所有实例方法的真正所在。换句话说,诸如valueOf(),toString()等方法实际上都是保存在了prototype属性名下。只不过是通过各自对象的实例访问到了。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的(第6章会详细介绍)。
在ECMAScript中,prototype属性是不可枚举的,因此无法用for-in无法发现prototype这个属性。(对property属性的讲解到此为止)
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数。实际上等于设置函数体内this对象的值。两个方法的区别只是在传入参数的方法上有区别而已。
首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例(就是普通数组),也可以是arguments对象。例子:
function sum(num1,num2){
return num1+num2;
}
function callSum1(num1,num2){
return sum.apply(this,arguments);
}
function callSum2(num1,num2){
return sum.apply(this,[num1,num2]);
}
console.log(callSum1(10,10)); //20
console.log(callSum2(10,10)); //20
在上面这个例子中,callSum1( )在执行sum()函数时传入了 this 作为 this 值(???)(因为是在全局作用域中调用的,所以传入的就是window对象)和arguments对象,而callSum2传入的第二个参数则是数组型。这两个函数都会正常执行并返回正确结果。(这个例子只是为了说明两种传入参数的方式都可以,还没体现出apply()和call()的作用)
✎在严格模式下,未指定环境对象而用函数,则this值不会转型为window(严格模式下的this不会默认转型为window)除非明确把函数添加到某个对象或者调用apply()或call(),否则this值将是undefined。
call()方法与apply()方法的作用相同,区别仅在于接收参数的方式不同。对于call()方法,第一个参数仍是this不变,变化的是其余参数都是直接传递给函数。换句话说,在使用call()方法时,参数都是逐个列举出来的。例子:
function sum(num1,num2){
return num1+num2;
}
function callSum1(num1,num2){
return sum.call(this,num1,num2);
}
console.log(callSum1(10,10)); // 20
在使用call()方法的情况下,callSum( )必须明确地传入每一个参数。结果与apply()没有什么不同。使用哪一种完全是看你觉得哪个更方便(MD以前还觉得这两个方法多深奥ZZ)。
apply()和call()的真正强大之处,是能够扩充函数赖以运行的作用域。例子:
window.color = "red";
var o = {color:"blue"};
function sayColor(){
console.log(this.color)
}
sayColor(); //red
sayColor.call(window); //red
sayColor.call(o); //blue
第一次调用sayColor()时在全局作用域中调用它,会显示“red”——因为对this.color的求值会转换成对window.color的求值。当运行sayColor。call(o)时,函数的执行环境对象就不一样了,因此此时函数体内的this对象指向了O,于是结果显示“blue”。
使用call()和apply()的最大好处,就是对象不需要与方法有任何耦合关系(不用为了在这个对象内调用方法而把函数写进对象中)。在书中前面的第一个例子中(笔记没有),为了在对象o中调用sayColor( ),要把这个函数写进对象o中,再通过o调用,而在这里的例子中,就不需要那么多步骤了。
ECMAScript5又定义了一个方法:bind()(所以函数对象的方法有三个)。这个方法会创建一个函数的实例(会创建一个函数),其this值会被绑定到传给bind()函数的值。例子:
window.color = "red";
var o = {color:"blue"};
function sayColor(){
console.log(this.color)
}
var objSayColor = sayColor.bind(o);
objSayColor();
在这里,sayColor()调用bind()并传入对象o,创建了objSayColor()函数。objSayColor()的this值等于o。因此无论在哪个环境对象中调用这个函数,都会看到“blue”。这种技巧的优点参考第22章。
因为是ECMAScrip5才有的方法,所以可以使用bind() 的浏览器有: IE9+,Firefox4+,Safari 5.1+,Opera 12+和Chrome。(兼容性挺低的)
最后说每个引用类型值都有继承的,都会被重写的toLocaleString()和toString(),valueOf(),这三个方法都始终返回函数的代码。返回代码的格式根据浏览器的不同会有差异。因为有返回值差异,所以返回的结果无法被用来实现任何功能,不过这些信息在调试代码时很有用。
<h2>5.6基本包装类型</h2>
读到这里又刷新了三观。这里说Boolean、Number、String是3个ECMAScript提供的特殊引用类型。这些类型与本章介绍的其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每创建一个基本类型的时候,后台就会创建一个对应的基本包装类型对象(WTF???),从而让我们能够调用一些方法来操作这些数据(这解释了为什么三种基本类型值也有自带的属性和方法)。例子,我们可以这样使用基本类型值的方法:
var s1 = "some text";
var s2 = s1.subString(2);
可以看到我们可以调用String类型自带的方法,但如下面的例子,我们不能在运行时为基本类型值添加属性和方法:
var s1 = "some text";
s1.color = "red";
console.log(s1.color); //undefined
我们知道,基本类型不是对象,因而从逻辑上它不应该有方法(尽管如我们所愿,它有方法)。其实,为了让我们能用方法操作基本类型值,后台已经自动完成了一系列的处理(注意后面是原理):当第一段代码的第二行(var s2 = s1.subString(2); )访问s1时,访问过程处于一种读取模式,也就是要从内存中读取这个字符串的值。而在读取模式中访问字符串时,后台都会自动完成下列处理:
(1)创建String类型的一个实例;
(2)在实例上调用指定的方法;
(3)销毁这个实例。
以上三个步骤用代码表示就是:
var s1 = new String("some txt");
var s2 = s1.subString(2); //把结果放回给另一个变量
s1 = null; //销毁这个对象
上面的步骤也适用于Boolean,Number类型。这也就解释了为什么不能给基本类型值添加属性和方法,因为基本类型值对象在调用完方法后就会被后台自动销毁。
引用类型和基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中(离开了作用域后可能就会被标记,然后被垃圾收集机制回收)。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁(亲测手动用new操作符创建的基本类型对象,可以添加属性和方法。)所以我们无法为基本类型值添加属性和方法。
当然如果没有必要的话,就不要显示地调用Boolean和Number、String来创建基本包装类型对象(不要var s1 = new String("some txt")这样写)。因为这样容易让人分不清自己是在处理基本类型还是引用类型的值。
Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。例如:
var obj = new Object("some text");
console.log(obj instanceof String); //true
console.log(obj instanceof Object); //true
就是把字符串传给Object构造函数,就会创建String的实例,传入数值参数就会得到Number实例,传入布尔值参数就会得到Boolean实例,但是你检测它是以上基本类型值会返回true,检测是不是Object也会返回true。
下面会讲每个基本包装类型提供的操作相应值的便捷方法。
<h3>5.6.1Boolean类型</h3>
这节的重点是理解基本类型的布尔值与Boolean对象之间的区别。当然,作者的建议是永远不要使用Boolean对象,这节就当冷知识吧。
创建Boolean对象可以像下面这样调用Boolean构造函数并传入true或false值。
var booleanObject = new Boolean(false)
Boolean类型的实例重写了的valueOf( )方法会返回基本类型的true和false,重写了的toString( )会返回字符串型的“true”和“false”。
书中前面提到过,但是当时不懂有什么用就没记的一段话:布尔表达式中的所有对象都会被转换为true。例子:
var falseObject = new Boolean(false);
var result = falseObject && true;
console.log(result); //true
在这个例子中我们用false值创建了Boolean对象的实例falseObject,并对这个对象与true进行&&运算,却发现返回到结果是true。因为,示例中的代码是对falseObject而不是对它的值(false)求值。而布尔表达式中的所有对象都会被转换为true,结果true&&true当然就等于true了。
<h3>5.6.2Number类型</h3>
Number类型也重写了valueOf()、toLocaleString()、toString()。valueOf()返回这个对象表示的基本类型的数值(亲测就是一个数)。toLocaleString()、toString()返回字符串类型的数值。
Number的toString()方法可以传递一个表示基数的参数,参数是几,就按几进制的数字的字符串形式返回。
var num = 10;
console.log(num.toString(2)) //"1010"
注意返回的是字符串形式的。
上面的几个是继承的方法,Number类型提供了一些可以将数值格式化的方法。
- toFixed():按照指定的小数位返回数字的字符串表示,且支持四舍五入。比如25.005用toFixed(2),会变成“25.01”。
- toExponential(():返回以指数表示法表示的数值的字符串形式。接收的参数也是指定输出结果中的小数位数。
var num = 10.005;
console.log(num.toFixed(2)) //"10.01"
var num = 10;
console.log(num.toExponential(1))//"1.0e+1"
<h3>5.6.3String类型</h3>
String对象继承的valueOf()、toLocaleString()、toString()返回的都是这个对象表示的基本字符串值。
String类型的每个实例都有一个length属性,表示字符串中有多少个字符(亲测中文也是有多少个字长度就是几)。书中说了,即使字符串中包含双字节字符(不是占一个字节的ASCII字符,每个字符也仍然算一个字符)。
String类型提供的方法很多,看到都怕。
<h4>1.字符方法</h4>
两个用于访问字符串中特定位置字符的方法:charAt( )和charCodeAt( )。两个方法都接收一个参数,即基于0的字符位置。两个方法的区别是charAt( )返回的是给定位置的那个字符。charCodeAt( )返回的是那个字符的字符编码。例子:
var stringValue = "hello World";
console.log(stringValue.charAt(1)); // "e"
console.log(stringValue.charCodeAt(1)); // "101" 是小写字母e的字符编码
ECMAScript5定义了另一个访问个别字符的方法,可以用方括号加数字索引来访问字符串中的特定字符。例子:
var stringValue = "hello World";
console.log(stringValue[1]); //"e"
类似于把字符串当成一个“数组”,按位置取出所在位置的字符。亲测可以直接在字符串后面加方括号,例子:
console.log("sdsdsdsd"[2]); //"s" 66666
支持这个语法的浏览器有IE8,Firefox,Safari,Opera 和Chrome所有版本的支持。冷知识:在IE7及更早版本使用这种语法会返回undefined值,尽管不是特殊的undefined值(不是很懂最后一句的意思,我以为返回的那个undefined是字符串型的,但是不是)
<h4>2.字符串操作方法</h4>
操作字符串的方法有几个,第一个是concat( ),可以将一或多个字符串拼接起来,参数就是要添加进去的字符串,参数数量不限。返回得到的新字符串。数组类型也有这个方法,不解释。不过在实践中使用最多的是加号操作符(+)(MDZZ)。
ECMAScript还提供了三个基于子字符串创建新字符串的方法:slice()、substr()、substring()。三个方法都返回被操作字符串的一个子字符串。三个方法的第一个参数都是指定子字符串的开始位置。slice()和substring()的第二个参数指定的是子字符最后一个字符后面的位置(忽略结尾那个位置)。而substr() 的第二个参数指定的则是返回字符的个数。如果没有第二个参数,则默认将字符串的长度作为结束的位置。(太乱实际用再百度)。与concat()方法一样,这三个方法也不会修改本身的那个字符串。对原始字符串无任何影响。例子:
var stringValue = "Hello World";
console.log(stringValue.slice(3)); //"lo world"
console.log(stringValue.substring(3)); //"lo World"
console.log(stringValue.substr(3)); //"lo World"
console.log(stringValue.slice(3,7)); //"lo W"
console.log(stringValue.substring(3,7)); //"lo W"
console.log(stringValue.substr(3,7)); //"lo Worl" substr()第二个参数表示要截取的字符个数
看起来三个方法作用很容易理解,但是传递给这些方法的参数是负数的情况下,他们的行为就不尽相同了。其中,slice()会将传入的负值与字符串的长度相加,substr()将负的第一个参数加上字符串的长度,而将第二个负的参数转换为0.最后,substring()方法会将所有负值参数都转换为0.很恐怖,不想举例子了,实践中没事不要用负数就行。需要再看书。
<h4>3.字符串位置方法</h4>
有两个可以从字符串中查找子字符串的方法:indexOf( )和lastIndexOf( )。这两个方法都是从一个字符串中搜索给定的子字符串,然后返回子字符串的位置。查找不到就返回-1.两个方法的区别是一个从头向后查询,一个从后向前查询。例子:
var stringValue = "hello world";
console.log(stringValue.indexOf("o")); //4
console.log(stringValue.lastIndexOf("o")) //7
这两个方法都接收第二个参数,表示从字符串哪个位置开始搜索。换句话说,indexOf()从该参数指定的位置向后搜索,忽略之前的所有字符;而lastIndexOf()则会从指定的位置向前搜索,忽略该位置之后的所有字符(为什么在讲第二次的时候才讲这么清楚)。例子:
var stringValue = "hello world";
console.log(stringValue.indexOf("o",6)); //7
console.log(stringValue.lastIndexOf("o",6))//4
indexOf( )从位置6(字母“w”)开始向后搜索,在位置7找到“o”。lastIndexOf( )从位置6开始向前搜索,找到的是“hello”中的“o”,所以返回4。
我们可以在使用第二个参数的情况下,通过循环把所有匹配的子字符串找出来,例子:
var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");
while(pos>-1){
positions.push(pos);
pos = stringValue.indexOf("e",pos+1);
}
console.log(positions); //[3, 24, 32, 35, 52]
<h4>4.trim()方法</h4>
ECMAScript5定义了trim()方法,该方法会创建一个字符串副本,删除前置和后置的所有空格,然后返回结果。
支持这个方法的浏览器有:IE9+,Firefox 3.5+,Safari 5+,Opera 10.5+和Chrome。此外,Firefox 3.5+,Safari 5+和Chrome 8+还支持非标准的trimLeft( )和trimRight( ) ,分别用于删除字符串开头和末尾的空格。
<h4>5.字符串大小写转换方法</h4>
toLowerCase()、toLocaleLowerCase()、toUpperCase()和toLocaleUpperCase()。为什么会有toLocaleLowerCase()、toLocaleUpperCase()这两个方法是因为有些地区比如土耳其语回味iUnicode大小写转换应用特殊的规则也是醉了,所以用这两个针对地区的方法来保证正确的转换。
<h4>6.字符串的模式匹配方法</h4>
正则看得头大
<h4>7.localCompare方法</h4>
tolocalCompare()方法就是用字符串跟方法的字符串参数比较,如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1),如果字符串等于字符串参数,则返回0;如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是1)。
而且localCompare()方法有个与众不同的地方,大小写是否区分要视使用方法的地区不同而定。比如美国以英语作为ECMAScript实现的标准语言,因此localCompare()就是区分大小写的,则大写字母在字母表中会排在小写字母前面。所以是否区分大小写要根据地区而定。(亲测中国地区区分大小写且大写字母在小写字母之前)
<h4>8.fromCharCode()方法</h4>
String构造函数(是构造函数的方法不是普通字符串的方法)本身还有一个静态方法:fromCharCode( )。这个方法可以接收一或多个字符编码,将他们转换为字符串。例子:
<h4>9.HTML方法</h4>
作者建议尽量不用这些方法,因为他们创建的标记通常无法表达语义(不懂)。方法有12个,需要再查书吧。
<h3>5.7单体内置对象</h3>
ECMA-262对内置对象的定义是:“由ECMAScript实现提供的,不依赖宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了,所以开发人员不用显示地实例化内置对象,因为他们已经实例化了。”前面介绍的Object、Array和String等都是内置对象。ECMA-262还定义了两个单体内置对象:Global和Math。
<h4>5.7.1Global对象</h4>
Global(全局)对象是ECMAScript中最特别的一个对象,因为你不管从什么角度上看,这个对象都是不存在的(???)。ECMAScript中的Global对象在某种意义上是作为一个“兜底儿对象”。换句话说,不属于任何对象的属性和方法,都是global对象的属性和方法。事实上,没有全局变量和全局函数,因为所有在全局作用域中定义的属性和函数,都是Global对象的属性(只是属性,函数也是Global对象的属性)。本书前面介绍过的那些函数,比如isNan(),isFinite()、parseInt()以及parseFloat(),都是Global对象的方法(array,object等类型对象的方法当然不是Global对象的,是array、object等这些对象的,不要混淆)。除此之外,Global还有其他一些方法。下面介绍这几个方法:
<h4>1.URI编码方法</h4>
Global对象的encodeURI()和encodeURIComponent()方法可以对URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。
有效的URI不能包含某些字符,例如空格,而这两个URI编码方法就会对URI进行编码,用特殊的UTF-8字符替换所有无效的字符,让浏览器能够接受和理解。encodeURI()和encodeURIComponent()的第一个区别是,encodeURI()只会把URI中无效的字符替换掉,encodeURIComponent()会把正确的特殊符号例如冒号、正斜杠、问号和井号也给替换掉。
第二个区别是,encodeURI()用于整个URI,而encodeURIComponent()用于对URI中的某一段进行编码。例子:
var uri = "http://www.wrow.com/illegal value.html#start";
console.log(encodeURI(uri)); //http://www.wrow.com/illegal%20%20value.html#start
console.log(encodeURIComponent(uri)); //http%3A%2F%2Fwww.wrow.com%2Fillegal%20%20value.html%23start
第二个方法中连http://
都被转换了字符导致无法访问,说明了一、encodeURIComponent()只适合编码部分URI;二、encodeURIComponent()会把一切正确,不正确的特殊符号都进行UTF-8字符编码。
有编码的函数,就有解码的函数,与这两个函数对应的函数是decodeURI()和decodeURIComponent()。其中,decodeURI()只能对非法字符进行解码,decodeURIComponent()可以对所有被编译的符号进行解码。例如,decodeURI()可以将%20替换成空格,但不能对%23做任何处理,因为%23表示井字号(#),而井字号是合法符号。但是decodeURIComponent()就%20和%23都可以解码。
<h4>2.eval()方法</h4>
最后一个Global对象的方法,大概是整个ECMAScript语言中最强大的一个方法:eval()(见仁见智,有些人觉得这个方法弊端多多)。eval()就像一个完整的ECMAScript解析器,它只接收一个参数,即要执行的ECMAScript(或JavaScript)字符串。例子:
eval("alert('hi')");
以上代码等价于下面这行(so what??):
alert('hi');
当解析器发现代码中调用eval()方法时,它会将传入的参数当作实际的ECMAScript语句来解析,然后把执行结果插入到原来位置。通过eval()方法执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。这意味着通过eval()执行的代码可以引用在包含环境中定义的变量。例子:
var msg = "hello world";
eval("alert(msg)");
可见,变量msg是在eval()调用的环境之外定义的,但其中调用的alert()仍然能够显示“hello world”。这是因为上面第二行代码最终被替换成了一行真正的代码(so?那干嘛要加个eval(),直接写进执行环境不就好了吗)。同样地,我们也可以在eval()调用中定义一个函数,然后在外部代码中引用这个函数:
eval("function sayHi(){ alert('Hi'); }");
sayHi();
显然,函数sayHi()是在eval()内部定义的。但由于对eval()的调用最终会被替换成定义函数的实际代码,因此可以在下一行的外部代码中调用sayHi( )。
在eval()中创建的仍和变量或函数都不会被提升,原理:因为在解析代码的时候,他们被包含在一个字符串中,它们只在eval()执行的时候创建。
严格模式下,在外部访问不到eval()中创建的任何变量或函数,因此前面的那个例子就会导致错误。在严格模式下,为eval赋值也会导致错误:
"use strict";
eval = "hi"; //causes error
<h4>3.Global对象的属性</h4>
前面说过了,没有明确对象的属性,最后都是Global的属性,例如,特殊的值undefined、NaN以及Infinity等。此外,所有原生引用类型的构造函数,像Object,Function也都是Global对象的属性。书中列出了所有Global对象的所有属性。在P133.
ECMAScript 5明确禁止给undefined、NaN和Infinity赋值,这样做即使在非严格模式下也会导致错误。
<h4>4.window对象</h4>
ECMAScript没有明确告诉我们如何直接访问Global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的(在浏览器中,Global对象是window对象的一部分,注意范围:浏览器中,window对象的一部分。)例子:
var color = "red";
function sayColor(){
alert(window.color);
}
window.sayColor(); //"red"
在这里我们定义了一个名为color的全局变量和一个sayColor()全局函数。在sayColor()内部,我们用window.color来访问变量,前面我们说过,全局环境中定义的变量和函数,实际上都是Global的属性,所以window.color等价于Global.color。以此说明,全局变量是window对象的属性。然后,又使用window.color来直接通过window.sayColor( )直接通过window对象调用这个函数。
✎JavaScript中的window对象除了扮演ECMAScript规定的Global对象的角色外,还承担了很多任务,所以说Global对象这是window对象的一部分。第8章在讨论浏览器对象模型时将详细介绍window对象。
另一种取得Global对象的方法是使用以下代码:
var global = function(){
return this; // 亲测return出来的是 window
}();
这是个立即调用的函数表达式,返回this的值。如前所述,在没有给函数明确指定this值的情况下,this值等于Global对象。而像这样通过简单地返回this来取得Global对象,在任何执行环境下都是可行的。(因为是立即执行函数的原因?)
<h4>5.7.2Math对象</h4>
不想写了这个,就是Math下的各种方法。可以直接在书中查阅。