JS学习4(引用类型)

作者: exialym | 来源:发表于2016-05-13 20:41 被阅读53次

    引用类型是一种数据结构,用于将数据和功能组织在一起。对象是某个特定引用类型的实例。新对象使用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类型的基本类型~怎么会有自己的方法~
    在你从内存中读取这个字符串的时候其实发生了一些事情:

    • 在你开始读取这个值的时候,ES为你创建了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); 
    

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

    相关文章

      网友评论

      • 我是刘高兴:问下这是看了JS高级程序设计的读书笔记吗~(我刚开始,还没看这书…)
        exialym:@我是刘高兴 嗯哼~

      本文标题:JS学习4(引用类型)

      本文链接:https://www.haomeiwen.com/subject/vvdmrttx.html