《JavaScript高级程序设计》Chapter 5 引用类型

Chapter 5 引用类型

Object类型

  1. 创建Object实例

    • new Object()
    var person = new Object();
    person.name = "Hello";
    
    • 使用对象字面量表示法(向函数传递大量参数的首选方式)
    var person = {
        name: "Hello",
        age: 29
    };
    //属性名可以使用字符串,效果相同
    var person = {
        "name": "Hello",
        "age": 29
    };
    
    • 使用空花括号(等价于new Object())
    var person = {};
    person.name = "Hello";
    
    • 访问对象可以使用点表示法也可以使用方括号表示法
    alert(person["name"]);
    alert(person.name);
    var propertyName = "name";
    alert(person[propertyName]);
    
    • 属性名中包含会导致语法错误的字符,或者属性名使用的是关键字或保留字,可以使用方括号表示法。
    • 通常,除非必须使用方括号表示法,否则推荐使用点表示法。

Array类型

  1. 一个数组的每一项可以保存不同的数据类型

  2. 创建Array实例

    • var colors = new Array();
    • var colors = new Array(20);
    • var colors = new Array("red", "green", "blue");
    • var colors = ["red", "green", "blue"];
    • var names = [];
    • var values = [1, 2,]; //这样会创建不一定2或3个元素的数组。
    • var opts = [ , , , , , ]; //这样会创建不一定5或6个元素的数组。
  3. 数组的length属性不是只读的

    var colors = ["red", "green", "blue"];
    colors.length = 2;
    alert(colors[2]); //undefined
    
    var colors = ["red", "green", "blue"];
    colors.length = 4;
    alert(colors[3]); //undefined
    
  4. 数组元素个数变化后,其length属性会自动更新

    var colors = ["red", "green", "blue"];
    colors[colors.length] = "black";
    
    var colors = ["red", "green", "blue"];
    colors[99] = "black";
    alert(colors.length); //100
    
  5. 可以使用检测数组

    • values instanceof Array
  6. ES5+之后,使用Array.isArray()方法也可以检测数组

    • Array.isArray(values);
  7. 转换方法

    • toString(): 得到一个以逗号分隔的字符串
    • valueOf(): 返回数组
    • alert(valueOf())会后台调用toString()
    • toLocaleString()通常返回和toString()同样的值,但是允许分别定义toString()与toLocaleString以达到不同的输出
    var person1 = {
        toLocaleString: function() {
            return "Nikolaos";
        },
        toString: function() {
            return "Nicholas";
        }
    };
    var person2 = {
        toLocaleString: function() {
            return "Grigorios";
        },
        toString: function() {
            return "Greg";
        }
    };
    
    • join(): 使用不同的分隔符连接数组元素
    var colors = ["red", "green", "blue"];
    alert(colors.join(","));
    alert(colors.join("||"));
    
  8. 栈方法

    • colors.push(arg1, arg2, ...); //在colors数组末尾逐个添加元素,返回值是推入个数
    • colors.pop(); //弹出数组末尾的元素,并同时将数组长度减1
  9. 队列方法

    • colors.shift(); //取得第一项,并同时将数组长度减1
    • colors.unshift(arg1, arg2, ...); //在数组的前端添加项,返回值是推入个数
    var colors = new Array();
    var count = colors.unshift("red", "green");
    count = colors.unshift("black");
    alert(colors.pop()); // "green"
    
  10. 重排序方法

    • values.reverse(); //将数组元素顺序颠倒
    • values.sort(); //将数组按照一定规则排序(可以接收一个比较函数,默认升序)
    • 返回值是经过排序后的数组
    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);
    
    function compare(value1, value2) {
        return value1 - value2;
    }
    
  11. 操作方法

    • concat()
    var colors = ["red", "green", "blue"];
    var colors2 = colors.concat("yellow", ["black", "brown"]); //传入一个值元素yellow和一个包含两个值(black, brown)的数组元素
    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() 向数组的中部插入项
      • 删除:splice(0, 2)删除数组中的前两项
      • 插入:splice(2, 0, "red", "green")从当前数组的位置2开始插入字符串"red"和"green"。
      • 替换:splice(2, 1, "red", "green")删除当前数组位置2的项,并从位置2开始插入字符串"red"和"green"。
      • 始终返回一个数组,包含从原数组中删除的项(如果没有删除的项,则返回空数组)。
  12. 位置方法

    • indexOf() 从数组的开头(位置0)开始向后查找
    • lastIndexOf() 从数组的末尾开始向前查找
    • 接收两个参数:要查找的项、查找起点位置的索引(可选)
    • 返回 要查找的项在数组中的位置/不存在时返回-1
  13. 迭代方法

    • every() 对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true
    • filter() 对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组
    • forEach() 对数组中的每一项运行给定函数,无返回值
    • map() 对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
    • some() 对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true
    • 传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置、数组对象本身
    • 以上方法都不会修改数组中的包含的值
    • Example
    var numbers = [1, 2, 3, 4];
    var everyResult = numbers.every(function(item, index, array) {
        return (item > 2);
    });
    alert(everyResult); //false
    
  14. 归并方法

    • reduce() 从数组的第一项开始,逐个遍历到最后,并构建一个返回值
    • reduceRight() 从数组的最后一项开始,向前遍历到第一项,并构建一个返回值
    • 以上方法都接收两个参数:在每一项上调用的函数、作为归并基础的初始值(可选);调用函数接收4个参数:前一个值、当前值、项的索引和数组对象本身。
    • Example
    var values = [1, 2, 3, 4, 5];
    var sum = values.reduce(function(prev, cur, index, array) {
        return prev + cur;
    });
    alert(sum); //15
    

Date类型

  1. 使用UTC时间1970年1月1日0时开始经过的毫秒数来保存日期,能精确到1970.1.1之前或之后的100 000 000年。
  2. 初始化
    • var now = new Date(); // 自动获取当前的日期和时间
    • var time = new Date(1000) // 传入指定日期的毫秒数以初始化为指定日期
      • 为了简化这个计算过程,ES提供了两个方法:Date.parse()和Date.UTC();
      • Date.parse() 接收一个表字符串参数,然后尝试根据这个字符串返回相应时期的毫秒数。
      • Date.UTC() 接收年份、基于0的月份(0-11)、月中的哪一天(1-31)、时、分、秒、毫秒,只有年月是必须的。
      • Date.UTC()的日期和时间都基于本地时区创建。
  3. Date.now() 返回调用该方法时的日期和时间的毫秒数
  4. 继承的方法
    • toLocaleString() 按照与浏览器设置的地区相适应的格式返回日期和时间
    • toString() 通常返回带有时区信息的日期和时间,时间一般以军用时间(0-23小时)表示
    • valueOf() 不返回字符串,返回日期的毫秒表示
  5. 格式化方法
    • toDateString() 以特定于实现的格式显示星期、月、日、年
    • toTimeString() 以特定于实现的格式显示时、分、秒、时区
    • toLocaleDateString() 以特定于地区的格式显示星期、月、日、年
    • toLocaleTimeString() 以特定于实现的格式显示时、分、秒
    • toUTCString() 以特定于实现的格式显示完整的UTC日期
  6. 日期/时间组件方法
    • 查阅手册

RegExp类型

  1. 通过RegExp类型来支持正则表达式

  2. 字面量形式创建正则表达式:var expression = / pattern / flags;

    • pattern 模式部分:可以是任何简单或复杂的正则表达式,可以包含字符串、限定符、分组、向前查找以及反向引用。每个正则表达式都可以带有一个或多个标志(flags),用以表明正则表达式的行为。
    • flags 标志:
      • g: 表示全局模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止。
      • i: 表示不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写。
      • m: 表示多行模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
    • Examples
      • var pattern1 = /at/g; 匹配字符串中所有的"at"实例
      • var pattern2 = /[bc]at/i; 匹配第一个"bat"或"cat",不区分大小写
      • var pattern3 = /.at/gi; 匹配所有以"at"结尾的3个字符的组合,不区分大小写
    • 模式中使用的所有元字符必须转义。包括:( [ { \ ^ & | ? * + . ] )
      • var pattern4 = /[bc]at/i; 匹配第一个"[bc]at",不区分大小写
      • var pattern5 = /.at/gi; 匹配所有".at",不区分大小写
  3. 使用RegExp构造函数创建正则表达式

    • 接收两个参数:要匹配的字符串模式、标志字符串(可选)

      • var pattern1 = /[bc]at/i;
      • var pattern2 = new RegExp("bc[at]", "i");
    • 不能把正则表达式的字面量直接传给RegExp构造函数。

    • 需要双重转义

      字面量模式 等价的字符串
      /[bc]at/ "\[bc\]at"
      /.at/ "\.at"
      /name/age/ "name\/age"
      /\d.\d{1,2}/ "\d.\d{1,2}"
      /\w\hello\123/ "\w\\hello\\123"
    • Example

    var re = null;
    for(i = 0; i < 10; i++) {
        re = /cat/g; // 即便是循环也只创建一次RegExp实例,所以只有第一次调用test()的时候会成功,这是因为第一次调用test()时找到了"cat",但第二次调用是从索引为3的字符(上一次匹配的末尾)开始的。
        re.test("catastrophe");
    }
    for(i = 0; i < 10; i++) {
        re = new RegExp("cat", "g"); // 每一次迭代都会创建新的RegExp实例,所以每次循环都会创建一个新的正则表达式。
        re.test("catastrophe");
    }
    
  4. RegExp实例属性

    • global: 布尔值,表示是否设置了g标志。
    • ignoreCase: 布尔值,表示是否设置了i标志。
    • lastIndex: 整数,表示开始搜索下一个匹配项的字符位置,从0算起。
    • multiline: 布尔值,表示是否设置了m标志。
    • source: 正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
  5. RegExp实例方法

    • exec() 专门为捕获组设计,接受一个参数:要应用模式的字符串;返回:包含第一个匹配项信息的数组|没有匹配项返回null。

      • 返回的数组是Array实例,但是包含两个额外的属性:index(表示匹配项在字符串中的位置)、input(表示应用的正则表达式的字符串)。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中捕获组普配的字符串(如果没有捕获组,则该数组只包含一项)。
      • Example
      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"
      
      • 对于exec()方法而言,即使在模式中设置了全局标志(g),它每次也只会返回一个匹配项。在不设全局标志的情况下,在同一个字符串上多次调用exec()将始终返回第一个匹配项的信息;而设置全局标志后,每次调用exec()则会在字符串中继续查找新的匹配项。(IE除外)
    • test() 接受一个字符串参数,在模式与该参数匹配的情况下返回true,否则返回false。

    • toString()和toLocaleString() 返回正则表达式的字面量。

  6. 构造函数属性

    • 查阅手册
  7. 局限性 (JS不支持的高级正则表达式)

    • 匹配字符串开始和结尾的\A和\Z锚。
    • 向后查找
    • 并集和交集类
    • 原子组
    • Unicode支持(单个字符除外)
    • 命名的捕获组
    • s(单行)标志和x(无间隔)标志匹配模式
    • 条件匹配
    • 正则表达式注释

Function类型

  1. 每个函数都是Function类型的实例,并且与其他引用类型一样具有属性和方法。

  2. 定义

    function sum (num1, num2) {
        return num1 + num2;
    }
    
    var sum = function(num1, num2) {
        return num1 + num2;
    };
    
    var sum = new Function("num1", "num2", "return num1 + num2"); //接收任意参数,但最后一个参数始终被作为函数体。不推荐。
    
  3. 没有重载

    • 使用相同名称定义的函数,后一个会覆盖前一个。
  4. 函数的声明与函数表达式

    • 解析器会率先读取函数声明,使其在执行任何代码之前可用。但是函数表达式必须等到解析器执行到它所在的代码行才能被解释执行。

      alert(sum(10, 10));
      function sum (num1, num2) {
          return num1 + num2;
      } // 可以正确执行
      
      alert(sum(10, 10));
      var sum = function(num1, num2) {
          return num1 + num2;
      }; // 无法正确执行,因为函数在一个初始化语句中,而不是声明。会导致第一行代码出现unexpected indentifier错误。
      
  5. 作为值的函数

    • 函数名本身是变量,所以可以被当做参数传递,但是要去掉圆括号。也可以当做返回值被传递出去。
  6. 函数内部属性

    • arguments: 有一个callee属性,是一个指针,指向拥有这个arguments对象的函数。
    function factorial(num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * arguments.callee(num - 1);
        }
    }
    
    • this: 引用函数执行的环境对象
    • caller: 保存着调用当前函数的引用,如果在全局作用域调用当前函数,则它的值为null。
  7. 函数的属性和方法

    • 属性

      • length: 表示函数希望接收的命名参数的个数
      • prototype: 保存他们所有实例方法的真正所在。
    • 方法

      • apply(): 接收两个参数:在其中运行函数的作用域、参数数组(可以是Array实例,也可以是arguments对象)
      function sum(num1, num2) {
          return num1 + num2;
      }
      function callSum(num1, num2) {
          return sum.apply(this, arguments);
      }
      function callSum_1(num1, num2) {
          return sum.apply(this, [num1, num2]);
      }
      
      • call(): 第一个参数是this没有变化,其与参数直接传递给函数
      function sum(num1, num2) {
          return num1 + num2;
      }
      function callSum(num1, num2) {
          return sum.call(this, num1, num2);
      }
      
      • 真正作用:扩充函数赖以运行的作用域。
      window.color = "red";
      var o = { color: "blue"};
      function sayColor() {
          alert(this.color);
      }
      sayColor();
      sayColor().call(this); // "red"
      sayColor().call(window); // "red"
      sayColor().call(o); // "blue"
      
      • apply和call在特定的作用域中调用函数,实际等于设置函数体内this对象的值。
      • bind(): 创建一个函数的实例,其this值会被绑定到传给bind()函数的值
      window.color = "red";
      var o = { color: "blue" };
      function sayColor() {
          alert(this.color);
      }
      var objectSayColor = sayColor.bind(o);
      objectSayColor(); // "blue"
      

基本包装类型

  1. 特殊的引用类型:Boolean Number String (基本包装类型)

  2. 基本类型值不是对象,所以不应该有方法。为了实现与访问对象一样的方法,系统进行了一系列处理,对于代码:

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

    系统进行了如下处理:

    • 创建String类型的一个实例
    • 在实例上调用指定方法
    • 销毁这个实例
  3. 引用类型与基本包装类型的主要区别:对象的生存期

    • 使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中;自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后被立即销毁。
    • 所以,不能在运行时为基本类型值添加属性和方法。
    • 可以显式地调用Boolean Number String来创建基本包装类型的对象,但是不推荐。对基本包装类型的实例调用typeof会返回"object",而且所有基本包装类型的对象都会被转换为布尔值true。
  4. Boolean类型

    • var booleanObject = new Boolean(true);
    • 在布尔表达式中使用Boolean对象会出现问题:
    var falseObject = new Boolean(false);
    var result = falseObject && true;
    alert(result); // true
    
    var falseValue = false;
    var result = falseValue && true;
    alert(result); // false
    
  5. Number类型

    • var numberObject = new Number(10);
    • 可以为toString方法传递一个表示基数的参数,告诉他返回几进制数值的字符串形式
    var num = 10;
    alert(num.toString(2)); // "1010"
    
    • 提供了一些用于将数值格式化为字符串的方法

      • toFixed() 按照指定的小数位返回值的字符串表示
      var num = 10;
      alert(num.toFixed(2)); // "10.00"
      
      var num = 10.005;
      alert(num.toFixed(2)); // "10.01"
      
      • toExponential() 返回以指定指数表示法表示的数值的字符串形式
      var num = 10;
      alert(num.toExponential(1)); // "1.0e+1"
      
      • toPrecision() 得到表示某个数值最合适的格式,接收一个参数,表示数值的所有数字的位数(不包括指数部分)
      var num = 99;
      alert(num.toPrecision(1)); // "1e+2"
      alert(num.toPrecision(2)); // "99"
      alert(num.toPrecision(3)); //"99.0"
      
  6. String类型

    • var stringObject = new String("Hello World!");

    • 实例方法

      • charAt()/charCodeAt() 用于访问字符串中的特定字符,接收一个参数,即基于0的字符位置,以 单字符字符串/字符编码 的形式,返回那个字符。
      • concat() 用于将一个或多个字符串拼接起来,返回拼接得到的字符串
      var stringValue = "hello ";
      var result = stringValue.concat("world");
      alert(result); // "hello world"
      
      • slice() substr() substring() 接收两个参数:指定字符串开始的位置、(可选)表示字符串到哪里结束/要返回的字符个数
      var stringValue = "hello world";
      alert(stringValue.slice(3)); // "lo world"
      alert(stringValue.substring(3)); // "lo world"
      alert(stringValue.substr(3)); // "lo world"
      alert(stringValue.slice(3, 7)); // "lo w"
      alert(stringValue.substring(3, 7)); // "lo w"
      alert(stringValue.substr(3, 7)); // "lo worl"
      
      var stringValue = "hello world";
      alert(stringValue.slice(-3)); // "rld" -3 会被转换成 8 (-3 + 字符串长度11 = 8)
      alert(stringValue.substring(-3)); // "hello world"
      alert(stringValue.substr(-3)); // "rld"
      alert(stringValue.slice(3, -4)); // "lo w"
      alert(stringValue.substring(3, -4)); // "hel"
      alert(stringValue.substr(3, -4)); // ""
      
      • indexOf() lastIndexOf() 从头/从末尾搜索给定的字符串,然后返回该字符串的位置(未找到返回-1);可选参数,表示从哪个位置开始搜索。
      var stringValue = "hello world";
      alert(stringValue.indexOf("o")); // 4
      alert(stringValue.lastIndexOf("o")); // 7
      
      var stringValue = "hello world";
      alert(stringValue.indexOf("o", 6)); // 7
      alert(stringValue.lastIndexOf("o", 6)); // 4 (IMPORTANT)
      
      • trim() 创建一个字符串副本,删除前置及后缀的所有空格,然后返回结果。
      • toLowerCase() toLocaleLowerCase() toUpperCase() toLocaleUpperCase() 带Locale的会进行地区优化
      • 字符串模式匹配(基于String) match() search()(返回字符串中第一个匹配的索引/没找到返回-1)
      var text = "cat, bat, sat, fat";
      var pattern = /.at/;
      var matches = text.match(pattern); // 等同于 var matches = pattern.exec(text);
      alert(matches.index); // 0
      alert(matches[0]); // "cat"
      alert(pattern.lastIndex) // 0
      
      var text = "cat, bat, sat, fat";
      var pos = text.search(/at/);
      alert(pos); // 1
      
      • replace() 字符串替换 第一个参数是一个RegExp对象或者一个字符串,第二个参数是一个字符串或者一个函数(参数:模式的匹配项、模式匹配项在字符串中的位置、原始字符串),为了实现插入的效果,ES提供了一些特殊字符序列(查阅手册)
      var text = "cat, bat, sat, fat";
      var result = text.replace("at", "ond"); // 只替换找到的第一个
      var result = text.replace(/at/g, "ond"); // 按照正则规则,替换全部
      
      function htmlEscape(text) {
          return text.replace(/[<>"&]/g, function(match, pos, originalText) {
              switch(match) {
                  case "<" return "\<";
                  case ">" return "\>";
                  case "&" return "\&";
                  case "\" return "\"";s
              }
          });
      }
      alert(htmlEscape("<p class=\"greeting\">Hello World!</p>")); // <p class="greeting">Hello World!</p>
      
      • split() 基于指定分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是一个字符串,也可以是一个RegExp对象。split()可以接受可选的第二个参数,用于指定数组的大小,以确保返回的数组不会超过既定大小。
      var colorText = "red, blue, green, yellow";
      var color = colorText.split(/[^\,]+/); // ["", ",", ",", ",", ""]
      
      • localeCompare() 比较两个字符串,并返回下列值中的一个
        • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数
        • 如果字符串等于字符串参数,则返回0
        • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数
      var stringValue = "yellow";
      alert(stringValue).localeCompare("brick"); // 1
      
      • fromCharCode() 静态方法,接收一或多个字符编码,然后将它们转换成一个字符串。
      alert(String.fromCharCode(104, 101, 108, 111)); // "hello"
      
      • HTML方法 查阅手册

单体内置对象

  1. 由ES实现提供的、不依赖于宿主环境的对象,如:Object Array String..

  2. Global对象

    • 不属于任何其他对象的属性和方法,最终都是它的属性和方法。
    • URI编码方法:使用encodeURI()和encodeURIComponent()方法可以对URI进行编码,以便发送给浏览器。encodeURI()不会对本身属于URI的特殊字符编码,而encodeURIComponent()会对它发现的任何非标准字符进行编码。
    • URI解码方法:decodeURI()和decodeURIComponent()
    • eval()方法 完整的ES解析器,接收一个参数:要执行的ES字符串。
      • eval("alert('hi')"); // 等价于 alert("hi");
      • 在严格模式下,在外部访问不到eval()中创建的任何变量或者函数。
    • 属性 查阅手册
    • window对象:Global对象作为window对象的一部分加以实现;因此,在全局作用域中声明的所有变量和函数,都成为了window对象的属性。
    • 取得global对象:
    var global = function() {
        return this;
    }();
    
  3. Math对象

    • 为保存数学公式和信息提供
    • Math.random() 返回一个 0 - 1 的随机数
    • 查阅手册

推荐阅读更多精彩内容