js相关基础

# 类型

# 基本类型

# 基本数据类型

# 【SSBNNU】 S

Symbol(ES6)、String、Boolean、Number、Null、Undefined; 简写:SSBNNU 栈(stack)

这些数据可以分为原始数据类型和引用数据类型:

  • 栈:原始数据类型(Undefined、Null、Boolean、Number、String)
  • 堆:引用数据类型(对象、数组和函数)

两种类型的区别是:存储位置不同。【S栈,引堆】

  • 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
  • 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体

在操作系统中,内存被分为栈区和堆区:

  • 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
  • 堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。

# 内置对象

Object 是 JavaScript 中所有对象的父对象

数据封装类对象:Object、Array、Boolean、Number 和 String

其他对象:Function、Arguments、Math、Date、RegExp、Error

# 判断方式对比

# typeof

typeof返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function等7种数据类型,*但不能判断null、array、object等,*但是 typeof null === 'object';

# typeof NaN 的结果是什么?

NaN 意指“不是一个数字”(not a number),NaN 是一个“警戒值”(sentinel value,有特殊用途的常规值),用于指出 数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。

typeof NaN; // "number"
console.log(typeof(NaN));//number
console.log(typeof(undefined));//undefined
console.log(typeof(null));//object
1
2
3
4

NaN 是一个特殊值,它和自身不相等,是唯一一个非自反(自反,reflexive,即 x === x 不成立)的值。而 NaN != NaN 为 true。

+0 === -0 //true           NaN === NaN//false    
Object.is(+0, -0) //false  Object.is(NaN, NaN) //true
1
2

# instanceof

instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性但它不能检测null 和 undefined; 基础类型没有 __proto__

# constructor

**constructor作用和instanceof非常相似。**但constructor检测 Object与instanceof不一样,还可以处理基本数据类型的检测但它不能检测null 和 undefined

const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象

# Object.prototype.toString.call()

最准确最常用的方式;toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。

对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。

及ES6新加入的方法Array.isArray():检测某个值是否为数组(ES6)

# 封装
//定义检测数据类型的功能函数 
function checkedType(target) { 
	return Object.prototype.toString.call(target).slice(8, -1) //获取从第九个到倒数第二个字符
}//targetType ==='Object'; 比如[object String]获取 String
1
2
3
4
# 不封装
function isObject (obj: any): Boolean {// 借鉴 Vue 源码的 object 检测方法
  //typeof null === 'object';null 是基础类型,不是 Object,故要先排除这种情况;
  return obj !== null && typeof obj === 'object'
}
//验证是否是对象或数组(前提是toString()方法没有被重写过)
function isArray(value){//安全类型检测
  return Object.prototype.toString.call(value) == "[object Array]";
}
function isFunction(value){// 注:在ie中在以COM对象形式实现的任何函数,isFunction()都将返回false
  return Object.prototype.toString.call(value) == "[object Function]";
}
1
2
3
4
5
6
7
8
9
10
11

# ===

===能用于判断null和undefined,因为这两种类型的值都是唯一的

undefined === null; // false,类型不相同
undefined !== null;  // true, 类型不相同
1
2
+0 === -0 //true           NaN === NaN//false    
Object.is(+0, -0) //false  Object.is(NaN, NaN) //true
1
2

# 比较实践

# typeof
console.log(typeof 2);               // number
console.log(typeof true);            // boolean
console.log(typeof 'str');           // string
console.log(typeof []);              // object【特殊】
console.log(typeof function(){});    // function
console.log(typeof {});              // object【特殊】    
console.log(typeof undefined);       // undefined【特殊】
console.log(typeof null);            // object【特殊】

# instanceof 只能正确判断引用数据类型,而不能判断基本数据类型
console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false 
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);     				 // true

# constructor 可以处理基本数据类型的检测;但它不能检测nullundefined  
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true

# 
var toString = Object.prototype.toString;
console.log(toString.call(2));                      //[object Number]
console.log(toString.call(true));                   //[object Boolean]
console.log(toString.call('str'));                  //[object String]
console.log(toString.call([]));                     //[object Array]
console.log(toString.call(function(){}));           //[object Function]
console.log(toString.call({}));                     //[object Object]
console.log(toString.call(undefined));              //[object Undefined]
console.log(toString.call(null));                   //[object Null]
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(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 原理分析

# instanceOf原理

其内部的机制是通过判断在其原型链中能否找到该类型的原型

基础类型没有 __proto__

f 的隐式原型 __proto__Foo.prototype ,是相等的,所以返回 true

console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true
1
2
3
// 实现 instanceof
function instanceof(obj, target) {
 // 验证如果为基本数据类型,就直接返回 false
  const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol']
  if(baseType.includes(typeof(obj))) { return false }

  obj = obj.__proto__// 获得对象的原型
  while (true) {// 判断对象的类型是否等于类型的原型
    // 如果__proto__ === null 说明原型链遍历完毕
    if (obj === null) return false
    // 如果存在 obj.__proto__ === target.prototype;说明对象是该类型的实例
    if (obj === target.prototype) return true
    obj = obj.__proto__// 原型链上查找
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 如果用 instanceof 判断基础类型会怎么样?

let str = '123';
console.log(str instanceof String) // -> false; 因为基础类型没有 `__proto__`
1
2

但是如果更改了实现检查的话; 静态方法Symbol.hasInstance就可以判断

class StringType {
  static [Symbol.hasInstance](val) {
    return typeof val === 'string'
  }
}
console.log(str instanceof StringType) // -> true
1
2
3
4
5
6

# 引入原型链相关

# 应用

# 对象的深拷贝操作比较

# 简化版本比较
function simpleClone(obj) {//浅拷贝
  let newObj = {};
  for (let i in obj) {
    newObj[i] = obj[i];
  }
  return newObj;
}
//方式一:直接用instancof/typeof判断;【推荐】简洁;
function deepClone(obj){
  var newObj= obj instanceof Array?[]:{};
  for(var i in obj){
    newObj[i]=typeof obj[i]=='object'? deepClone(obj[i]):obj[i]; 
  }
  return newObj;
} 
function deepClone(obj) {//第二种方式;跟方式一类似;
  let result;
  if (typeof obj == 'object') {
    result = isArray(obj) ? [] : {}
    for (let i in obj) {
      result[i] = isObject(obj[i])||isArray(obj[i])?deepClone(obj[i]):obj[i]
    }
  } else {
    result = obj
  }
  return result
}
function isObject(obj) {
  return Object.prototype.toString.call(obj) == "[object Object]"
}
function isArray(obj) {
  return Object.prototype.toString.call(obj) == "[object Array]"
}
//定义检测数据类型的功能函数 
function checkedType(target) { 
	return Object.prototype.toString.call(target).slice(8, -1) //获取从第九个到倒数第二个字符
}//targetType ==='Object'; 比如[object String]获取 String
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 类型转换

# =====转换过程

# ==类型转换过程
  1. 如果类型不同,进行类型转换
  2. 判断比较的是否是 null 或者是 undefined, 如果是, 返回 true .
  3. 判断两者类型是否为 string 和 number, 如果是, 将字符串转换成 number
  4. 判断其中一方是否为 boolean, 如果是, 将 boolean 转为 number 再进行判断
  5. 判断其中一方是否为 object 且另一方为 string、number 或者 symbol , 如果是, 将 object 转为原始类型再进行判断
# == 操作符的强制类型转换规则
  • 字符串和数字之间的相等比较,将字符串转换为数字之后再进行比较。
  • 其他类型和布尔类型之间的相等比较,先将布尔值转换为数字后,再应用其他规则进行比较。
  • null 和 undefined 之间的相等比较,结果为真。其他值和它们进行比较都返回假值。
  • 对象和非对象之间的相等比较,对象先调用 ToPrimitive 抽象操作后,再进行比较。
  • 如果一个操作值为 NaN ,则相等比较返回 false( NaN 本身也不等于 NaN )。
  • 如果两个操作值都是对象,则比较它们是不是指向同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true,否则,返回 false。
# ===== 及Object.is()的区别

== : 只进行值的比较,会进行数据类型的转换。 === : 不仅进行值得比较,不进行转换。还要进行数据类型的比较。

===== 的区别在于: == 运算发生的隐式类型转换,而 === 在执行比较运算前不会发生隐式的类型转换

一言以蔽之==先转换类型再比较,===先判断类型,如果不是同一类型直接为false

{a: 1} == "[object Object]" //true, 左边会执行 .toString()
1

Object.is():用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致;不同之处只有两个:一是+0不等于-0,二是NaN等于自身。Object.is(v1, v2) 修复了 === 的一些BUG (-0和+0, NaN和NaN)

+0 === -0 //true           NaN === NaN//false    
Object.is(+0, -0) //false  Object.is(NaN, NaN) //true
1
2

# 隐式类型转换

首先要介绍ToPrimitive方法,这是 JavaScript 中每个值隐含的自带的方法,用来将值 (无论是基本类型值还是对象)转换为基本类型值

如果值为基本类型,则直接返回值本身;

如果值为对象,其看起来大概是这样:ToPrimitive(obj,type)type的值为number或者string

(1)当typenumber时规则如下:

  • 调用objvalueOf方法,如果为原始值,则返回,否则下一步;
  • 调用objtoString方法,后续同上;
  • 抛出TypeError 异常。

(2)当typestring时规则如下:

  • 调用objtoString方法,如果为原始值,则返回,否则下一步;
  • 调用objvalueOf方法,后续同上;
  • 抛出TypeError 异常。

可以看出两者的主要区别在于调用toStringvalueOf的先后顺序。默认情况下:

  • 如果对象为 Date 对象,则type默认为string
  • 其他情况下,type默认为number

所有对象都有valueOf方法,valueOf方法对于:如果存在任意原始值,它就默认将对象转换为表示它的原始值。

类型先通过 valueOftoString 进行隐式转换;

let a = {
  value: 0,
  valueOf: function() {
    this.value++;
    return this.value;
  }
};
console.log(a == 1 && a == 2);//true
1
2
3
4
5
6
7
8
# 基础隐式类型转换

JavaScript 中的隐式类型转换主要发生在+、-、*、/以及==、>、<这些运算符之间。而这些运算符只能操作基本类型值,所以在进行这些运算前的第一步就是将两边的值用ToPrimitive转换成基本类型,再进行操作。

以下是基本类型的值在不同操作符的情况下隐式转换的规则 (对于对象,其会被ToPrimitive转换成基本类型,所以最终还是要应用基本类型转换规则):

1:+操作符

+操作符的两边有至少一个string类型变量时,两边的变量都会被隐式转换为字符串;其他情况下两边的变量都会被转换为数字。

1 + '23' // '123'
 1 + false // 1 
 1 + Symbol() // Uncaught TypeError: Cannot convert a Symbol value to a number
 '1' + false // '1false'
 false + true // 1
1
2
3
4
5
# + 操作符什么时候用于字符串的拼接?

根据 ES5 规范 11.6.1 节,

  • 如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+ 将进行拼接操作。
  • 如果其中一个操作数是对象(包括数组),则首先对其调用 ToPrimitive 抽象操作,该抽象操作再调用 [[DefaultValue]],以数字作为上下文。
  • 如果不能转换为字符串,则会将其转换为数字类型来进行计算。

简单来说就是,如果 + 的其中一个操作数是字符串(或者通过以上步骤最终得到字符串),则执行字符串拼接,否则执行数字加法

那么对于除了加法的运算符来说,只要其中一方是数字,那么另一方就会被转为数字

2:-\*\操作符

这三个操作符是为数字操作而设计的,所以操作符两边的变量都会被转换成数字,注意NaN也是一个数字

1 * '23' // 23
 1 * false // 0
 1 / 'aa' // NaN
1
2
3

3:==操作符

操作符两边的值都尽量转成number

3 == true // false, 3 转为number为3,true转为number为1
'0' == false //true, '0'转为number为0,false转为number为0
'0' == 0 //true '0'转为number为0
1
2
3

4:对于<>比较符

如果两边都是字符串,则比较字母表顺序:

'ca' < 'bd' // false
'a' < 'b' // true
1
2

其他情况下,转换为数字再比较:

'12' < 13 // true
false > -1 // true
1
2
# 对象的隐式转换

对象会被ToPrimitive转换为基本类型再进行转换:

示范一:

var a = {}
a > 2 // false
1
2

其对比过程如下:

a.valueOf() // {}, 上面提到过,ToPrimitive默认type为number,所以先valueOf,结果还是个对象,下一步
a.toString() // "[object Object]",现在是一个字符串了
Number(a.toString()) // NaN,根据上面 < 和 > 操作符的规则,要转换成数字
NaN > 2 //false,得出比较结果
1
2
3
4

示范二:

var a = {name:'samy'}
var b = {age: 18}
a + b // "[object Object][object Object]"
1
2
3

运算过程如下:

a.valueOf() // {},上面提到过,ToPrimitive默认type为number,所以先valueOf,结果还是个对象,下一步
a.toString() // "[object Object]"
b.valueOf() // 同理
b.toString() // "[object Object]"
a + b // "[object Object][object Object]"
1
2
3
4
5

# 显示/强制转换

switch case得注意类型,比如type是int或者string类型;通过type = +type转换成int类型;

var b = parseInt("01");
console.log("b=" + b);//b=1
var c = parseInt("09/08/2009");
console.log("c=" + c);//c=9
1
2
3
4
//2+5+'3' //73  //由于2和5是整数,它们将以数字形式相加。因为3是一个字符串,它将与 7 拼接,结果是73。

var y = 1;
if (eval(function f() {})) {
  y += typeof F;
}
console.log(y);//1undefined
//eval(function f(){})返回函数f(){}(为真)。因此,在if语句中,执行typeof f返回undefined,
//因为if语句代码在运行时执行,而if条件中的语句在运行时计算。
1
2
3
4
5
6
7
8
9

# 进制转换

# 规则方法

  • 十进制转其他; toString(2/8/16/32/0x); 当调用参数不是在2-32时,抛出 TypeError 异常。 当调用该方法的对象不是 Number 时抛出 TypeError 异常

    var x=300;
    console.log(x);
    console.log("2进制---"+x.toString(2));//100101100
    console.log("8进制---"+x.toString(8));//454
    console.log("16进制---"+x.toString(16));//12c
    console.log("32进制---"+x.toString(32));//9c
    // console.log("32进制---"+x.toString(11111));//toString() radix argument must be between 2 and 36
    
    1
    2
    3
    4
    5
    6
    7
  • 其他转十进制 ;parseInt(x,2/8/16/32/0x)); 如果字符串参数小于 2 或者大于 36或者第一个字符不能被转换为数字,那么 parseFloat() 会返回 NaN。

    var x='300';
    console.log("2进制转10进制---"+parseInt(x,2));//NaN
    console.log("8进制转10进制---"+parseInt(x,8));//192
    console.log("16进制转10进制---"+parseInt(x,16));//768
    
    1
    2
    3
    4
  • 其他转其;他先用parseInt转成十进制再用toString转到目标进制

    parseInt(num).toString(8)  //十进制转八进制
    parseInt(num).toString(16)   //十进制转十六进制
    parseInt(num,2).toString(8)   //二进制转八进制
    parseInt(num,2).toString(16)  //二进制转十六进制
    parseInt(num,8).toString(2)   //八进制转二进制
    parseInt(num,8).toString(16)  //八进制转十六进制
    parseInt(num,16).toString(2)  //十六进制转二进制
    parseInt(num,16).toString(8)  //十六进制转八进制
    
    parseInt(num,8);   //八进制转十进制
    parseInt(num,16);   //十六进制转十进制
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# ["1", "2", "3"].map(parseInt) 答案是多少

parseInt() 函数能解析一个字符串,并返回一个整数,需要两个参数 (val, radix),其中 radix 【该值介于 2 ~ 36 之间,并且字符串中的数字不能大于radix才能正确返回数字结果值】; 但此处 map 传了 3 个 (element, index, array),

因为二进制里面,没有数字3,导致出现超范围的radix赋值和不合法的进制解析,才会返回NaN 所以["1", "2", "3"].map(parseInt) 答案也就是:[1, NaN, NaN]

 function parseInt(str, radix) {
     return str + '-' + radix;
 };
 var a=["1", "2", "3"];
 a.map(parseInt);  // ["1-0", "2-1", "3-2"] 不能大于radix
1
2
3
4
5
parseInt("10");			//返回 10
parseInt("19",10);		//返回 19 (10+9)
parseInt("11",2);		//返回 3 (2+1)
parseInt("17",8);		//返回 15 (8+7)
parseInt("1f",16);		//返回 31 (16+15)
parseInt("010");		//未定:返回 10 或 8
1
2
3
4
5
6

# 精度相关

# isNaN 和 Number.isNaN 函数的区别?

  • 函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。
  • 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,这种方法对于 NaN 的判断更为准确。

# 为什么 0.1 + 0.2 为什么不等于 0.3

js遵循IEEE 754 双精度版本(64位)标准的语言都有的问题。计算机无法识别十进制,JS会将十进制转换为对应的二进制(二进制即:01)。所以 0.1 在二进制表示为

// (0011) 表示循环
0.1 = 2^-4 * 1.10011(0011)
1
2
console.log(0.1.toString(2));
// -> 0.0001100110011001100110011001100110011001100110011001101
console.log(0.2.toString(2));
// -> 0.001100110011001100110011001100110011001100110011001101
1
2
3
4

原生解决办法:parseFloat((0.1 + 0.2).toFixed(10)),不完全正确;

console.log(0.1 + 0.2); //0.30000000000000004
console.log(parseFloat((0.1 + 0.2).toFixed(10))); //0.3
1
2

另一种方案:把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完成后再进行降级(除以10的n次幂),即:

(0.1*10 + 0.2*10)/10 == 0.3 //true
1
# 解决 JS 的精确度问题
  1. 目前主流的解决方案是 先乘再除; (0.1*10 + 0.2*10)/10 == 0.3 //true
    • 比如精确到小数点后2位;.toFixed(8)
    • 先把需要计算的数字都 乘1000
    • 计算完成后再把结果 除1000
  2. 原生解决办法:parseFloat((0.1 + 0.2).toFixed(10))普通计算
  3. 使用新基础类型 BigInt (兼容性很差)
  4. 用第三方库;大型计算;

ps: toFixed()方法可把Number四舍五入为指定小数位数的数字

# 变量

# let、const、var的区别

区别 var let const
是否有块级作用域 × ✔️ ✔️
是否存在变量提升 ✔️ × ×
是否添加全局属性 ✔️ × ×
能否重复声明变量 ✔️ × ×
是否存在暂时性死区 × ✔️ ✔️
是否必须设置初始值 × × ✔️
能否改变指针指向 ✔️ ×

# const对象的属性可以修改吗

const保证的并不是变量的值不得改动,而是变量指向的那个内存地址不能改动。对于基本类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。

但对于引用类型的数据(主要是对象和数组),变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。

# 变量/函数提升

函数提升就是为了解决相互递归的问题,大体上可以解决像ML语言这样自下而上的顺序问题。 变量提升是人为实现的问题,而函数提升在当初设计时是有目的的

ES6前:js没有块级作用域{}的概念。(有函数作用域、全局作用域、eval作用域) ; var关键字声明变量。无论声明在何处,都会被视为声明在函数的最顶部; ES6后:let和const的出现,js有了块级作用域的概念。 在 ES6 中,letconstvarclassfunction一样也会被提升,只是在进入作用域和被声明之间有一段时间不能访问它们,这段时间是临时死区(TDZ)。报错提示:ReferenceError: aLet is not defined //TDZ区域

下面来看一下变量提升可能会导致的问题:

var tmp = new Date();
function fn(){
	console.log(tmp);
	if(false){
		var tmp = 'hello world';
	}
}
fn();  // undefined
1
2
3
4
5
6
7
8

在这个函数中,原本是要打印出外层的tmp变量,但是因为变量提升的问题,内层定义的tmp被提到函数内部的最顶部,相当于覆盖了外层的tmp,所以打印结果为undefined。

var tmp = 'hello world';
for (var i = 0; i < tmp.length; i++) {
	console.log(tmp[i]);
}
console.log(i); // 11
1
2
3
4
5

由于遍历时定义的i会变量提升成为一个全局变量,在函数结束之后不会被销毁,所以打印出来11。

函数和变量重名的情况下;

var foo = 3;
function hoistFunction() {
    console.log(foo); // function foo() {}
    foo = 5;
    console.log(foo); // 5
    function foo() {}
}
hoistFunction();
console.log(foo); // 3

// 预编译之后
var foo = 3;
function hoistFunction() {
   var foo;
   foo = function foo() {};
   console.log(foo); // function foo() {}
   foo = 5;
   console.log(foo); // 5
}
hoistFunction();
console.log(foo);    // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#

# 正则

# 类数组

# 数组

# for循环

  • 简单for
  • 循环for-in(最慢):**循环遍历的是对象的属性,而不是数组的索引。**因此, for-in 遍历的对象便不局限于数组,还可以遍历对象。
  • forEach(最快):没有返回值; a.forEach(function(item, idx, arr){})
  • map有返回值
  • for-of(ES6+):
# 方法及比较
  • forEach/map 不能 break 和 return;

  • for-in 缺点更加明显,它不仅遍历数组中的元素,还会遍历自定义的属性,甚至原型链上的属性都被访问到。而且,遍历数组元素的顺序可能是随机的。 for in更适合遍历对象;

  • for-of优点:推荐

    • 跟 forEach 相比,可以正确响应 break, continue, return。
    • for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM nodelist 对象。
    • 其不仅可以遍历数组,还可以遍历类数组对象和其他可迭代对象。
    • for-of 循环也支持字符串遍历,它将字符串视为一系列 Unicode 字符来进行遍历。
    • for-of 也支持 Map 和 Set (两者均为 ES6 中新增的类型)对象遍历。
# forEach 、for in、 for of各自的特点是什么?
  1. forEach 只能遍历数组,且不能中断(break等无效); 可查看后面forEach内部源码的实现;
  2. for in 遍历的是对象的可枚举属性;结合hasOwnPropery方法可以判断某属性是否是该对象的实例属性;
  3. for of 遍历的是对象的迭代器属性;

记住,for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值

for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name

# 常用方法

栈方法 push() 可以接收任意数量的参数,把他们逐个添加到数组的末尾,返回修改后数组的长度 pop() 从数组末尾移除最后一项,返回移除的项 队列方法 unshift() 向数组前端添加任意个项并返回新数组的长度 shift() 移除数组的第一项并返回该项 排序 sort(compare): compare函数接收两个参数,如果返回负数,则第一个参数位于第二个参数前面;如果返回零,则两个参数相等;如果返回正数,第一个参数位于第二个参数后面; (a,b) => (b-a) // 降序,升序相反 Array.sort();如果不带参数调用sort,数组以字母顺序进行排序升序; 如果数组中包含undefined元素,它们会被排到数组的尾部

操作方法 concat(数组 | 一个或多个元素) ;合并数组,返回新数组 slice(起始位置 ,[结束位置]);切分数组,返回新数组,新数组不包含结束位置的项 splice(起始位置,删除的个数,[插入的元素]) // 删除|插入|替换数组,返回删除的元素组成的数组,会修改原数组 reverse(): 反转数组元素的顺序 Array.join()是Array.split()方法的逆向操作;以指定分隔符连接,如果不指定,默认以,连接; Array.toString(): 返回一个字符串,表示指定的数组及其元素。跟join类似;

# 其他遍历相关方法

some()、every()、filter()、map()、forEach()都是对数组的每一项调用函数进行处理。

– some()、every()的返回值 :true / false

– filter()、map()的返回值 :一个新数组

– forEach()无返回值。当然forEach也可以改变原数组:return

使用some()、every()、filter()、map()、forEach()都不改变原数组

find返回通过指定条件(函数内判断)的数组的第一个元素的值; 不创建新数组

arr.filter()、includes()、find()、findIndex(); indexOf(), lastIndexOf()//方法对大小写敏感

# 归并方法: reduce/reduceRight

对数组中的元素依次处理,将上次结果作为下次处理的输入,最后得到最终结果。接收一个函数作为累加器;

数组只有一项或是个空数组不做遍历操作; 如果归并的数组为空,则会报错;

# 遍历方法类似原理

# foreach的类似实现原理
// if (!Array.prototype.forEach) {
Array.prototype.forEach = function(fn) {
  for (var i = 0; i < this.length; i++) {
    fn(this[i], i, this);
  }
};
// }
["a", "b", "c"].forEach(function(value, index, array) {
  console.log((value, "Is in position " + index + " out of " + (array.length - 1)));
});
1
2
3
4
5
6
7
8
9
10

# 常用算法

# 合并

1.终极篇

[1,2,3,4].concat([5,6]) //[1,2,3,4,5,6]
[...[1,2,3,4],...[4,5]] //[1,2,3,4,5,6]
let arrA = [1, 2], arrB = [3, 4]
Array.prototype.push.apply(arrA, arrB))//arrA值为[1,2,3,4]
1
2
3
4

2.开始篇

let arr=[1,2,3,4];
 [5,6].map(item=>{
   arr.push(item)
 })
 //arr值为[1,2,3,4,5,6],注意不能直接return出来,return后只会返回[5,6]
1
2
3
4
5
# 判断是否包含值

1.终极篇

[1,2,3].includes(4) //false
[1,2,3].indexOf(4) //-1 如果存在换回索引
[1, 2, 3].find((item)=>item===3)) //3 如果数组中无值返回undefined
[1, 2, 3].findIndex((item)=>item===3)) //2 如果数组中无值返回-1
1
2
3
4

includes(),find(),findIndex()是 ES6的api

2.开始篇

[1,2,3].some(item=>{
  return item===3
}) //true 如果不包含返回false
1
2
3
# 最大值
var arr = [1, 2, 3, 4];
console.log(Math.max(...arr)); //4
console.log(Math.max.call(this, 1, 2, 3, 4))
console.log(Math.max.call(this, ...arr)); //4
console.log(Math.max.apply(this, arr)); //4
console.log(
  arr.reduce((prev, cur, curIndex, arr) => {
    return Math.max(prev, cur);
  }, 0)
); //4

//两个功能是一样的;比如:func是Math.max的话;
//return func(..._args);
//return func.apply(null, _args);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Math.max()是Math对象内置的方法,参数是字符串;

# 求两数组中不同元素
const arr1 = [1,2,3,4,5]
const arr2 = [2,3,1,0,5]
const ans = arr1.filter(v => arr2.indexOf(v) == -1 )//[4]

const a = new Set(arr1) const b = new Set(arr2)
new Set([...a, ...b])//并集  
new Set([...a].filter(v => b.has(v)))//交集  
new Set([...a].filter(v => !b.has(v)))//差集
1
2
3
4
5
6
7
8

# 原型链

# 类/TS

# 异步

上次更新: 2022/04/15, 05:41:33
×