Object 类型
创建Object实例的方式有两种
- new Object() 方式
- 对象字面量方式
var obj1 = new Object() // new 关键字创建Object对象
var obj2 = {} // 对象字面量方式
console.log(obj1, obj2);
Array类型
创建数组的方式有两种
- new Array() 方式
- 数组字面量方式
var arr = new Array(); // 构造函数
var colors = ["red", "green"]; // 数组字面量
检测数组: Array.isArray()
转换方法
数组是对象,拥有Object原型上自带的7个属性。
数组调用toString():数组的每一项都调用toString()转为字符串形式,再用逗号拼接成一个字符串
数组调用valueOf(): 还是数组
var p1 = {
toString: () => "p1 toString",
toLocaleString: () => "p1 toLocaleString",
};
var p2 = {
toString: () => "p2 toString",
toLocaleString: () => "p2 toLocaleString",
};
var arr = [p1, p2];
console.log(
arr.toString(), // p1 toString,p2 toString
arr.toLocaleString() // p1 toLocaleString,p2 toLocaleString
);
从上面示例可以看出,数组调用toString()经历了两个步骤
- 每一项都调用toString(),转为字符串表示
- 再把每一项字符串表示,用逗号拼接为一个字符串
栈方法
栈是后进先出,用数组方法来模拟栈
push(),接收多个参数,把参数推入末尾;返回推入后的数组长度
pop(), 没有参数,从数组末尾移除最后一项;返回移除的项
var arr = [];
//push 推入多个元素,返回新推入后的数组长度
var count = arr.push("red", "blue");
console.log(count, arr); // 2 ['red', 'blue']
// pop 弹出尾部最后一个元素
var item = arr.pop();
console.log(item, arr); // blue ['red']
队列方法
队列:先进先出
push(),接收多个参数,从队列尾部推入;返回推入之后的数组
shift(), 没有参数,从队尾移除一个元素,返回移除的元素
var arr = [];
var count = arr.push("red", "green");
console.log(count, arr); // 2 ['red', 'green']
// shift 是从头部移除一个元素,返回移除的值
var item = arr.shift();
console.log(item, arr); // red ['green']
反过来,用unshift()从队列头部推入多个参数;用pop() 从队列尾部移除一个元素
var arr = [];
// unshift(), 从队列头部添加多个元素
var count = arr.unshift("red", "green");
console.log(count, arr); // 2 ['red', 'green']
// pop 是从队列尾部移除一个元素,返回移除的值
var item = arr.pop();
console.log(item, arr); // green ['red']
栈和队列的示意图
| 数组方法 | 作用 | 返回值 |
|---|---|---|
| push | 从尾部推入多个参数 | 推入后的数组长度 |
| unshift | 从头部推入多个参数 | 推入后的数组长度 |
| pop | 从尾部弹出一个参数 | 弹出的参数 |
| shift | 从头部弹出的一个参数 | 弹出的参数 |
推入的push、unshift都是接收多个参数,且返回推入之后的数组长度
弹出的pop、shift都是没有参数,直接弹出最后一个或者第一个元素;返回的是弹出的那个元素
重排序方法
数组调用reverse(), 直接翻转了数组;而且原数组也被改变,它不是一个纯函数
返回的是对翻转后的数组的引用,不是复制了数组,所以原数组也被改变了
var arr = [1, 2, 3, 8, 9, 10, 12, 21, 30];
// [30, 21, 12, 10, 9, 8, 3, 2, 1] [30, 21, 12, 10, 9, 8, 3, 2, 1]
// reverse 改变了原数组
console.log(arr.reverse(), arr);
数组的sort() 是对数组每一项都调用toString(), 再比较字符串的大小;就算是数字数组,也会先把每一项转为字符串,再比较字符串。
sort() 也是返回排序后的数组的引用,就是返回的同一个数组,所以看到原数组也被改变了。
var arr = [1, 2, 3, 8, 9, 10, 12, 21, 30];
// (9) [1, 10, 12, 2, 21, 3, 30, 8, 9]
// (9) [1, 10, 12, 2, 21, 3, 30, 8, 9]
// 数组的sort()把每一项都调用了toString(),再比较字符串大小
// sort() 也改变了原数组,不是一个纯函数
console.log(arr.sort(), arr);
比较函数,
- 如果第一个参数应该位于第二个参数之前,返回一个负数
- 如果两个参数相等,返回0
- 如果第一个参数应该位于第二个参数之后,返回一个正数
var arr = [1, 2, 3, 8, 9, 10, 12, 21, 30];
function sortFn(first, second) {
if (first < second) {
return -1;
} else if (first > second) {
return 1;
} else {
return 0;
}
}
// [1, 2, 3, 8, 9, 10, 12, 21, 30]
// [1, 2, 3, 8, 9, 10, 12, 21, 30]
// 传入函数以后,按照大小来比较
console.log(arr.sort(sortFn), arr);
更为简单的方法
var arr = [1, 2, 3, 8, 9, 10, 12, 21, 30];
function sortFn(first, second) {
return first - second;
}
// [1, 2, 3, 8, 9, 10, 12, 21, 30]
// [1, 2, 3, 8, 9, 10, 12, 21, 30]
// 传入函数以后,按照大小来比较
console.log(arr.sort(sortFn), arr);
操作方法
concat()
先创建当前数组的一个副本,将接收到的参数添加到副本末尾,返回这个新构建的数组
如果concat() 不传入参数,相当于创建了一个当前数组的副本
var arr = ["red", "green"];
// concat() 不传任何参数,相当于把调用数组复制了一个副本,赋给了newArr
var newArr = arr.concat();
console.log(newArr, arr); // ['red', 'green'] ['red', 'green']
如果是多个参数,追加到数组末尾;如果是数组,把数组中的所有元素都追加到新数组
var arr = ["red", "green"];
// 参数可以是单个值,也可以是数组,如果是数组,可以把里面每个元素都是追加到新数组中
var newArr = arr.concat('blue','yellow',['black','white']);
// ['red', 'green', 'blue', 'yellow', 'black', 'white'] ['red', 'green']
console.log(newArr, arr);
slice(start, end)
新建一个副本,返回start开始,到end结束的一个数组,end不包含在内。原数组不变
var arr = ["red", "green", "blue", "yellow"];
// 获取第1,2个元素
var newArr = arr.slice(1, 3);
// ['green', 'blue'] ['red', 'green', 'blue', 'yellow']
console.log(newArr, arr);
splice(start, count, insert1, insert2)
splice(开始位置,删除个数,插入值1,插入值2...)
截取/删除: 返回删除值组成的数组,原数组被改变 splice(start, count)。返回值作为截取数组,元素组是删除后的数组
插入: 第3个参数开始,就是在start的位置上,插入这些元素。返回值永远都是删除的元素组成的数组。只插入,返回值就是空数组。
替换: 第2个参数代表删除几个元素,再从第3个元素开始,依次插入到start位置,替换元素
var arr = ["red", "green", "blue", "yellow"];
// 从1位开始,删除2个元素; 返回删除的元素组成的数组
var newArr = arr.splice(1, 2);
// ['green', 'blue'] ['red', 'yellow']
console.log(newArr, arr);
var arr = ["red", "green", "blue", "yellow"];
// 从第1位开始,删除0个元素,并插入两个元素
var newArr = arr.splice(1, 0, "pink", "skyBlue");
// ['red', 'pink', 'skyBlue', 'green', 'blue', 'yellow']
console.log(newArr, arr);
var arr = ["red", "green", "blue", "yellow"];
// 删除2位,再插入2个元素
var newArr = arr.splice(1, 2, "pink", "skyBlue");
// ['green', 'blue'] ['red', 'pink', 'skyBlue', 'yellow']
console.log(newArr, arr);
位置方法
indexOf(value): 查找value在数组中的位置,找不到返回-1
迭代方法
every()、some()、map()、filter()、forEach()
参数一:函数;参数二:运行该函数的作用域对象
归并方法
reduce()和reduceRight()
- 参数1: 函数(前一个值,当前值,项的索引,数组对象)
- 参数2: 初始值
不给初始值,默认是省去了第一次执行,直接把结果作为第2次(inde=1)的prev项
var arr = [1, 2, 3, 4, 5];
var resValue = arr.reduce((prev, cur, index) => {
// 1 2 1 第2次, prev = 1, cur = 2, 相加 = 3
// 3 3 2 第3次, prev = 3, cur = 3, 相加 = 6
// 6 4 3 第4次, prev = 6, cur = 4, 相加 = 10
// 10 5 4 第5次, prev = 10, cur = 5, 相加 = 15
// 15
console.log(prev, cur, index);
return prev + cur;
});
console.log(resValue);
给初始值,从index=0开始。第一轮prev为初始值,cur位第一项。
var arr = [1, 2, 3, 4, 5];
var resValue = arr.reduce((prev, cur, index) => {
// 0 1 0 第1次, prev = 0, cur = 1, 相加 = 1
// 1 2 1 第2次, prev = 1, cur = 2, 相加 = 3
// 3 3 2 第3次, prev = 3, cur = 3, 相加 = 6
// 6 4 3 第4次, prev = 6, cur = 4, 相加 = 10
// 10 5 4 第5次, prev = 10, cur = 5, 相加 = 15
// 15
console.log(prev, cur, index);
return prev + cur;
}, 0);
console.log(resValue);
var arr = [1, 2, 3, 4, 5];
var resValue = arr.reduce((prev, cur, index) => {
// 10 1 0 第1次, prev = 10, cur = 1, 相加 = 11
// 11 2 1 第2次, prev = 11, cur = 2, 相加 = 13
// 13 3 2 第3次, prev = 13, cur = 3, 相加 = 16
// 16 4 3 第4次, prev = 16, cur = 4, 相加 = 20
// 20 5 4 第5次, prev = 20, cur = 5, 相加 = 25
// 25
console.log(prev, cur, index);
return prev + cur;
}, 10);
console.log(resValue);
Date类型
console.log(
new Date(), // Wed Nov 20 2024 00:03:55 GMT+0800 (中国标准时间) 当前日期和时间
Date.parse(new Date()), // 1732032235000 日期毫秒数
Date.UTC(2024, 10, 20) , // 1732060800000 接收年 月 日
Date.now(), // 1732032459944 当前时间
(new Date()).valueOf() // 1732032540448 也是当前时间的毫秒
);
要获取当前时间的时间戳,Date.now()和(new Date()).valueOf()
Function类型
函数是Function类型的实例对象
函数名是指针,指向函数对象
函数没有重载,是因为函数名就是一个指针,当给这个指针赋值一个函数对象后,指针指向的是函数对象;当再次给这个名字的指针赋值另一个函数对象后,相当于这个指针指向了后面的函数对象。所以也就没有重载。
函数声明与函数表达式
函数声明会有函数声明提升(function declaration hoisting),就是把函数声明提到全局作用域的顶部,所以使用在前,声明在后,也能成功调用。
函数表达式如果用var声明,其实只会提升函数变量名到作用域顶部,初始化部分不会提到上面去。所以不能在函数表达式之前使用函数。
作为值的函数
- 函数作为另一个函数的参数
- 函数作为一个函数的返回值
var arr = [
{
name: "Alice",
age: 28,
},
{
name: "Jack",
age: 19,
},
{
name: "Cindy",
age: 25,
},
];
// 根据传入的属性名,返回一个排序函数
function makeSortByParams(paramName) {
return function (obj1, obj2) {
// 比较对象的属性,小的排在前面
if (obj1[paramName] < obj2[paramName]) {
return -1;
} else if (obj1[paramName] > obj2[paramName]) {
return 1;
} else {
return 0;
}
};
}
var sortByName = makeSortByParams("name");
var sortByAge = makeSortByParams("age");
// console.log(arr.sort(sortByName));
console.log(arr.sort(sortByAge));
以上函数作为了返回值,而且该返回的函数用到了外层函数中的局部变量。导致外层函数执行结束后,弹栈并清空外层函数的变量对象及其变量和函数时,因为内层函数对paramName还有引用,所以这个变量不会被销毁。这就形成了闭包。
函数内部属性
arguments和this
arguments.callee 指向拥有这个arguments对象的函数,也就是 函数名
function factorial(num) {
if (num === 1) {
return 1;
}
// arguments.callee 指向拥有这个arguments对象的函数,也就是 factorial
return num * arguments.callee(num - 1);
}
console.log(factorial(5)); // 120
函数属性和方法
length: 形参个数
apply(运行函数的作用域,参数数组):参数可以是数组,也可以是arguments对象
call 和apply一样,只是从第二个参数开始,都是一个个参数,而非数组
function sum(num1, num2) {
return num1 + num2;
}
function callSum1(num1, num2) {
// this是window对象; 参数是arguments
return sum.apply(this, arguments);
}
function callSum2(num1, num2) {
// this是window对象;参数是数组
return sum.apply(this, [num1, num2]);
}
console.log(
callSum1(10, 20), // 30
callSum1(10, 20) // 30
);
基本包装类型
var s1 = 'hello'
var s2 = s1.substring(2) // llo
console.log(s2);
字符串是基本类型变量,照理应该不能调用方法和属性。其实第二行访问s1时,访问过程属于读取模式,也就是要从内存中读取这个字符串的值。读取模式中访问字符串,后台都会进行三个步骤
- 创建String类型的一个实例
- 在实例上调用指定的方法
- 销毁这个实例
var s1 = new String("hello"); // 创建String类型的实例
s1.substring(2); // 用实例对象调用指定的方法
s1 = null; // 销毁这个String实例对象,空对象用null
引用类型和基本包装类型的主要区别是对象的生存期。引用实例是在当前作用域销毁之前都会保存在内存中,而基本包装类型,只存在于一行代码的执行瞬间。
var s1 = 'hello'
s1.color = 'red' // 读取模式,创建了String基本包装类型,String实例对象创建了color属性以后,立即销毁了这个实例
console.log(s1.color); // undefined, 又进入读取模式,又新建了String实例对象。这个实例对象上没有color属性
Object()构造函数,会根据传入值的类型,返回相应基本包装类型的实例
String类型
1. 字符方法
charAt(val): 返回指定位置的字符
charCodeAt(val): 返回指定位置的字符编码
var str = "hello";
console.log(
str.charAt(1), // e
str.charCodeAt(1) // 101
);
2. 字符串操作方法
concat(val1,va2...):字符串拼接,返回新的字符串
slice(start, end) 返回start到end的新字符串,end不包括
substring(start, end) 返回start到end的新字符串,end不包括
substr(start, count) 返回start开始,count个字符的字符串
var str = "hello";
console.log(
str.slice(1, 3), // el
str.substring(1, 3), // el
str.substr(1, 3) // ell
);
3. 字符串操作方法
indexOf(字符串):查询搜索字符串在原本字符串中的位置
单体内置对象
URI编码方法
encodeURI() 主要用于整个URI,不会对本身属于URI的特殊字符进行编码,如冒号、正斜杠、问号和井字号。
encodeURIComponent() 用于URI中的某一段,如查询字符串。对所有非字母数字的字符进行编码。
var uri = "https://e2.aliyun.com/go/portal/opportunities?keywords=螺纹钢";
// https://e2.aliyun.com/go/portal/opportunities?keywords=%E8%9E%BA%E7%BA%B9%E9%92%A2
// 保留了 本身属于URI的特殊字符,: / ? =
console.log(encodeURI(uri));
// https%3A%2F%2Fe2.aliyun.com%2Fgo%2Fportal%2Fopportunities%3Fkeywords%3D%E8%9E%BA%E7%BA%B9%E9%92%A2
// 所有非字母数字的字符都被编码了
console.log(encodeURIComponent(uri));