Web开发技术JavaScript学习笔记深究JavaScript

JS学习4(引用类型)

2016-05-13  本文已影响53人  exialym

引用类型是一种数据结构,用于将数据和功能组织在一起。对象是某个特定引用类型的实例。新对象使用new操作符后面跟一个构造函数来创建,构造函数本身就是一个函数,只不过这个函数是出于创建新对象而定义的。

var person = new Object();

这里调用的构造函数是Object(),只为新对象定义了默认的属性和方法。

Object类型

这是ECMAScript中使用的最多的类型,大多数引用类型的实例都是Object类型的实例。

初始化

创建它有两种办法:

//方法1
var person = new Object();
person.name = "Nicholas";
person.age = 29;
//方法2
var person = {
    name : "Nicholas",
    age : 29
};
//这样也可以~
var person = {}; // new Object()   
person.name = "Nicholas";
person.age = 29;

在方法2中注意,属性值是字符串,如果你输入的不是字符串,那也会被转化成字符串。
要注意的是使用字面量来定义对象的时候实际上不会调用Object()构造函数哦
在需要像函数传递大量可选参数的情况下,使用对象字面量来传递是个很好的选择。

访问

有两种方法来访问:

alert(person["name"]); //"Nicholas" 
alert(person.name); //"Nicholas"

一般推荐第二种,但是第一种有特殊的用法:

var propertyName = "name"; //需要通过变量来访问属性
alert(person[propertyName]); //"Nicholas"
//属性名里包含非字母非数字等用在.方法里会出错的字符
person["first name"] = "Nicholas";

Array类型

ECMAScript中的数组也与其它语言的数组有很大区别,数组中的每一项都可以保存任何类型的数据,大小也是动态调整的。

初始化

同样的,使用数组字面量初始化数组时不会调用Array的构造函数。

var colors = new Array();
var colors = new Array(20);
var colors = new Array("red", "blue", "green");
var names = new Array("Greg");
var colors = ["red", "blue", "green"];
var names = [];

访问

使用方括号下标的方式来访问。
在别的语言中会有数组越界,在JS里,越界访问返回undefined,越界赋值则会帮你创建这个元素,如果数组原来只有3个元素,你直接设置第100个,数组长度也会变为100,前面那些没赋值的都会变成undefined,虽然这样不报错,但还是不推荐。
length属性始终会返回0或更大的值,而且有趣的是,这个属性并不是只读的,还可以设置长度,通过设置长度就可以移除不要的或添加新项。
这样的特性造成了在最后添加元素很方便:

colors[colors.length] = "black";

检测数组

ECMAScript3中使用value instanceof Array来判断一个对象是不是数组。但是当一个网页中包含多个框架时,就有可能包含多个全局执行环境,包含多个Array的构造函数,在框架之间传递数组时这样判断就会出问题。ECMAScript5为了解决这个问题创建了isArray()方法。

转换方法

alert(colors.toString()); //对数组调用toString时会对数组中的每一个元素调用toString,然后加上逗号拼成一个字符串
alert(colors.toLocaleString());//对数组调用这个方法和调用toString()方法类似,会先调用每一项的toLocaleString()方法
alert(colors.valueOf());//valueOf方法则还是返回数组
alert(colors);//直接将数组传到alert里其实是默认调用了toString再显示出来
alert(colors.join("||"));      //这个可以自定义字符串的分隔符,默认使用逗号。red||green||blue

栈方法

使数组模仿栈操作,有push()和pop()方法。push接受任意数量的参数将它们逐个添加到数组的末尾,并返回新的数组长度。pop移除最后一个元素并返回该元素。

队列方法

模仿队列操作,使用push(),shift()方法模仿。shift()方法移除第一项并返回这个元素。
还可以使用pop()和unshift()方法来模拟,unshift是在数组的最前端加上任意个项并返回新的数组长度。
这两种方法模拟队列的方向不一样,我更喜欢第一组。

重排序方法

reverse()方法会直接反转数组。
sort()方法会调用数组中每一项的toString方法,然后根据转换得来的字符串按照升序排列数组。也就是说[0,1,2,10]会被排成[0,1,10,2]。这显然不是我们想要的
sort可以接受一个参数,这个参数是一个比较函数,这个比较函数接受两个参数,如果第一个应该在第二个前面,则返回一个负数,相等返回0,否则返回正数。

function compare(value1, value2) {     
    if (value1 < value2) {         
        return -1;     
    } 
    else if (value1 > value2) {         
        return 1;     
    } 
    else {         
        return 0;     
    } 
} 
var values = [0, 1, 5, 10, 15]; 
values.sort(compare); 
alert(values);  //0,1,5,10,15  

对于数值类型或者valueOf()方法会返回数值的直接用减法代替咯:

function compare(value1, value2){     
    return value2 - value1; 
}

一些方法

concat()

var colors = ["red", "green", "blue"]; 
var colors2 = colors.concat("yellow", ["black", "brown"]);  
alert(colors);     //red,green,blue         
alert(colors2);    //red,green,blue,yellow,black,brown 

slice()

var colors = ["red", "green", "blue", "yellow", "purple"]; 
var colors2 = colors.slice(1); 
var colors3 = colors.slice(1,4);  
alert(colors2);   //green,blue,yellow,purple 
alert(colors3);   //green,blue,yellow  

splice()

var colors = ["red", "green", "blue"]; 
var removed = colors.splice(0,1);    //  删除第一项
alert(colors);     // green,blue 
alert(removed);    // red 被删除的项  

removed = colors.splice(1, 0, "yellow", "orange");   // 在1的位置插入2项  
alert(colors);     // green,yellow,orange,blue 
alert(removed);    // 空的,因为没删除什么
 
removed = colors.splice(1, 1, "red", "purple");   //  删除1项,插入两项
alert(colors);     // green,red,purple,orange,blue 
alert(removed);    // yellow 被删除的项

位置方法

indexOf和lastIndexOf方法用来查找元素在数组中的位置。接收两个参数,起始索引和要查找的元素,这两个的区别就是一个从前开始找一个从后开始找。他们只会返回他们找到的第一个元素的索引。在查找时使用===全等符号来比较。如果找不到则返回-1。

var person = { name: "Nicholas" }; var people = [{ name: "Nicholas" }];  
var morePeople = [person];  
alert(people.indexOf(person));     //-1 这里两个对象不是一个,并不全等alert(morePeople.indexOf(person)); //0 

迭代方法

在ES5中为数组定义了5个迭代方法,这些方法都接收两个参数:要在数组每一项上运行的函数和运行该函数的作用域对象(这个对象会影响函数内this的值)。
对于作为参数被传进去的函数,它会接收到3个参数:数组项的值,数组项的索引,和数组本身。返回值每个函数不一样。
every()
如果函数对每一项都返回true,则every返回true。这个一般用来检测数组的每一项是否满足某个要求。
some()
如果函数对某一项返回true,这个函数就返回true
filter()
它返回函数返回true的项组成的数组
map()
返回在数组原始项上运行传入函数的结果返回的数组
foreach()
木有返回值

归并方法

这也是ES5里的方法:reduce()和reduceRight()。这两个方法一个从前一个从后,迭代数组的所有项,最终返回一个值。他们都接受两个参数:在每一项上调用的函数和初始值(可选)。函数接收4个参数:前一个值,当前项,项的索引和数组对象。且这个函数返回的任何值都会自动的作为下一项的第一个参数。

var values = [1,2,3,4,5]; 
var sum = values.reduce(function(prev, cur, index, array){      return prev + cur;  
}); 
alert(sum); //15 

Date类型

var now = new Date(); //当前时间
var someDate = new Date(Date.parse("May 25, 2004")); //这个字符串格式没有标准,有可能有的浏览器不支持呢
var someDate = new Date("May 25, 2004"); //这个其实会自动调用parse
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55)); //这个比较保险
var start = Date.now(); //获取当前毫秒数,用来分析代码很有用

Date类型重写了toString(),toLocaleString(),valueOf()方法。前两个每个浏览器实现都不太一样,所以在真正用的时候也不太可能用这些方法将值呈现给用户。valueOf方法则是将时间转换为毫秒值,这个就比较有用了,这就意味着可以直接使用><来判断时间的先后。

var date1 = new Date(2007, 0, 1);    //"January 1, 2007" 
var date2 = new Date(2007, 1, 1);    //"February 1, 2007"  
alert(date1 < date2); //true 
alert(date1 > date2); //false 

那么如果想获得准确的时间信息并显示给用户我们应该怎么办呢
getFullYear()
getMonth()
getDate()
等等。。。
也有设置的方法
setFullYear()
setMonth()
等等

RegExp类型

正则表达式

var expression = / pattern / flags ; 

其中pattern可以是任何简单或复杂的正则表达式,flags是标志,标志可以带多个。标志有3个:g表示全局模式,即模式会被应用到所有字符串,不会在找到第一个后就停止;i表示区分大小写;m表示多行,在到达一行文本末尾时会查找下一行。

var pattern1 = /at/g; //匹配字符串中所有at的实例
var pattern2 = /[bc]at/i; //匹配第一个bat或cat,不区分大小写
var pattern2 = new RegExp("[bc]at", "i"); //这样也是可以的
var pattern3 = /.at/gi; //匹配所有以at结尾的3个字符的组合,不区分大小写

在ES3中,使用字面量创建正则表达式与使用new创建正则表达式不一样。使用字面量创建的正则表达式始终共享一个RegExp类型实例。这就有问题了:

var re = null;
var i;
for (i=0; i < 10; i++){     
    re = /cat/g;     
    re.test("catastrophe"); 
} 

在上面这段代码中每次循环都使用的是同一个RegExp实例,在第一次查找的过程中,查到了索引为3的地方。在循环到第二次的时候,因为是同一个实例,实例属性不会被重置,于是第二次循环就从第4个字符开始查找了,这样当然就找不到了。
在ES5中,修复了这个不合理的地方。字面量也是直接调用构造函数那样新建一个实例。

实例属性

global:是否设置了g
ignoreCase:是否设置了i
lastIndex:开始搜索下一个匹配项的字符位置
multiline:是否设置m
source:正则表达式的字符串表示

实例方法

exec()专为捕获组设计:

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()它接受一个字符串,在模式与该参数匹配的情况下返回true:

var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
    alert("The pattern was matched.");
}

RegExp构造函数属性

这些属性基于所执行的最近的一次正则表达式操作而变化

var text = "this has been a short summer";
var pattern = /(.)hort/g;
if (pattern.test(text)){
    alert(RegExp.input);   // this has been a short summer
    alert(RegExp.leftContext);  // this has been a
    alert(RegExp.rightContext);   // summer
    alert(RegExp.lastMatch);    // short
    alert(RegExp.lastParen);   // s
    alert(RegExp.multiline);   // false
}

还有9个用于储存捕获组的构造函数属性:RegExp.$1、...RegExp.$2

var text = "this has been a short summer";
var pattern = /(..)or(.)/g;
if (pattern.test(text)){
    alert(RegExp.$1);       //sh
    alert(RegExp.$2);       //t
}

Function类型

在ES里Function实际上是对象!所以每个函数都是Function类型的实例,与其他引用类型一样具有属性和方法,至于函数名,就是一个指向函数对象的指针。所以以下3种方法声明函数都是等价的:

//1
function sum (num1, num2) {     
    return num1 + num2; 
} 
//2
var sum = function(num1, num2){     
    return num1 + num2; 
}; 
//3
var sum = new Function("num1", "num2", "return num1 + num2");

第三种方法可以很明显的看出函数是对象的迹象,但是平时使用的时候并不推荐使用第三种办法,会导致性能问题,因为要解析字符串里的表达式。
对于函数的名字,仅仅是指向函数的指针:

//首先定义了一个名为sum的函数
function sum(num1, num2){     
    return num1 + num2; 
} 
alert(sum(10,10));        //20  
//这里不使用带圆括号的函数名,意思就是访问函数的指针而不是调用函数
//这里将sum的值赋给了anotherSum
var anotherSum = sum; 
//于是anotherSum也指向了sum函数
alert(anotherSum(10,10)); //20
//将sum指向别处  
sum = function (num1, num2){
  return num1 - num2;
};
//anotherSum还指向原来的sum函数
alert(anotherSum(10,10)); //20
//sum指向新函数
alert(sum(10,10));//0

函数声明和函数表达式

函数声明会被解析器率先获取并放到其所在执行环境的顶部,代码开始执行后随时可用。而函数表达式得在代码执行到其所在的代码行时才被解释。
就这点区别。

这个可以正确执行:

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

这个则会报错:

alert(sum(10,10)); 
var sum = function(num1, num2){     
    return num1 + num2; 
}; 

作为值的函数

函数可以作为参数传给另一个函数,也可以作为返回值。

//将一个函数作为参数,并将这个函数的返回值作为返回值
function callSomeFunction(someFunction, someArgument){              
    return someFunction(someArgument); 
} 
function add10(num){     
    return num + 10; 
} 
//调用时将函数的指针传入
var result1 = callSomeFunction(add10, 10); 
alert(result1);   //20 

直接返回一个函数:

function createComparisonFunction(m) {
  return function(o1, o2){
    return m+o1+o2;
  };
}  
var func = createComparisonFunction(1)
alert(func(2,3));
alert(createComparisonFunction(1)(11,0)); 

函数内部属性

在函数内部有两个特殊对象arguments和this。arguments的主要用途是保存参数,不过他还有一个callee的属性,它是一个指针,指向拥有这个arguments对象的函数。于是就可以这样用:

//递归阶乘,使用callee自己调用自己就不受函数名的限制了
function factorial(num){
    if (num <=1) {
        return 1;
    } else {
        return num * arguments.callee(num-1);
    } 
}

在ES5中还有caller属性,返回指向调用这个函数的函数的指针。在全局作用域中调用的函数这个属性是null

function outer(){
    inner();
}
function inner(){ 
    alert(inner.caller);
} 
outer();

函数的属性和方法

每个函数都有2个属性:length和prototype。length表示希望接受到的命名参数的个数。prototype以后再细说。
每个函数都包含两个方法:apply()和call()。这两个方法在特定的作用域中调用函数,实际上就是手工设置函数体内this的值。
apply接受两个参数,第一个就是你要设置的作用域,第二个是函数本身要接受的参数,这个参数可以以数组的形式给出,也可以以arguments对象形式。call则是将参数直接列在上面:

function sum(num1, num2){
    return num1 + num2;
}
function callSum(num1, num2){
    return sum.call(this, num1, num2);
}
function callSum1(num1, num2){
    return sum.apply(this, arguments);
}
function callSum2(num1, num2){
    return sum.apply(this, [num1, num2]);
}
alert(callSum(10,10));   //20
alert(callSum1(10,10));   //20
alert(callSum2(10,10));   //20

ES5新定义了一个方法bind(),这个方法创建一个函数的实例,这个函数的this值会被绑定到bind的参数上。

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

基本包装类型

三个基本类型有自己的包装类,Boolean、Number、String。

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

是不是觉得很奇怪~s1明明是一个String类型的基本类型~怎么会有自己的方法~
在你从内存中读取这个字符串的时候其实发生了一些事情:

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

自动创建的包装类只存在那一瞬间,所以为他添加属性和方法不太可能。
像上面那样显示的声明也是可以的,但是并不推荐也没有必要。对包装类实例调用typeOf会返回object,值转换时永远为true。这点一定要注意。
Object构造函数会自动识别并创建响应的包装类实例:

var obj = new Object("some text");
alert(obj instanceof String);   //true

还有一点要注意,包装类的构造函数和名字一样的转型函数要区分:

var value = "25";
var number = Number(value); 
alert(typeof number);    //"number"
var obj = new Number(value); 
alert(typeof obj);    //"object"

Boolean类型

重写了valueOf(),返回基本类型的true和false。重写了toString(),返回"true","false"。但是这个类型强烈不推荐使用,因为在转换时永远会被转换为true:

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

Number类型

var num = 10; alert(num.toString()); //"10" alert(num.toString(2)); //"1010" alert(num.toString(8)); //"12" 
alert(num.toString(10)); //"10" alert(num.toString(16)); //"a"
alert(num.toFixed(2));     //"10.00"
alert(num.toExponential(1)); //"1.0e+1"
var num = 99;
//这个方法会自动帮你选择合适的表示法
alert(num.toPrecision(1)); //"1e+2" alert(num.toPrecision(2)); //"99" alert(num.toPrecision(3)); //"99.0"

String类型

一些基本方法

var stringValue = "hello world"; alert(stringValue.length);     //"11" 
alert(stringValue.toUpperCase());        //"HELLO WORLD"
alert(stringValue.toLowerCase());        //"hello world" 
alert(stringValue.charAt(1));   //"e" 
alert(stringValue.charCodeAt(1));   //"101" 
alert(stringValue[1]);   //"e" 

concat()用来拼接字符串,不过实际中使用+更加普遍:

var stringValue = "hello "; 
var result = stringValue.concat("world", "!");  
alert(result);          //"hello world!" 
alert(stringValue);      //"hello" 

还有三个返回子字符串的方法:slice()、substr()、substring()
查找子字符串的方法:indexOf()、lastIndexOf()。和数组那个差不多,可以指定查找的子字符串和起始位置。只会返回查找到的第一个位置。
trim()删除头尾的空格:

var stringValue = "   hello world   "; 
var trimmedStringValue = stringValue.trim(); alert(trimmedStringValue);     //"hello world"   

匹配模式的方法:
match():与exec()相同,接收一个正则表达式或一个RegExp对象。返回数组。
search():与match参数相同。返回第一个匹配项的索引。
replace():第一个参数可以是一个字符串或一个正则表达式,第二个参数可以是一个字符串或一个函数。如果第一个参数是字符串,则只能替换搜索到的第一个字。

var text = "cat, bat, sat, fat";  
var result = text.replace("at", "ond"); 
alert(result);    //"cond, bat, sat, fat"  
result = text.replace(/at/g, "ond"); 
alert(result);    //"cond, bond, sond, fond" 

replace()有一些进阶用法:

var text = "cat, bat, sat, fat";  
//这里第二个参数是字符串的时候,有一些特殊的字符序列使你可以使用最近一次匹配结果中的内容$$ $& $' $` $n $nn
result = text.replace(/(.at)/g, "word ($1)"); 
alert(result);    //word (cat), word (bat), word (sat), word (fat) 
function htmlEscape(text){     
    return text.replace(/[<>"&]/g, function(match, pos, originalText){         
        switch(match){             
            case "<":                 
                return "&lt;";             
            case ">":                 
                return "&gt;";             
            case "&":                 
                return "&amp;";             
            case "\"":                 
                return "&quot;";         
        }                  
    }); 
}  
alert(htmlEscape("<p class=\"greeting\">Hello world!</p>"));  
//&lt;p class=&quot;greeting&quot;&gt;Hello world!&lt;/p&gt; 

split()方法

var colorText = "red,blue,green,yellow"; 
var colors1 = colorText.split(",");   //["red", "blue", "green", "yellow"] 
var colors2 = colorText.split(",", 2);       //["red", "blue"] 
var colors3 = colorText.split(/[^\,]+/);     //["", ",", ",", ",", ""] 

localeCompare():按字母表顺序比较字符串

var stringValue = "yellow";        alert(stringValue.localeCompare("brick"));      //1 alert(stringValue.localeCompare("yellow"));      //0 alert(stringValue.localeCompare("zoo"));         //-1 

fromCharCode():
接收字符编码初始化字符串

单体内置对象

不依赖宿主环境的对象,比如Object,Array。还有两个Global和Math。

Global

终极兜底儿对象,不属于任何其他对象的属性和方法都是它的。实际上没有全局变量和全局函数,都是它的是它的。比如isNaN()等。
encodeURI()、encodeURIComponent():
这两个是用来对URI进行编码的:

var uri = "http://www.wrox.com/illegal value.htm#start";  
//"http://www.wrox.com/illegal%20value.htm#start" alert(encodeURI(uri));  
//"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start" alert(encodeURIComponent(uri)); 

可见encodeURIComponent()只能用来对部分的URI(比如参数部分)来编码。
与之对应的是encodeURI()、encodeURIComponent()。encodeURI()就不会解码%23,只会解码空格这样的。所以用的时候要注意对应。
eval()方法:
执行字符串里的代码

eval("function sayHi() { alert('hi'); }"); 
sayHi(); 

像undefined Date NaN RegExp Infinity Error Object EvalError Array RangeError Function ReferenceError Boolean SyntaxError String TypeError Number URIError 都是Global的属性哦。
在浏览器中,Global是作为window对象的一部分来实现的。

Math对象

属性们:Math.E、Math.LN10、Math.LN2、Math.LOG2E、Math.LOG10E、Math.PI、Math.SQRT1_2、Math.SQRT2

var max = Math.max(3, 54, 32, 16); 
alert(max);    //54  
var min = Math.min(3, 54, 32, 16); 
alert(min);    //3  
//配合apply()的使用技巧
var values = [1, 2, 3, 4, 5, 6, 7, 8]; 
var max = Math.max.apply(Math, values);  
alert(Math.ceil(25.9));     //26 向上取整
alert(Math.round(25.9));    //26 四舍五入
alert(Math.floor(25.9));    //25 向下取整

Math.random()方法返回一个0-1的随机数

//1-10的随机整数
var num = Math.floor(Math.random() * 10 + 1); 

还有一些方法就不提了正弦什么的。

上一篇下一篇

猜你喜欢

热点阅读