基本引用类型

本文最后更新于:2020年11月8日 凌晨

基本引用类型

  • 引用值(或者对象)是某个特定引用类型的实例。

  • 对象被认为是某个特定引用类型的实例。

  • 新对象通过使用 new 操作符后跟一个构造函数(constructor)来创建。

    1
    2
    // Date在这里就是一个构造函数
    let now = new Date();

Date

Date 类型可以精确表示 1970 年 1 月 1 日之前及之后 285 616 年的日期。

1
2
// 在不给 Date 构造函数传参数的情况下,创建的对象将保存当前日期和时间;比如:Thu Nov 11 2020 14:23:48 GMT+0800 (中国标准时间)
let now = new Date();
Date.parse()
  • 接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒数。

  • 该方法返回的是1970年1月1日到传入时间字符串的毫秒数;

  • 允许接受的数据格式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // “月/日/年”,如"5/23/2019";
    let now_1 = Date.parse("5/23/2019"); // 1558540800000
    // “月名 日, 年”,如"May 23, 2019";
    let now_2 = Date.parse("May 23, 2019");
    // “周几 月名 日 年 时:分:秒 时区”,如"Tue May 23 2019 00:00:00 GMT-0700";
    let now_3 = Date.parse("Tue May 23 2019 00:00:00 GMT-0700");
    // ISO 8601 扩展格式“YYYY-MM-DDTHH:mm:ss.sssZ”,如 2019-05-23T00:00:00(只适用于兼容 ES5 的实现)。
    let now_4 = Date.parse("2019-05-23T00:00:00");

    // Date.parse()该方法返回毫秒数,但是在new Date中传入毫秒数返回标准时间格式
    let date = new Date(now_1);
    // 也可以在new Date中显示调用Date.parse(),也是返回标准时间格式
    let date_2 = new Date(Date.parse("May 23, 2019"));
  • 如果接受的字符串不是表示表示日期,返回NaN;

  • 如果直接传给表示上述规则日期的字符串给Date()返回结果相同;后台自动调Date.parse();获取到毫秒数再转成标准时间;

    1
    let now = new Date("5/23/2019");
Date.UTC()
  • 方法也返回日期的毫秒数,根据传入的不同的值生成的值;

  • 支持传入 年, 月(下标表示法,比如1yue是0, 2月是1), 日(1-31), 时(0-23, 24小时制), 分, 秒, 毫秒;

  • 年和月是必须的;不提供 日 默认是第1日;其他参数默认是0;

    1
    2
    3
    4
    5
    6
    7
    // 获取2000年1月1日毫秒数
    let date = Date.UTC(2000, 0); // 946684800000
    // 获取2005 4月5日 17:55:55
    let date_2 = Date.UTC(2005, 4, 5, 17, 55, 55)

    // 同样可以再new Date中传入该毫秒数获取标准时间
    let allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));
  • Date.parse()一样可以直接在new Date中传入时间格式,隐式的获取时间对象;

    1
    let now = new Date(2000, 0);
Date.now()
  • 获取方法的执行时候的日期和时间的毫秒数;

    1
    2
    3
    4
    5
    6
    7
    let start = Date.now();

    doSomeThing();

    let end = Date.now();

    let res = end - start; // 这样可以计算出中间函数运行了多少时间;
日期的格式化方法
  • toDateString,显示日期中的周几、月、日、年(格式特定于实现);
  • toTimeString()显示日期中的时、分、秒和时区(格式特定于实现);
  • toLocaleDateString()显示日期中的周几、月、日、年(格式特定于实现和地区);
  • toLocaleTimeString()显示日期中的时、分、秒(格式特定于实现和地区);
  • toUTCString()显示完整的 UTC 日期(格式特定于实现)。
  • toLocaleString()返回与浏览器运行的本地环境一致的日期和时间。
  • toString()方法通常返回带时区信息的日期和时间,而时间也是以 24 小时制(0~23)表示的。

⚠️: 因为这些方法的输出与 toLocaleString()和 toString()一样,会因浏览器而异。因此不能用于在用户界面上一致地显示日期。

日期/时间方法

除去设置日期方法,详情请看MDN;

  • getTime:返回日期的毫秒表示;与 valueOf()相同

    1
    2
    let now = new Date().getTime();
    let now_2 = new Date().valueOf();
  • getFullYear(): 返回 4 位数年(即 2019 而不是 19);

    1
    let now = new Date().getFullYear(); // 2020
  • getUTCFullYear:返回 UTC 日期的 4 位数年;

    注意这里跟getFullYear可能存在不同,即使大部分输出一样,但是当某些特定时间12月31日或者1月1日,可能输出年不一样取决于当地的时区和UTC通用时间之间的关系;

    1
    let now = new Date().getUTCFullYear(); // 2020
  • getMonth:返回日期的月(下标的方式,0 表示 1 月,11 表示 12 月)

    1
    let now = new Date().getMouth(); // 10 表示 11月
  • getUTCMonth: 返回 UTC 日期的月(下标的方式,0 表示 1 月,11 表示 12 月)区别同上

    1
    let now = new Date().getUTCMonth(); // 10 表示 11月
  • getDate: 返回日期中的日(1~31)

    1
    let now = new Date().getDate(); // 12号
  • getUTCDate: 返回日期中的日(1~31)

    1
    let now = new Date().getUTCDate(); // 12号
  • getDay: 返回日期中表示周几的数值(下标计数,从周日开始计算,0 表示周日,6 表示周六)

    1
    let now = new Date().getDay(); // 5
  • getUTCDay: 返回 UTC 日期中表示周几的数值(下标计数,从周日开始计算,0 表示周日,6 表示周六)

    1
    let now = new Date().getUTCDay(); // 5
  • getHours: 返回日期中的时(0~23, 24小时计数)

    1
    let now = new Date().getHours(); // 15
  • getUTCHours: 返回 UTC 日期中的时(0~23, 24小时计数)

    1
    let now = new Date().getUTCHours(); // 15
  • getMinutes: 返回日期中的分(0~59)

    1
    let now = new Date().getMinutes(); // 50
  • getUTCMinutes: 返回 UTC 日期中的分(0~59)

    1
    let now = new Date().getUTCMinutes(); // 50
  • getSeconds: 返回日期中的秒(0~59)

    1
    let now = new Date().getSeconds(); // 59
  • getUTCSeconds: 返回 UTC 日期中的秒(0~59)

    1
    let now = new Date().getUTCSeconds(); // 59
  • getMilliseconds: 返回日期中的毫秒

    1
    let now = new Date().getMilliseconds(); // 814
  • getUTCMilliseconds: 返回 UTC 日期中的毫秒

    1
    let now = new Date().getUTCMilliseconds(); // 814
  • getTimezoneOffset: 返回以分钟计的 UTC 与本地时区的偏移量

    1
    let now = new Date().getTimezoneOffset(); // -480

原始的包装类型

如下讲述原始值的包装类型,JS的原始值有五种:undefined, null ,Number, String, Boolean,以下只包含后三种;

每当使用某种原始值的方法或者属性的时候,后台会创建一个相应原始包装类型的对象;从而暴露出原始值的各种方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let str = "some test;
// 原始值理论上不是对象,但是后台会默认创建对象
let str_2 = str.substring(2);

// 当执行到上述第二步的时候相当于下面的三步,注意知识第二部内部相当于以下三步
// 上述的第二部相当于在新建了对象但是该生命周期执行完就销毁了;
let s1 = new String("some test");
let s2 = s1.substring(2);
s1 = null;

// 所以在原始值可以赋值但是无法访问,因为在第二行的生命周期内已经被销毁
let str_3 = "some";
str_3.color = "red";
console.log(str_3.color); // undefiend

当我们使用构造函数去创建一个原始值的包装对象的时候,使用typeof判断类型:

1
2
3
4
let str = new String("some");
typeof str === 'object'; // true
typeof str === "string"; // false
console.log(str instanceof String); //true

当我们传给new Object的类型是单类型时候,会转成相应的类型:

1
2
3
4
5
6
7
8
9
let obj = new Object("some");
typeof str === 'object'; // true
typeof str === "string"; // false
console.log(obj instanceof String); //true

let obj_2 = new Object(123);
typeof str === 'object'; // true
typeof str === "number"; // false
console.log(obj_2 instanceof Number); //true

Boolean

创建布偶值对象:

1
let boo = new Boolean(false);

布偶值对象

Boolean对象会重写对象自带的valueOf和toString两个方法,前者返回true或者false,后者返回”true”或者”false”;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 这是因为所有的对象在布偶表达式中都会转换成true
let falseObject = new Boolean(false);
let res = falseObject && true;
console.log(res); // true

let boo = false;
let res_2 = boo && true;
console.log(res_2); // false


//typeof和instance比较
typeof falseObject; // 'object'
typeof boo // 'boolean'
falseObject instanceof Boolean;// true
// 这是因为instanceof 运算符用于检测构造函数的 prototype 属性是否出现在后续的Boolean的原型链上。
boo instanceof Boolean ; // false

Number

创建数值对象:

1
let num = new Number(10);

Number类型重写了valueOf,toString, toLocaleStrting方法:

1
2
3
4
5
6
7
let num = 10;
// 在toString方法中可以穿入需要转换的进制
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
toFixed

方法提供了转换数据的小数点位数,一般来说只是支持0-20位的小数,返回值是字符串:

1
2
let num = 12;
num.toFixed(2); // "12.00",传参表示保留的小数点位数
toPrecision和toExponential

上述两种方法都可以返回科学技术法,区别在于前者可以根据情况返回(如果数值比较小,那么可以不会用科学计数表示),后者是固定返回科学计数表示,

两个函数的都接受一个参数表示需要返回的位数,返回值都是字符串形式的数值:

1
2
3
4
5
6
7
8
let num = 99;
// 这里与toFixed不同,返回的位数不是小数位数,是包括整数位数
num.toPrecision(1); "1e+2" // 1*10^2 因为四舍五入了
num.toPrecision(2); "99"
num.toPrecision(3); "99.0"

// toExponential是固定返回科学计数法,并且位数包括整数位
num.toExponential(2); // "9.90e+1"
1
2
3
4
5
6
7
8
// typeof 和 instanceof 对比
let numberObject = new Number(10);
let numberValue = 10;

typeof numberObject; // "object"
typeof numberValue; // 'number'
numberObject instanceof Number; // true
numberValue instanceof Number; // false
isInteger

该方法辨别数值保存是否是整数;

1
2
3
Number.isInteger(1); // true
Number.isInteger(1.0); // true
Number.isInteger(1.01); // flase
isSafeInteger

该方法检验数值是否是安全数值范围内的数值;

JS中数值需要存在一个安全范围内,超出范围可能保存异常

Number.MIN_SAFE_INTEGER 到 Number.MAX_SAFE_INTEGER

判断是否超出这个范围可使用isSafeInTeger

1
2
Number.isSafeTeger(-1* (2 ** 53)); // false
Number.isSafeTeger(-1* (2 ** 53) + 1); // true

String

创建字符串对象:

1
let strObj = new String("hello world");

3个继承的方法valueOf(), toLocaleString(), toString()都是返回对象的原始字符串值;

1
2
3
4
5
let str = new String("some");

str.valueOf(); // "some"
str.toLocaleString(); // "some"
str.toString(); // "some"
length

每一个String对象都有一个length属性,表示字符串中字符的数量:

1
2
let str = "some";
str.length = 4;

一般来说JS中的字符串都是由16位码元组成的(UTF-16),对于很多字符来说,每16位码元对应一个字符,使用length 就是返回字符串中包含了多少了16位码元;

1
2
let str = "abcd";
str.length; // 4
charAt

该方法返回字符串中给定索引的字符,搜索字符是从下标0开始计算, 不穿入参数默认是第0位字符:

1
2
3
let str = "abcde"
str.charAt(2); // 'c'
// 当如果字符串是空的,那么无论输入几都是返回的空字符串 - ""
concat

将一个或多个字符串拼接成一个新字符串,但是不会改变原字符串:

1
2
3
4
5
6
7
let str = 'some';
let res = str.concat( "world");
console.log(res); // some world;

// concat可以穿入多个参数字符串
let res_2 = str.concat("world", "!");
console.log(res_2); // some world!;
slice

返回调用它们的字符串的一个子字符串, 第一个参数表示子字符串开始的位置下标包含该位置,第二个参数表示子字符串结束的位置下标(即该位置之前的字符会被提取出来,不包含该位置的);

省略第二个参数表示提取到字符串末尾, 不会修改原字符串;

字符串都是按照数组从下标0开始计算:

1
2
3
let str = "hello world";
str.slice(3); // "lo world
str.slice(3, 7); // "lo w"

当穿入参数为负数的时候,将所有负值参数都当成字符串长度加上负参数值进行操作,可以理解成对字符串的倒数位数进行操作:

1
2
3
let str = "some";
str.slice(-1); // "e" , 可以获取字符串的最后一位,即最后一位的下标
str.slice(-2, -1); // str.slice(2, 3) "m"
substr

第一个参数是提取的开始位置,第二个参数是提取的个数, 不会修改原字符串;

1
2
3
4
let str = "some";
str.substr(0, 1); // "s"
str.substr(-1); // 当第一个参数为负数的时候,那么跟slice一样 "e"
str.substr(-1, -2); // 当第二个参数是负数的时候,那么会变成0, 那么返回的字符串为空字符串 - "" (str.substr(-n, 0))
substring

返回调用它们的字符串的一个子字符串, 第一个参数表示子字符串开始的位置下标包含该位置,第二个参数表示子字符串结束的位置下标(即该位置之前的字符会被提取出来,不包含该位置的),与slice基本一致;

1
2
3
let str = "some";
str.substring(3); // "e"
str.substring(0, 3); // "som" => str.substring(3, 0) 相同的结果

在substring中如果存在负的值,那么就会全部替换成0, 在穿入值中0,1和1,0表达相同的结果;

1
2
let str = "some";
str.substring(3, -1); // "som" => str.substring(3, 0) 相同的结果
indexOf 和 lastIndexOf

这两个方法从字符串中搜索传入的字符串,并返回位置下标(如果没找到,则返回-1)。

  • indexOf 是从字符串的开头开始寻找;
  • lastIndexOf 是从字符串的末尾开始寻找;
1
2
3
let str = "hello world";
str.indexOf("o"); // 4
str.lastIndexOf("o"); // 7

两个方法都可以传入第二个参数,表示开始搜索的位置:

1
2
3
let str = "hello world";
str.indexOf("o", 6); // 7 从第6位向后搜索
str.lastIndexOf("o", 6); // 4 从第6位向前搜索
startsWith 和 endsWith

这两者都是搜索是否包含某一个字符串的,都是返回布偶值(true/false):

  • startsWith 是检查是不是以某个字符串开头,是的话(包括空字符串)返回true,否则返回false;
  • endsWith 是检查是不是以某个字符串结尾的,是的话(包括空字符串)返回true,否则返回false;
  • 他们都无法检查字符串中间是否存在某个子字符串;
1
2
3
4
5
let str = "some world";
str.startsWith("som"); // true
str.startsWith("om"); // false
str.endsWith("ld"); // true
str.endsWith("rl"); // false

startsWith 传入第二个参数就表示开始搜索的位置:

1
2
3
4
let str = "some world";
str.startsWith("som"); // true
str.startsWith("som", 1); // false
str.startsWith("om", 1); // true
includes

该方法会检查整个字符串是否存在某个字符串,返回布偶值;

1
2
3
4
5
6
7
let str = "some world";
str.includes("som"); // true
str.includes(""); // true
str.includes("qqq"); // false

// 传入第二个参数表示开始搜索的位置同上;
str.includes("som", 2); // false
trim

这个方法会创建字符串的一个副本,删除前、后所有空格符,再返回结果, 该方法不会修改原字符串:

1
2
let str = " some world ";
console.log(str.trim()); // "some world"
repeat

这个方法接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果,, 该方法不会对原字符串产生改变:

1
2
let str = "na";
console.log(str.repeat(6)); // "nananananana"
padStrat 和 padEnd

padStart()和 padEnd()方法会复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度条件

  • 第一个参数是长度,如果小于原本字符串长度,那么会返回原字符串;
  • 第二个参数是需要填充的内容,默认是空格,如果填充内容是多个字符串,那么可能因为长度不够而被截断;
  • padStart是拼接在字符串前面,padEnd是拼接在字符串的最后;
  • 不会对原字符串进行修改;
1
2
3
4
5
6
let str = "some";
str.padStart(6); // " some"
str.padStart(6, "!"); // "!!some"

str.padEnd(6); // "some "
str.padEnd(6, "!"); // "some!!"
toLowerCase, toLocaleLowerCase, toUpperCase, toLocaleUpperCase
  • toLowerCase, toLocaleLowerCase都是转换成小写;
  • toUpperCase, toLocaleUpperCase都是转换成大写;
  • toLocaleLowerCase,toLocaleUpperCase和两种不一样的地方在于,在少数语言中(如土耳其语),Unicode 大小写转换需应用特殊规则,要使用地区特定的方法才能实现正确转换。
1
2
3
4
5
let stringValue = "hello world"; 
console.log(stringValue.toLocaleUpperCase()); // "HELLO WORLD"
console.log(stringValue.toUpperCase()); // "HELLO WORLD"
console.log(stringValue.toLocaleLowerCase()); // "hello world"
console.log(stringValue.toLowerCase()); // "hello world"
replace

该方法可以替换字符串的局部,这个方法接收两个参数,第一个参数可以是一个 RegExp 对象或一个字符串(这个字符串不会转换为正则表达式),第二个参数可以是一个字符串或一个函数(关于replace的回掉后续在说明)。

要想替换所有子字符串,第一个参数必须为正则表达式并且带全局标记,不会改变原字符串;

1
2
3
4
5
6
7
8
let text = "cat, bat, sat, fat"; 

let result = text.replace("at", "ond");
// 只会替换第一个at
console.log(result); // "cond, bat, sat, fat"

result = text.replace(/at/g, "ond");
console.log(result); // "cond, bond, sond, fond"
split

这个方法会根据传入的分隔符将字符串拆分成数组;。作为分隔符的参数可以是字符串,也可以是 RegExp 对象;

还可以传入第二个参数,即数组大小;

1
2
3
let colorText = "red,blue,green,yellow"; 
let colors1 = colorText.split(","); // ["red", "blue", "green", "yellow"]
let colors2 = colorText.split(",", 2); // ["red", "blue"]

Global

Global 对象是 ECMAScript 中最特别的对象,因为代码不会显式地访问它。

在全局作用域中定义的变量和函数都会变成 Global 对象的属性 。包括 isNaN()、isFinite()、parseInt()和 parseFloat(),实际上都是 Global 对象的方法;

encodeURL 和 encodeURLComponent

两个方法用于编码统一资源标识符(URI),以便传给浏览器识别;

区别在于前者不会编码属于URL的特殊字符,比如URL自带的问号,斜杠,冒号等;但是后者会编码所有特殊字符;

1
2
3
4
5
6
7
let uri = "http://www.wrox.com/illegal value.js#start";

// "http://www.wrox.com/illegal%20value.js#start" ,
console.log(encodeURI(uri));

// "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start"
console.log(encodeURIComponent(uri));
decodeURL 和 decodeURLComponent

两个方法与上述相对,但是是解码上述对应的编码;

比如decodeURL只会解码decodeURL能编码的字符,比如%20空格,对于%23这种不会解码因为不是encodeURL编码的;

decodeURLComponent可以解码所有被 encodeURIComponent()编码的字符,基本上就是解码所有特殊值;

1
2
3
4
5
6
7
let uri = "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.js%23start";

// http%3A%2F%2Fwww.wrox.com%2Fillegal value.js%23start, %3A %2F等不会被解码;
console.log(decodeURI(uri));

// http:// www.wrox.com/illegal value.js#start
console.log(decodeURIComponent(uri));
eval

eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。详情请看MDN;

  • 这个方法就是一个完整的 ECMAScript 解释器,它接收一个参数,即一个要执行的 ECMAScript(JavaScript)字符串;
  • 当解释器发现 eval调用时,会将参数解释为实际的 ECMAScript 语句,然后将其插入到该位置;
  • 如果参数不是字符串那么eval() 会将参数原封不动地返回;
  • 在严格模式下,在 eval()内部创建的变量和函数无法被外部访问,无法访问外部变量和方法或者暴露变量和方法。
  • eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。
1
2
3
eval("console.log('hi')"); // 等价于 console.log('hi');
console.log(eval('2 + 2') === eval('4')); // true
console.log(eval('2 + 2')); // 4
1
2
3
// 通过 eval()执行的代码属于该调用所在上下文,被执行的代码与该上下文拥有相同的作用域链
let msg = "hello world";
eval("console.log(msg)"); // "hello world"
1
2
3
// 类似地,可以在 eval()内部定义一个函数或变量,然后在外部代码中引用
eval("function sayHi() { console.log('hi'); }");
sayHi();
弊端

不建议使用eval函数,因为该方法会执行传入的所有字符串,可能是来历不明的字符串或者参数;

如果参数是一个修改数据的JSON串那么可能会直接执行;

对象属性

Global 对象有很多属性,其中一些前面已经提到过了。像 undefined、NaN 和 Infinity 等特殊值都是 Global 对象的属性。

属性名 解释
undefined 特殊值 undefined
NaN 特殊值 NaN
Infinity 特殊值 Infinity
Object Object构造函数
Array Array构造函数
Function Function构造函数
Boolean Boolean构造函数
String String构造函数
Number Number构造函数
Date Date构造函数
RegExp RegExp构造函数
Symbol Symbol构造函数
Error Error 的构造函数
以及各种报错的构造函数,比如TypeError, RangeError等

window简介

虽然没办法直接访问Global,但是浏览器将window当作Global对象的代理;

所以全局的变量和函数都变成了window的属性;

1
2
3
4
5
var color = "red"; 
function sayColor() {
console.log(window.color);
}
window.sayColor(); // "red"

Math

ECMAScript 提供了 Math 对象作为保存数学公式、信息和计算的地方。

min 和 max 方法

min()和 max()方法用于确定一组数值中的最小值和最大值。

这两个方法都接收任意多个参数, 返回需要的数据;

1
2
3
4
5
let max = Math.max(3, 54, 32, 16); 
console.log(max); // 54

let min = Math.min(3, 54, 32, 16);
console.log(min); // 3
ceil

该方法始终向上舍入为最接近的整数;

1
2
3
console.log(Math.ceil(25.9)); // 26 
console.log(Math.ceil(25.5)); // 26
console.log(Math.ceil(25.1)); // 26
floor

该方法始终向下舍入为最接近的整数;

1
2
3
console.log(Math.floor(25.9)); // 25 
console.log(Math.floor(25.5)); // 25
console.log(Math.floor(25.1)); // 25
round

该方法进行四舍五入;

1
2
3
console.log(Math.round(25.9)); // 26 
console.log(Math.round(25.5)); // 26
console.log(Math.round(25.1)); // 25
fround

该方法返回数值最接近的单精度(32 位)浮点值表示

1
2
3
console.log(Math.fround(0.4)); // 0.4000000059604645 
console.log(Math.fround(0.5)); // 0.5
console.log(Math.fround(25.9)); // 25.899999618530273
random

Math.random()方法返回一个 0~1 范围内的随机数,其中包含 0 但不包含 1 — [ 0 , 1 ) 。

1
2
3
4
Math.random(); // 0.8181384571824157 位数如左,每一次不一定相同

// 当我们需要随机个位整数, 范围 0 - 9
let num = Math.floor(Math.random() * 10);

当我们需要符合密码学的随机数可以使用widnow.crypto.getRandomValues();后续说明


基本引用类型
http://www.clearluv.com/2020/11/08/基本引用类型/
作者
徐楠峰
发布于
2020年11月8日
许可协议