javascript高级程序设计(第5章) -- 引用类型

第五章:引用类型

本章内容:

  • 使用对象
  • 创建并操作数组
  • 理解基本的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属性的对象。

类数组拥有以下特征:

  1. 具有length属性
  2. 可以被遍历

这种对象有很多,比较特别的是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 函数内部属性

在函数内部有两个特殊的对象: argumentsthis和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 函数的属性和方法

每个函数都包含的属性:lengthprototype

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的值。而在读取模式中访问字符串,后台会自动完成下列操作:

  1. 创建String的一个实例。
  2. 在实例上调用指定方法。
  3. 销毁这个实例。

可以将上面步骤想象如下代码:

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提供了很多关于计算的属性和方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容