一、数据类型
- 基本类型:String、Number、Boolean、Undefined、Null、Symbol、bigInt
- 引用类型:Object、Function
- 基本类型的数据是存放在栈内存中的,而引用类型的数据是存放在堆内存中的
1.基本类型
var num1 = 5;
var num2 = num1;
基本类型的复制就是在栈内存中开辟出了一个新的存储区域用来存储新的变量,这个变量有它自己的值,只不过和前面的值一样,所以如果其中一个的值改变,则不会影响到另一个。
新增类型:bigInt 可用来表示大于 253 - 1 的整数,这原本是 Javascript中可以用_number_来表示的最大数字。
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数BigInt()。
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991); // 9007199254740991n
2.引用类型
var obj1 = new Object();
var obj2 = obj1;
定义一个对象其实是在栈内存中存储了一个指针,这个指针指向堆内存中该对象的存储地址。复制给另一个对象的过程其实是把该对象的地址复制给了另一个对象变量,两个指针都指向同一个对象,所以若其中一个修改了,则另一个也会改变。
3.数据类型判断
-
typeof
typeof 是一个操作符,其右侧跟一个一元表达式,并返回这个表达式的数据类型。返回的结果用该类型的字符串(全小写字母)形式表示,包括以下 7 种:number、boolean、symbol、string、object、undefined、function
typeof 2; // number
typeof undefined; // undefined
typeof '222'; // string
typeof true; // boolean
typeof Symbol(); // symbol
typeof 1n; // bigint
typeof BigInt('1'); // bigint
typeof null; // object
typeof {}; // object
typeof []; // object
typeof new Function(); // function
typeof new Date(); // object
typeof new RegExp(); // object
对于基本类型,除 null 以外,均可以返回正确的结果。
对于引用类型,除 function 以外,一律返回 object 类型。
对于 null ,返回 object 类型。
对于 function 返回 function 类型。
其中,null 有属于自己的数据类型 Null ,引用类型中的 数组、日期、正则 也都有属于自己的具体类型,而 typeof 对于这些类型的处理,只返回了处于其原型链最顶端的 Object 类型。
-
instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。
在这里需要特别注意的是:instanceof 检测的是原型,我们用一段伪代码来模拟其内部执行过程:
instanceof (A, B) = {
var L = A.__proto__;
var R = B.prototype;
if(L === R) {
// A的内部属性 __proto__ 指向 B 的原型对象
return true;
}
return false;
}
当 A 的 __proto__ 指向 B 的 prototype 时,就认为 A 就是 B 的实例
[] instanceof Array; // true
{} instanceof Object; // true
new Date() instanceof Date;// true
function Person(){};
new Person() instanceof Person; // true
[] instanceof Object; // true
newDate() instanceof Object; // true
newPerson instanceof Object; // true
我们发现,虽然 instanceof 能够判断出 [ ] 是Array的实例,但它认为 [ ] 也是Object的实例
[ ].__proto__ 指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype,最终 Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:
从原型链可以看出,[] 的 __proto__ 直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。依次类推,类似的 new Date()、new Person() 也会形成一条对应的原型链 。
因此,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
-
constructor
当一个函数 F被定义时,JS引擎会为F添加 prototype 原型,然后再在 prototype上添加一个 constructor 属性,并让其指向 F 的引用。
当执行 var f = new F() 时,F 被当成了构造函数,f 是F的实例对象,此时 F 原型上的 constructor 传递到了 f 上,因此 f.constructor == F
F 利用原型对象上的 constructor 引用了自身,当 F 作为构造函数来创建对象时,原型上的 constructor 就被遗传到了新创建的对象上, 从原型链角度讲,构造函数 F 就是新对象的类型。
细节问题:
null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object
-
toString
toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx],其中 Xxx 就是对象的类型。
对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
Object.prototype.toString.call(''); // [object String]
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call(Symbol()); // [object Symbol]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(newFunction()); // [object Function]
Object.prototype.toString.call(newDate())=; // [object Date]
Object.prototype.toString.call([]); // [object Array]
Object.prototype.toString.call(newRegExp()); // [object RegExp]
Object.prototype.toString.call(newError()); // [object Error]
二、常用数组遍历方法
1.forEach()遍历数组
let a = [1, 2, 3, 4, 5];
let result = a.forEach((currentValue, currentIndex)=>{
console.log(currentValue, currentIndex);
// 1 0
// 2 1
// 3 2
// 4 3
// 5 4
});
console.log(result); // undefined 没有返回值
console.log(a); // [1, 2, 3, 4, 5]
- 注:forEach中不能使用break和continue关键字
- 注:forEach中可以使用return来实现continue的效果,无法实现break效果。一经调用,就一定会遍历完。
2.every(fn)判断数组中是否所有元素都满足fn函数中的条件
let a = [1, 2, 3, 4, 5];
let result = a.every((currentValue)=>{
return currentValue > 0;
});
console.log(result); // true 所有元素都大于0
result = a.every((currentValue)=>{
return currentValue > 1;
});
console.log(result); // false 1并不大于1
console.log(a); // [1, 2, 3, 4, 5]
3.filter(fn)返回数组中满足fn函数中条件的集合
let a = [1, 2, 3, 4, 5];
let result = a.filter((currentValue)=>{
return currentValue > 4;
});
console.log(result); // [5] 只有5满足条件
console.log(a); // [1, 2, 3, 4, 5]
4.find(fn)返回数组中第一个匹配fn函数中条件的值,没有则返回undefined
let a = [1, 2, 3, 4, 5];
let result = a.find((currentValue)=>{
return currentValue > 3;
});
console.log(result); // 4
result = a.find((currentValue)=>{
return currentValue > 5;
})
console.log(result); // undefined
console.log(a); // [1, 2, 3, 4, 5]
5.findIndex(fn)返回数组中第一个匹配fn函数中条件的索引,没有则返回-1
let a = [1, 2, 3, 4, 5];
let result = a.findIndex((currentValue)=>{
return currentValue > 3;
});
console.log(result); // 3
result = a.findIndex((currentValue)=>{
return currentValue > 5;
});
console.log(result); // -1
console.log(a); // [1, 2, 3, 4, 5]
6.includes()返回一个布尔值,表示某个数组是否包含给定的值
let a = [1, 2, 3, 4, 5];
let result = a.includes(2);
console.log(result); // true
result = a.includes(6);
console.log(result); // false
console.log(a); // [1, 2, 3, 4, 5]
7.map(fn)以fn函数中返回值组成新的数组返回
let a = [1, 2, 3, 4, 5];
let result = a.map((v, i)=>{
return 9;
});
console.log(result); // [9, 9, 9, 9, 9]
console.log(a); // [1, 2, 3, 4, 5]
8.reduce()累计器
let a = [1, 2, 3, 4, 5];
let result = a.reduce((accumulator, currentValue, currentIndex, array)=>{
console.log(accumulator, currentValue, currentIndex, array);
return accumulator + currentValue;
// 5 1 0 [1, 2, 3, 4, 5] 第一次accumulator的值为reduce第二个参数5, currentValue为数组第一个元素
// 6 2 1 [1, 2, 3, 4, 5] 第二次accumulator的值为5加上数组a中的第一个值,即是第一次循环时return的值
// 8 3 2 [1, 2, 3, 4, 5] 同上
// 11 4 3 [1, 2, 3, 4, 5] 同上
// 15 5 4 [1, 2, 3, 4, 5] 同上
}, 5);
console.log(result); // 20 为最终累计的和
// 无初始值时,accumulator的初始值为数组的第一个元素,currentValue为数组第二个元素
result = a.reduce((accumulator, currentValue, currentIndex, array)=>{
console.log(accumulator, currentValue, currentIndex, array);
return accumulator + currentValue;
// 1 2 1 [1, 2, 3, 4, 5]
// 3 3 2 [1, 2, 3, 4, 5]
// 6 4 3 [1, 2, 3, 4, 5]
// 10 5 4 [1, 2, 3, 4, 5]
})
console.log(result); // 15 为最终累计的和
console.log(a); // [1, 2, 3, 4, 5]
9.some(fn)检查数组中是否含有满足fn函数条件的值
let a = [1, 2, 3, 4, 5];
let result = a.some((currentValue)=>{
return currentValue > 2;
});
console.log(result); // true
result = a.some((currentValue)=>{
return currentValue > 6;
});
console.log(result); // false
console.log(a); // [1, 2, 3, 4, 5]
- 注:以上方法均不改变原数组
- 注:find(), findIndex(), includes()均为es6语法,ie不支持
三、常用对象遍历方法
1.for-in
for-in会遍历出原型链上的属性,可以通过hasOwnProperty方法来实现过滤
Object.prototype.sexy = '男';
let obj = {
id: 1,
name: "zhangsan",
age: 18
};
for(let key in obj){
console.log(key + '---' + obj[key]);
}
// 输出结果:
// id---1
// name---zhangsan
// age---18
// sexy---男
for(let key in obj){
if (obj.hasOwnProperty(key)) {
console.log(key + '---' + obj[key]);
}
}
// 输出结果:
// id---1
// name---zhangsan
// age---18
2.Object.keys(obj)、Object.values(obj)
该方法返回对象自身属性名、属性值组成的数组,它会自动过滤掉原型链上的属性,然后可以通过数组的 forEach() 方法来遍历
let obj = {
id: 1,
name: "zhangsan",
age: 18
};
console.log(Object.keys(obj));
// 输出结果: obj对象的key组成的数组['id','name','age']
console.log(Object.values(obj));
// 输出结果: obj对象的value组成的数组['1','zhangsan','18']
Object.keys(obj).forEach((key)=>{
console.log(key+ '---'+obj[key]);
});
// 输出结果:
// id---1
// name---zhangsan
// age---18
3.Object.getOwnPropertyNames(obj)
该方法返回对象自身属性名组成的数组,包括不可枚举的属性
let obj = {
id: 1,
name: "zhangsan",
age: 18
};
Object.getOwnPropertyNames(obj).forEach((key)=>{
console.log(key+ '---'+obj[key]);
});
// 输出结果:
// id---1
// name---zhangsan
// age---18
四、常用字符串操作方法
-
charAt()
返回给定位置的那个字符
var stringValue = 'hello world';
console.log(stringValue.charAt(1)); // 'e'
-
concat()
将一或多个字符串拼接起来,返回拼接得到的新的字符串
var stringValue = 'hello ';
var resrult = stringValue.concat('world');
console.log(resrult); // 'hello world'
console.log(stringValue); // 'hello ' 不改变源字符串
-
replace()、replaceAll()
替换指定字符串
var stringValue = 'cat,bat,sat,fat';
var result = stringValue.replace('at', 'ond');
console.log(result); // 'cond,bat,sat,fat'
var result = stringValue.replaceAll('at', 'ond');
console.log(result); // 'cond,bond,sond,fond'
result = stringValue.replace(/at/g,'ond');
console.log(result); // 'cond,bond,sond,fond'
console.log(stringValue); // 'cat,bat,sat,fat' 不改变源字符串
- slice()、substring()、substr()
提取字符串的片断,并在新的字符串中返回被提取的部分
substring()方法返回一个索引和另一个索引之间的字符串。语法如下:
str.substring(indexStart, [indexEnd])
注意要点:
substring()提取的字符包含 indexStart 但不包括 indexEnd。
如果indexStart 等于indexEnd,返回一个空字符串。
如果indexEnd省略,则将字符提取到字符串的末尾。
如果任一参数小于0或是NaN,它被视为为0。
如果任何一个参数都大于str.length,则被视为是str.length。
如果indexStart大于indexEnd,则返回交换两者的值之后的结果
var str = 'abcdefghij';
console.log('(1, 2): ' + str.substring(1, 2)); // '(1, 2): b'
console.log('(1, 1): ' + str.substring(1, 1)); // '(1, 1): '
console.log('(-3, 2): ' + str.substring(-3, 2)); // '(-3, 2): ab'
console.log('(-3): ' + str.substring(-3)); // '(-3): abcdefghij'
console.log('(1): ' + str.substring(1)); // '(1): bcdefghij'
console.log('(-20, 2): ' + str.substring(-20, 2)); // '(-20, 2): ab'
console.log('(2, 20): ' + str.substring(2, 20)); // '(2, 20): cdefghij'
console.log('(20, 2): ' + str.substring(20, 2)); // '(20, 2): cdefghij'
console.log(str ); // 'abcdefghij' 不改变源字符串
substr()方法返回从指定位置开始的字符串中指定字符数的字符。语法如下:
str.substr(start, [length])
注意要点:
substr()会从start获取长度为length字符(如果截取到字符串的末尾,则会停止截取)。
如果start是正的并且大于或等于字符串的长度,则返回一个空字符串。
若start为负数,则将该值加上字符串长度后再进行计算(如果加上字符串的长度后还是负数,则从0开始截取)。
如果length为0或为负数,返回一个空字符串。如果length省略,则将字符提取到字符串的末尾。
var str = 'abcdefghij';
console.log('(1, 2): ' + str.substr(1, 2)); // '(1, 2): bc'
console.log('(-3, 2): ' + str.substr(-3, 2)); // '(-3, 2): hi'
console.log('(-3): ' + str.substr(-3)); // '(-3): hij'
console.log('(1): ' + str.substr(1)); // '(1): bcdefghij'
console.log('(-20, 2): ' + str.substr(-20, 2)); // '(-20, 2): ab'
console.log('(20, 2): ' + str.substr(20, 2)); // '(20, 2): '
console.log(str ); // 'abcdefghij' 不改变源字符串
slice()方法返回一个索引和另一个索引之间的字符串,语法如下:
str.slice(beginIndex[, endIndex])
注意要点:
若beginIndex为负数,则将该值加上字符串长度后再进行计算(如果加上字符串的长度后还是负数,则从0开始截取)。
如果beginIndex大于或等于字符串的长度,则slice()返回一个空字符串。
如果endIndex省略,则将slice()字符提取到字符串的末尾。如果为负,它被视为strLength + endIndex其中strLength是字符串的长度。
var str = 'abcdefghij';
console.log('(1, 2): ' + str.slice(1, 2)); // '(1, 2): b'
console.log('(-3, 2): ' + str.slice(-3, 2)); // '(-3, 2): '
console.log('(-3, 9): ' + str.slice(-3, 9)); // '(-3, 9): hi'
console.log('(-3): ' + str.slice(-3)); // '(-3): hij'
console.log('(-3,-1): ' + str.slice(-3,-1)); // '(-3,-1): hi'
console.log('(0,-1): ' + str.slice(0,-1)); // '(0,-1): abcdefghi'
console.log('(1): ' + str.slice(1)); // '(1): bcdefghij'
console.log('(-20, 2): ' + str.slice(-20, 2)); // '(-20, 2): ab'
console.log('(20): ' + str.slice(20)); // '(20): '
console.log('(20, 2): ' + str.slice(20, 2)); // '(20, 2): '
- split():把字符串分割为子字符串数组
var stringValue = 'hello world';
console.log(stringValue.split(' ')); // ['hello', 'world']
console.log(stringValue.split('')); // ['h','e', 'l', 'l', 'o', '','w', 'o', 'r','l', 'd']
console.log(stringValue); // 'hello world' 不改变源字符串
- trim():移除字符串首尾空白,但不能移除中间的空白
var stringValue = ' hello world ';
console.log(stringValue.trim()); // 'lo world'
console.log(stringValue); // ' hello world ' 不改变源字符串
- indexOf()、lastIndexOf():搜索指定的子字符串,返回子字符串的位置,没有找到则返回-1
var stringValue = 'hello world';
console.log(stringValue.indexOf('o')); //4
console.log(stringValue.lastIndexOf('o')); //7
- toLowerCase():创建原字符串的小写副本
- toUpperCase():创建原字符串的大写副本
五、常用数组操作方法
- join():数组拼接为字符串
var arr = [1,2,3];
console.log(arr.join()); // '1,2,3'
console.log(arr.join('-')); // '1-2-3'
console.log(arr); // [1, 2, 3](原数组不变)
- push()、pop()
- push(): 把里面的内容添加到数组末尾,并返回修改后的长度。
- 移除数组最后一项,返回移除的那个值,减少数组的length。
var arr = ['Lily','lucy','Tom'];
var count = arr.push('Jack','Sean');
console.log(count); // 5
console.log(arr); // ['Lily', 'lucy', 'Tom', 'Jack', 'Sean']
var item = arr.pop();
console.log(item); // Sean
console.log(arr); // ['Lily', 'lucy', 'Tom', 'Jack']
- shift()、unshift()
- shift():把数组的第一个元素从其中删除,并返回第一个元素的值
- unshift():向数组的开头添加一个或更多元素,并返回新的长度
var arr = ['Lily','lucy','Tom'];
var count = arr.unshift('Jack','Sean');
console.log(count); // 5
console.log(arr); //['Jack', 'Sean', 'Lily', 'lucy', 'Tom']
var item = arr.shift();
console.log(item); // Jack
console.log(arr); // ['Sean', 'Lily', 'lucy', 'Tom']
- sort():将数组里的项从小到大排序
var arr1 = ['a', 'd', 'c', 'b'];
console.log(arr1.sort()); // ['a', 'b', 'c', 'd']
sort()方法比较的是字符串,没有按照数值的大小对数字进行排序,要实现这一点,就必须使用一个排序函数
function sortNumber(a,b)
{
return a - b;
}
arr = [13, 24, 51, 3];
console.log(arr.sort()); // [13, 24, 3, 51]
console.log(arr.sort(sortNumber)); // [3, 13, 24, 51]
- reverse():反转数组
var arr = [13, 24, 51, 3];
console.log(arr.reverse()); // [3, 51, 24, 13]
console.log(arr); // [3, 51, 24, 13]
- concat():连接两个或多个数组
var arr = [1,3,5,7];
var arrCopy = arr.concat(9,[11,13]);
console.log(arrCopy); // [1, 3, 5, 7, 9, 11, 13]
console.log(arr); // [1, 3, 5, 7]
- slice(start,end):数组截取
var arr = [1,3,5,7,9,11];
var arrCopy = arr.slice(1);
var arrCopy2 = arr.slice(1,4);
console.log(arrCopy); // [3, 5, 7, 9, 11]
console.log(arrCopy2); // [3, 5, 7]
console.log(arr); // [1, 3, 5, 7, 9, 11]
- splice(index,howmany):删除、插入和替换
- 删除:指定 2 个参数:要删除的第一项的位置和要删除的项数。
书写格式:arr.splice(1, 3) - 插入:可以向指定位置插入任意数量的项,只需提供 个参数:起始位置、 0(要删除的项数)和要插入的项。
书写格式:arr.splice(2, 0, 4, 6) - 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数:起始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。
书写格式:arr.splice(2,0,4,6)
- 删除:指定 2 个参数:要删除的第一项的位置和要删除的项数。
var arr = [1,3,5,7,9,11];
var arrRemoved = arr.splice(0,2);
console.log(arr); // [5, 7, 9, 11]
console.log(arrRemoved); // [1, 3]
var arrRemoved2 = arr.splice(2,0,4,6);
console.log(arr); // [5, 7, 4, 6, 9, 11]
console.log(arrRemoved2); // []
var arrRemoved3 = arr.splice(1,1,2,4);
console.log(arr); // [5, 2, 4, 4, 6, 9, 11]
console.log(arrRemoved3); // [7]