第五章:引用类型
本章内容:
- 使用对象
- 创建并操作数组
- 理解基本的JavaScript类型
- 使用基本类型和基本包装类型
引用类型的值是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组合在一起。
对象是某个引用类型的实例。新对象是使用new操作符后跟一个构造函数创建的。构造函数本身是一个函数,只不过该函数是为了创建新对象的目的而定义的。
var person = new Object();
这行代码创建了Object引用类型的一个新实例,然后把该实例保存在了变量person中。使用的构造函数是Object,它只为新对象创建了属性与方法。
5.1 Object类型
Object类型是所有引用类型的父类。
创建Object类型有两种方式:
// 1.使用new操作符后跟Object构造函数
var person = new Object();
person.name = 'zhangzhuo';
person.age = 18;
// 2.使用对象字面量
var person = {
name: 'zhangzhuo',
age: 18
};
访问对象属性有两种方式:
// 用上面的例子
// 第一种`.`
alert(person.name);
// 第二种使用`[]`
alert(person['name']);
使用方括号的优点是可以用变量来访问
var propertyName = 'name';
alert(person[propertyName])
5.2 Array类型
数组是值得有序集合。每个值可以叫做一个元素。每个元素在数组中有一个位置,称之为索引。Javascript数组是无类型的,即每个元素可以是不同类型。
每个数组都有一个length属性,表示当前数组的长度。
数组继承来自Array.prototype的属性,
创建数组
// 第一种 new
var colors = new Array();
// 第二种 字面量
var colors = ['red','yellow'];
5.2.1 检测数组
// 方法一
if(value instanceof Array){
// do something
}
instanceof的问题在网页中如果包含多个框架的话就会出现问题。
// 方法二
if(Array.isArray(value)){
// do some thing
}
isArray实现代码如下:
var isArray = Function.isArray || function(o){
return typeof o == 'object' &&
Object.prototype.toString.call(o) === '[object Array]';
}
5.2.3 栈方法
栈是一种LIFO(Last-In-First-Out 后进先出)的数据格式,用Array可以模仿栈的操作。
// 数组模拟栈操作
var colors = new Array();
colors.push('yellow'); // ['yellow']
colors.push('orange'); //['yellow','orange'];
var item = colosr.pop(); // item=>'orange' ['yellow']
函数名 | 说明 | 返回值 |
---|---|---|
push | 数组元素的尾部添加一个或多个元素 | 数组长度 |
pop | 删除最后一个元素 | 删除的值 |
5.2.4 队列方法
队列是一种FIFO(First-In-First-Out 先进先出)的数据格式,队列在列表末端添加元素,在列表前端删除项。
可以使用push和shift或者unshift和pop
// 数组模拟栈操作
var colors = new Array();
colors.push('yellow'); // ['yellow']
colors.push('orange'); //['yellow','orange'];
var item = colors.shift(); // item=>'yellow' ['orange']
函数名 | 说明 | 返回值 |
---|---|---|
shift | 删除第一个元素 | 删除的值 |
unshift | 元素的首端添加一个或多个元素 | 数组长度 |
5.2.6 操作方法
concat()
Array.concat方法传递并返回一个新数组,它的元素包括调用concat()的原始数组和concat的每个参数。如果参数中任何一个自身是数组,则链接数组的元素,并非数组的本身。但要注意,concat不会递归数组的数组。
var a = [1,2,3];
a.concat(4,5); // [1,2,3,4,5]
a.concat([4,5]); // [1,2,3,4,5]
a.concat(4,[5,[6,7]]); // [1,2,3,4,5,[6,7]]
slice()
Array.slice()返回制定数组的一个片段或子数组,它的两个参数分别指定了起始于终止的位置。如果只有一个参数,则从起始位置到数组末端。
var a = [1,2,3,4,5];
a.slice(0,3); // [1,2,3]
a.slice(1); //[2,3,4,5]
slice方法可以让类数组转换为真正数组 Array.prototype.slice.call(xx);
splice()
Array.splice()方法是在数组中插入和删除元素的通用方法,不同slice和concat会返回新数组。而splice会修改调用的数组。
- 删除: 两个参数, 要删除第一项的位置,和删除的项数。 eg: slice(0,2)
- 插入: 三个参数,起始位置,0(需要删除的项数),要添加的项。如果要插入多个,则可传递4,5,6
- 替换: 三个参数,起始位置,删除的项数,要添加的项。(可以理解为 删除+插入)
splice始终会返回一个数组,包含从原数组删除的项。
// demo1
var colors = ['red','orange','blue'];
var removed = colors.splice(0,2); //删除前两项
alert(removed); //['red','orange']
alert(colors); ['blue']
// demo2
var colors = ['red','orange','blue'];
var removed = colors.splice(0,0,'pink','yellow'); // 首位添加pink
alert(removed); //[]
alert(colors); //['pink','yellow','red','orange','blue'];
// demo3
var colors = ['red','orange','blue'];
var removed = colors.splice(0,1,'pink'); // 更换第一位置的red => pink
alert(removed); //['red']
alert(colors); //['pink''orange','blue'];
5.2.6 迭代方法
ECMAScript 5为数组定义了5个迭代方法。每个方法都接受两个参数:要在每一项上运行的函数和(可选)运行该函数的作用域对象--影响this值。
传入这些方法中的函数会接受三个参数:数组项的值,该项在数组的位置,数组本身。
- every(): 如果每一项都返回true, 则返回true;
- some(): 如果有一项返回true,则为true;
- filter(): 返回该函数会返回true的项所组成的数组;
- forEach(); 迭代循环像for,没有返回值;
- map(): 返回每次调用的结果组成的数组
5.2.6 归并方法
ECMAScript 5为数组定义了2个归并方法reduce和reduceRight。
迭代所有的项,然后构建一个最终的返回值。
接受两个参数,要在每一项执行的函数(可选)作为归并的初始值。传入函数接受四个参数,前一个值,当前值,项的索引和数组本身。
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur,index,array){return prev+cur});
alert(sum); //10
类数组
能通过Array.prototype.slice转换为真正的数组的带有length属性的对象。
类数组拥有以下特征:
- 具有length属性
- 可以被遍历
这种对象有很多,比较特别的是arguments对象,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。
我们可以通过Array.prototype.slice.call(fakeArray)将伪数组转变为真正的Array对象。
来看个示例
var fakeArray01 = {0:'a',1:'b',length:2};//这是一个标准的有伪数组对象
var arr01 = Array.prototype.slice.call(fakeArray01);
alert(arr01[0]);//a
var arr02 = [].slice.call(fakeArray01);
alert(arr02[0]);//a
类数组的实现
我们来看一些特殊的用例:
var fakeArray01 = {a:'a',b:'b',length:2};//没有length下标对应的值
var arr01 = Array.prototype.slice.call(fakeArray01); // [empty * 2]
alert(arr01[0]);//undefined
var fakeArray02 = {0:'a',1:'b',length:'num'};//length不是数值
var arr02 = Array.prototype.slice.call(fakeArray02); []
alert(arr02[1]);//undefined
同样fakeArray01和fakeArray02被转换成了真正的数组,但是数组中的值都为undefined
查看 V8 引擎[的源码,可以将 slice 的内部实现简化为:
function ArraySlice(start, end) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
var len = TO_UINT32(this.length);
var start_i = TO_INTEGER(start);
var end_i = len;
if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);//如果没传入end,end=length,即slice第二个参数可选。
if (start_i < 0) {
start_i += len;//参数1的A分支 处理负值,+= length。如:为-1,则start从倒数第一个开始,负值绝对值小于length
if (start_i < 0) start_i = 0;//参数1的A.a分支 若仍未负值,则等于0。 即处理负值绝对值大于length [1,2,3].slice(-4)。
} else {
if (start_i > len) start_i = len;//参数1的B分支 参数大于length,则等于length,处理 [1,2,3].slice(5),返回[]
}
if (end_i < 0) {
end_i += len;//参数2的A分支 处理负值,+= length。如:为-1,则start从倒数第一个开始,负值绝对值小于length
if (end_i < 0) end_i = 0;//参数2的A.a分支 若仍未负值,则等于0。 即处理负值绝对值大于length [1,2,3].slice(1,-4)。
} else {
if (end_i > len) end_i = len;//参数2的B分支 参数大于length,则等于length,处理 [1,2,3].slice(1,5) == [1,2,3].slice(1) ==
}
//最终返回结果的值。可以看到这里会返回一个新的真正的数组(ps:slice的好基友splice是修改原数组的。)
var result = [];
// 处理分支1 如果经历了上面代码的层层检查设置,结束值小于开始值,那么直接返回空数组,处理 [1,2,3].slice(2,1)
if (end_i < start_i) return result;
// 处理分支2 如果是数组 && !%IsObserved(this) && 结束大于1000 && %EstimateNumberOfElements(this) < 结束值 ,那么使用方法SmartSlice来处理
if (IS_ARRAY(this) &&
!%IsObserved(this) &&
(end_i > 1000) &&
(%EstimateNumberOfElements(this) < end_i)) {
SmartSlice(this, start_i, end_i - start_i, len, result);
} else {
// 处理分支2 调用SimpleSlice 处理。
SimpleSlice(this, start_i, end_i - start_i, len, result);
}
//设置length,似乎多余?还是v8中的数组[] 需指定length。 此处待探寻。。。
result.length = end_i - start_i;
return result;
}
/*
* ......
*/
// Set up non-enumerable functions of the Array.prototype object and
// set their names.
// Manipulate the length of some of the functions to meet
// expectations set by ECMA-262 or Mozilla.
InstallFunctions($Array.prototype, DONT_ENUM, $Array(
//......
"slice", getFunction("slice", ArraySlice, 2)
//......
));
可以看出,slice 并不需要 this 为 array 类型,只需要有 length 属性即可。并且 length 属性可以不为 number 类型,当不能转换为数值时,ToUnit32(this.length) 返回 0.
根据以上结论可以得出:fakeArray01被转换成了lenth为2的数组,其值都被初始化为undefined,fakeArray02被转换成了length为0的数组,自然访问下标为1的元素返回undefined
为什么使用类数组?
因为纯数组不能增加额外的属性。 arguments对象还有个callee属性。
5.4 RegExp 类型
ECMAScript通过RegExp类型来支持正则表达式。可以使用以下两种方式创建正则表达式:
var pattern1 = new RegExp(); // 用构造函数
var expression = / pattern / flags; // 使用字面量
其中的模式(pattern)部分可以是简单或复杂的正则表达式,可以包含字符类、限定类、分组、向前查找以及反向引用。每个表达式可以带一个或多个标志(flags),用于表达正则表达式的行为。正则表达式的匹配模式支持下列3个标志(也可称为修饰符
):
- g: 表示全局模式(global)模式,即模式应用于所有的字符串;
- i: 表示不区分大小写(case-insensitive)模式, 即在确定匹配时忽略字符串的大小写;
- m: 表示多行模式,即在达到一行末尾时还会匹配下一行;
字符类
将直接量字符单独放进方括号内就组成了字符类(character class)。 一个字符类可以匹配它所包含的任意字符。因此/[abc]/
就和'a'、'b'、'c'任意一个匹配。
/[abc]/.test('a'); // true
/[abc]/.test('123'); // false
另外可以通过 ^
符号作为否定字符类,将^
作为左方括号的第一个字符。
/[^abc]/.test('a'); // false
字符 | 匹配 |
---|---|
[...] | 方括号任意字符 |
[^...] | 不在方括号任意字符 |
. | 除了换行符和Unicode行终止符以外的字符 |
\w | 任何ASCII字符组成的单词,等价于[a-zA-Z0-9] |
\W | 任何不是ASCII字符组成的单词, 等价于上面的非 |
\s | 任何Unicode空白符 |
\S | 任何非Unicode空白符的字符 |
\d | 任意ASCII数字,等价于[0-9] |
\D | 除了ASCII数字以外的字符 |
pattern = /at/g; //匹配'at'
pattern = /[bc]at/i; // 匹配第一个'bat'或'cat'
重复类
定义指定标识能重复出现的标志。
字符 | 含义 |
---|---|
{n,m} | 匹配前一项至少n次,至多m次 |
{n,} | 匹配前一项至少n次或者更多 |
{n} | 匹配前一项n次 |
? | 匹配前一项0次或者1次,也就说是可选的,等价于{0,1} |
+ | 匹配前一项1次或者多次,等价于{1,} |
* | 匹配前一项0次或多次, 等价于{0,} |
pattern = /\d{2,4}/; //匹配2-4个数字
pattern = /\w{3}/; //匹配3个字母
pattern = /\s+java\s+/; //匹配前后带有一个或多个的空格符的字符串java
pattern = /[^(]*/; //匹配多个非括号的字符
选择、分组和引用
字符 | 含义 |
---|---|
| | 选择该字符左边的表达式或者右边的表达式(有短路特性) |
(...) | 组合, 将几个项组合为一个单元,这个单元可供‘*’、‘+’、'?'、‘|’等符号加以修饰,而且可以记住这个组合相匹配的字符串以供此后引用使用 |
(?:...) | 只组合,把项组合到一个单元,但不记忆与该组合匹配的字符串 |
\n | 和第n个分组第一次匹配的字符相匹配,组是圆括号的子表达式(也有可能是嵌套的),组索引是从左向右的左括号数,(?::形式不编码 |
指定匹配位置
将模式定位在搜索字符串的特定位置上
字符 | 含义 |
---|---|
^ | 匹配字符串开头,在多行检索中,匹配一行的开头 |
$ | 匹配字符串的结尾,在多行检索中,匹配一行的结尾 |
pattern = /^Javascript$/; //匹配单词'Javascript'
用于模式匹配的string方法
string 支持四种正则表达式的方法:
search(pattern)
'Javascript'.search(/java/i); // 0
replace(pattern,repleaceStr)
进行检索与替换。参数第一个为正则表达式,第二个为要替换的字符串。
// 将不区别大小写的javascript替换为正确的JavaScript
'javascript&Javascript'.replace(/javascript/gi,'JavaScript'); //JavaScript&JavaScript
/*
* replace功能还不止这样。
* 正则表达式圆括号起来的子表达式是带有从左向右的索引编号的,而且表达式会记忆与每个子表达式匹配的文本。
* 如果替换文字是$加数字,那么replace将用于指定的子表达式相匹配的字符串来替换着两个字符
*/
// demo: 将英文引号替换中文引号
'"hello wolrd"'.replace(/"([^"]*)"/g,' “$1” '); // “ hello wolrd ”
mattch(pattern)
唯一的参数是正则表达式,返回的是由匹配结果组成的数组
"1 plus 2 equals 3".match(/\d+/g); // ["1", "2", "3"]
split(pattern|str)
拆分方法,将字符串以一种分隔符拆分为数组, 参数是分隔符或者正则,返回拆分后的数组
"1,2,3,".split(','); //['1','2','3']
"1, 2, 3".split(/\s*,\s*/); //['1','2','3']
RegExp实例方法
exec(str)
RegExp对象的主要方法是exec()
,该方法主要用于捕获组而设计的。exec()接受一个参数,即应用模式的字符串组。然后返回包含第一个匹配信息的数组。如果没有匹配则返回null。返回的数组虽然是Array的实例,但包含了两个额外的属性:index,和input。其中index表示匹配项在字符串中的位置。input表示应用正则表达式的字符串。在数组中,第一项是整个模式匹配的字符串,其他项是与模式中捕获组匹配的字符串(如果没有捕获组,则该数组只有一项)
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); //0
alert(matches.input); //mom and dad and baby
alert(matches[0]); //mom and dad and baby
alert(matches[1]); // and dad and baby
alert(matches[2]); // and baby
test(str)
如果只想知道是否匹配,可以用test。接受一个字符串参数,匹配返回true。
var text = '000-00-0000';
var pattern = /\d{3}-\d{2}-\d{4}/ig;
if(pattern.test(text)){
alert("this pattern was matched")
}
5.5 Function类型
函数实际上是对象。每个函数都是Function的一个实例,因此函数名是指向函数对象的指针,不会与某个函数绑定。
// 函数声明语法
function sum(num1, num2){
return num1 + num2;
}
// 函数表达式
var sum = function(num1, num2){
return num1 + num2;
}
这两种方式其实没有太大区别,只是在环境变量创建时候,函数声明语法会被解析器早一些被读取。
由于一个函数名仅仅是指向函数的指针,因此函数名与包含对象的指针没有什么区别。一个函数可以有多个函数名。
function sum(num1, num2){
return num1 + num2;
}
alert(sum(1,2)); //3
var anotherSum = sum;
sum = null;
alert(anotherSum(2,2)); //4
5.5.1 没有重载(深入理解)
先看一个例子:
function addSomeNumber(num){
return num + 10;
}
function addSomeNumber(num){
return num + 20;
}
alert(addSomeNumber(10)); //30
其实代码与下面的相同
var addSomeNumber = function(num){
return num + 10;
}
addSomeNumber = function addSomeNumber(num){
return num + 20;
}
alert(addSomeNumber(10)); //30
创建第二个函数的时候,实际上覆盖了引用第一个函数的变量addSomeNumber;即无法实现重载
5.5.2 函数声明与函数与表达式
解析器在执行环境中加载数据的时候(创建阶段),对函数声明与函数表达式并非一视同仁。解析器会率先读取函数声明,使其在执行任何代码之前可以使用,函数表达式要在解析器执行到它所在的代码行,才会真正的被解析执行。(关于执行具体顺序可以看我第四章中的变量对象创建过程
)
alert(sum(10,10)) // 20
alert(otherSum(20,20)); // error otherSum is not a function
function sum(num1,num2){
return num1 + num2;
}
var otherSum = function(num1,num2){
return num1 + num2;
}
在变量对象创建过程函数表达式被提前,变量提升,实际解析器等价于这样。
function sum(num1, num2){
return num1 + num2;
}
var otherSum;
alert(sum(10,10)) // 20
alert(otherSum(20,20)); // error otherSum is not a function
otherSum = function(num1,num2){
return num1 + num2;
}
作为值的函数
在ECMASscript中函数名本身就是变量,所以函数可以作为值来使用。也就是说,可以向传递参数一样把一个函数传递到另一个函数,还可以在将函数作为另一个函数的结果返回。
将函数作为参数传递
function callOhterFunction(someFunction, someArguments){
return someFunction(someArguments);
}
function add10(num){
return num + 10
}
var result1 = callOhterFunction(add10, 10);
alert(result1); //20
function getGretting(name){
return 'hello '+name;
}
var result2 = callOhterFunction(getGretting, 'zz');
alert(result2); //hello zz
将函数中在返回另一个函数
function createComparisonFunction(propertypeName){
return function(object1,object2){
var value1 = object1[propertypeName];
var value2 = object2[propertypeName];
if(value1 < value2){
return -1;
} else if(value1 > value2){
return 1;
} else {
return 0;
}
}
}
var data = [{name:'Zachary', age:28},{name:'Nicholas', age:29}];
data.sort(createComparisonFunction('name'));
alert(data[0].name); //Nicholas
data.sort(createComparisonFunction('age'));
alert(data[0].name); //Zachary
5.5.3 函数内部属性
在函数内部有两个特殊的对象: arguments
、this
和es5中的caller
arguments
arguments是个类数组对象,包含了传递的所有参数。虽然arguments的主要用途是保存参数,但这个对象还有一个名为callee的属性,该属性是一个指针用来指向拥有arguments对象的函数(正在执行的函数)。
// 经典的阶乘函数
function factorial(num){
if(num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
factorial(5); //120
这会产生一个问题:函数体内部与函数名高度耦合。
var otherFactorial = factorial;
factorial = null;
otherFactorial(5); // error: factorial is not a function
这里使用callee属性改写(这样就可以解除耦合关系)
// 使用callee改写
function factorial(num){
if(num <= 1){
return 1;
} else {
return num * arguments.callee(num -1);
}
}
factorial(5); //120
this
可以理解this引用的函数执行的环境对象。(后面第七章会详细讲解this)
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
alert(this.color);
}
sayColor(); //red
o.sayColor = sayColor;
o.sayColor(); ;//blue
请记住:函数的名字仅仅是一个包含指针的变量而已。因此,即使在不同环境中执行,全局的sayColor()函数与o.sayColor()指向的仍是同一个函数。
caller
这个属性保存着调用这个当前函数的函数的引用。如果在全局作用域下调用当前函数,它的值为null
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer(); // 打印出outer的源代码
inner(); // null (全局函数中调用,返回null)
关于callee和caller
在严格模式下,为了加强安全性,这两个属性的读写操作都会产生类型错误,不然第三方的代码就能相同的环境中窥视其他的代码。
arguments.callee 与 caller(function)。
5.5.5 函数的属性和方法
每个函数都包含的属性:length
和 prototype
。
length
代表希望接收的命名参数的个数。(注意:arguments.length指的是实际接收的参数个数)
function sayName(name){
alert(name);
}
function sum(num1, num2){
return num1 + num2;
}
function sayHi(){
alert('hello')
}
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0
prototype
:指向一个对象的引用,这个对象称为原型对象
,继承公共方法,都是依靠此属性(在第六章会详细讲解)。
每个函数都包含了两个非继承的方法:apply()
和call()
。
这两个方法的用途都是在特定作用域下调用函数,实际上是设置了函数体内this对象的值。
apply()方法接受两个参数, 一个是运行函数的作用域,另一个是参数数组,也可以是Array的实例,也可以是arguments对象。
call()方法接受两个参数,第一个是运行函数作用域,第二个是参数需要逐个列举出来。
function sum(num1,num2){
return num1 + num2;
}
function applySum1(num1, num2){
return sum.apply(this, arguments); //传递arguments对象
}
function applySum2(num1, num2){
return sum.apply(this, [num1, num2]); //传递数组
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
applySum1(10,10); //20
applySum2(10,10); //20
callSum(10,10); //20
实际上apply和call真正的勇武之地:他们真正强大的地方是可以扩充函数赖以生存的作用域。
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
使用call(apply)来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系。在前面一个例子第一个版本,我们先将函数放到对象o中o.sayColor = sayColor
然后再通过o来调用它,而在通过call后就不需要那些重复的步骤了。
es5还新增了一种方法:bind()。这个方法会创建一个函数的实例,其this会被绑定到传给bind()函数的值。
window.color = 'red';
var o = {color:'blue'};
function sayColor(){
alert(this.color);
}
var otherSayColor = sayColor.bind(o);
otherSayColor(); //blue
5.6 基本包装类型
为了方便操作基本类型值,ECMAScript提供了3中特殊的引用类型:Boolean、Number、String。
每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象。
var s1 = "some text";
var s2 = s1.substring(2);
alert(s2); // "me text"
基本类型不是对象,它应该不能有方法。其实,为了让我们实现这种直观的操作方式,后台已经自动完成了一系列处理。
当第二行读取s1时,访问过程处于一种读取模式,也就是要从内存中读取s1的值。而在读取模式中访问字符串,后台会自动完成下列操作:
- 创建String的一个实例。
- 在实例上调用指定方法。
- 销毁这个实例。
可以将上面步骤想象如下代码:
var s1 = new String('some text');
var s2 = s1.substring(2);
s1 = null;
引用类型与基本包装类型的主要区别在于对象的生存期。
使用new操作符创建引用类型的实例,在执行流离开当前作用域之前一直保存在内存中。而自动创建的自动包装类型,则只存在于一行代码的执行瞬间,然后立马销毁。所以我们无法再运行时为基本类型值添加属性与方法。
var s1 = 'some text';
s1.color = 'red';
alert(s1.color); //undefined
原因就是第二行创建String对象在第三行代码时已经销毁了。第三行代码又创建了一个自己的String对象,然而这个对象没有color属性。
5.7 单体对象
5.7.1 Global对象
URL编码方法:
Global对象的encodeURI()和encodeURIComponent()方法对URI进行编码,以便发送给浏览器。
encodeURI主要对整个URI, 而encodeURIComponent主要对URI的某一段进行编码。
主要区别有:encodeURI不会对本身属于URI的特殊字符做编码,例如冒号,正斜杠,问好,井号。 而enocdeURIComponent()则会对任何非标准字符串做编码。
var uri = "http://www.wrox.com/illegal value.html#start";
alert(encodeURI(uri)); // http://www.wrox.com/illegal%20value.html#start
alert(encodeURIComponent(uri)); // http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.html%23start
一般来说,我们使用encodeURIComponent要比encodeURI要多,因为实践中更常见的是对查询字符串参数而不是对基础uri进行编码
小结:
对象在javascript中被称为引用类型的值,而且有一些引用类型可以用来创建特定的对象。总结如下:
- 引用类型与 传统的面向对象程序中的类相识,但实现不同;
- Object类型是一个基础类型,其他所有类型都继承Object的基本行为;
- Array类型是一组值得有序列表,同时还提供了操作和转换这些值得方法;
- Date类型提供了日期和时间的信息,还有包括时间的计算方法;
- RegExp类型是ECMASscript支持正则表达式的一个接口,提供了基本的和高级的正则表达式功能;
函数实际上是Function的实例,因此函数也是对象;由于函数是对象,函数也拥有方法,可以增强其行为。
因为有基本包装类型,所以Javascrip中的基本类型值可以当做对象来访问。有三种基本包装类型:Boolean、Number、String。以下是他们的共同特征:
- 每个包装类型都映射到同名的基本类型;
- 在读取模式下访问基本类型的值,就会自动创建对应的基本包装类型的一个对象,从而方便了数据操作;
- 基本包装类型的语句一经执行完毕,就会立马销毁新创建的包装对象;
在左右代码执行之前,作用域就存在两个内置对象:Global和Math。在大多数ECMAScript实现中都不能直接访问Global对象;不过Web浏览器实现承担该角色是window
对象。全局变量和函数都是Global对象的属性。Math提供了很多关于计算的属性和方法。