1. JS值类型与引用类型的区别

1. 值类型/简单数据类型: string、number、boolean、undefined、null

值存在栈中 赋值拷贝也是栈数据 修改拷贝数据对原数据没有影响

1
2
3
4
let num1 = 10
let num2 = num1
num2 = 20
console.log(num1, num2) // 10 20

2.引用类型/复杂数据类型: Array、function、Object

栈中存储地址 堆中存储数据 赋值时拷贝的是栈地址 修改拷贝数据(堆)对原数据有影响

1
2
3
4
let num3 = [1,2]
let num4 = num3
num4[0] = 10
console.log(num3, num4)

3. 变量修改 栈与堆的区别

1
2
3
num4 = 10
// 只是修改了栈中数据 但地址对应的数据没有影响
console.log(num3, num4)
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
let num = 10 // 测试
function fn1() {
num = 20
}
fn1()
console.log(num) // 20

// 2.
let arr = [1,2,3]
function fn2(arr) {
arr = 100
}
fn2()
console.log(arr) // [1,2,3]

// 3.
function fn3(arr) {
arr[0] = 100
}
fn3(arr)
console.log(arr) // [100,2,3]

// 4.
function fn4() {
arr[0] = 100
}
fn4()
console.log(arr) // [100,2,3]

2. 字符串方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. split(,) 把字符串转换为数组 和join()相反
let n1 = 'red, blue'
console.log(n1.split(','))
// 2. replace('替换前的字符', '替换后的字符')
let n2 = '蔡徐坤'
console.log(n2.replace('蔡', '嗯'))
// 删除某个字符 把字符替换为空字符
console.log(n2.replace('蔡', ''))
// 应用场景:
// 1. 名字替换为 xx先生
console.log(n2.replace('徐坤', '先生'))
// 2. 敏感词替换
let n3 = '你是傻逼'
console.log(n3.replace('傻逼', '**'))

3. 数组的增删改查方法以及返回值

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
let arr = [1,2,3,4]
1. 新增数组
// .push 在数组最后面添加元素, 返回值是新数组的长度
// .unshift 在数组最前面面添加元素, 返回值是新数组的长度
arr.push(11, 22)
arr.unshift(0)
2. 删除数组
// .pop 删除数据的最后一个元素, 返回值是删除的那个元素
// .shift 删除数据的最前一个元素, 返回值是删除的那个元素
arr.pop()
arr.shift()
3. 查询数组
// .slice(起始下标, 结束下标), 返回 起始下标<=范围下标<结束下标 的数组
// .splice(起始下标, 删除数量, ...删除位置新增的元素)
console.log(arr.slice(1, 3))
console.log(arr.splice(1, 1))
console.log(arr)
4. 数组.join('分隔符') 将数组每一个元素用分隔符拼接成字符串
// 应用场景: 将商品标签 ['自营', '进口'] 转换为 自营|进口
// 应用场景: 将演出歌手 ['周杰伦', '周慧妹'] 转换为 周杰伦$$周慧妹
console.log(arr.join('|'))
5. 数组.reverse 翻转数组(修改原数组)
console.log(arr.reverse(arr))
// 应用场景: 翻转字符串
let a = '蔡徐坤'
console.log(a.split('').reverse('').join(''))
6. .sort 数组排序 按照ASCII编码顺序排序
console.log(arr.sort())
// 两个引用数据类型, 是不比较堆数据的, 只比较栈地址
let obj = [
{name: '华为', age: 10},
{name: '小米', age: 40},
{name: '苹果', age: 30}
]
obj.sort((a, b) => {
return a.age - b.age // 从小到大排序
})
console.log(obj)

4. 数组的遍历方法

1. 数组forEach 是没有return的也没有返回值

  1. 应用场景: 修改数组的每一个元素值
  2. 金典面试题: 如何终止forEach? (try-catch)
1
2
3
4
5
6
7
// forEach 用来遍历数组对象
// 加强版for循环
let arr = ['小米','苹果','华为']
arr.forEach(function (item, index) {
console.log(item) // 数组元素
console.log(index) // 数组下标
})

2. 数组map

  1. return 会新数组返回值
  2. 应用场景: 数组的转换: 根据数组的元素, 得到一个全新的数组
  3. 由于map创建一个新数组, 在没有使用返回的数组情况下调用它是不恰当的:
  4. 应该使用forEach或for…of作为代替
1
2
3
4
5
let arr = [1,2,3]
arr.map(i => {
console.log(i)
console.log({name: i}) // 数组的转换
})

2. 数组filter

  1. return true: 满足筛选条件的 放入新数组中
  2. return true: 不满足筛选条件的 不放入新数组
  3. filter方法自身返回值: 满足条件的新数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 筛选20以上的数组
let n1 = [10, 15, 20, 30]
let fn = n1.filter(function (a, b) {
console.log(a) // 数组元素
console.log(b) // 数组下标
// 筛选20以上的数组
return a >= 20
})
// 2. 筛选完 会创建符合条件的新数组
// 不符合条件则返回空数组
console.log(fn) // [20, 30]

// 3. 箭头函数写法
let say = n1.filter(item => item >= 40)
console.log(say)

3. 数组 indexOf(): 查找元素下标(只能找值类型下标)

4. 数组 findIndex(): 查找元素下标(只能找引用类型下标)

  1. return true: 找到了, 循环结束, 返回当前元素下标
  2. return false: 没找到, 循环继续, 最终返回-1
  3. findIndex方法自身返回值: 元素下标 || -1
  4. 应用场景: 找到对象元素中 元素的下标
1
2
3
4
5
6
7
8
9
let arr = [
{name: '小东', age: 1},
{name: '小刚', age: 2},
{name: '小明', age: 3}
]
let n1 = arr.findIndex((item, index) => {
return item.age == 2
})
console.log(n1) // 1

5. 数组find和findIndex作用是一样的, 返回值是元素本身

  1. 应用场景: 修改对象数组中某一个对象属性
1
2
3
4
5
6
let n2 = arr.find((item, index) => {
return item.age == 2
console.log(item) // 所有对象
})
console.log(n2) // 返回小刚对象
console.log(n2.age *= 0.1) // 0.2

6. 数组reduce方法

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
// reduce累计器 返回累计处理的结果 用于求和..
let n4 = [1,2,3,4]
// reduce(function (上一次值, 当前值){}, 初始值
// 1. 没有初始值
let n5 = n4.reduce(function (a, b) {
return a + b
})
console.log(n5)
// 2. 有初始值
let n6 = n4.reduce(function (a, b) {
return a + b
}, 10) // 初始值
console.log(n6)
// 箭头函数写法
let n7 = n4.reduce((a, b) => a + b, 10)
console.log(n7)
// 3. reduce 执行过程
// 01. 没有初始值
// 上一次值是数组的第一个值
// 每一次循环 就会把返回值给下一次循环的上一次值
let n8 = [1,2,3]
let n9 = n8.reduce((a, b) => a + b)
console.log(n9)
// 上一次值 当前值 返回值 (第一次循环)
// 1 2 3
// 上一次值 当前值 返回值 (第二次循环)
// 3 3 6
// 02. 有初始值 把初始值做为上一次值
let n10 = [1,2]
let n11 = n10.reduce((a, b) => a + b, 10)
console.log(n11)
// 上一次值 当前值 返回值 (第一次循环)
// 10 1 11
// 上一次值 当前值 返回值 (第二次循环)
// 11 13 13

5. 箭头函数和普通函数的区别

  • this指向不同

1. function函数的this指向: 谁调用函数, this就指向谁

  1. 普通函数: fn() this指向window
  2. 对象方法: fn.方法名() this指向对象
  3. 构造函数: new 函数名 this指向实例对象
1
2
3
4
5
6
7
8
9
function fn() {
console.log(this)
}
fn() // window
let obj = {
a: fn
}
obj.a() // 指向obj
new fn() // 指向fn

2. 箭头函数this: 箭头函数没有this 本质是访问上级作用域this

  1. 箭头函数不能作为构造函数(报错)
  2. 箭头函数不能使用上下文修改this(call、apply、bind)
  3. 箭头函数不能使用arguments(报错)
1
2
3
4
let f = () => {
console.log(this)
}
f.call('你好') // 还是window

3. 经典面试题

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
let obj1 = {
name: '小明',
age() { // 等价于 age function() {}
function fn1() {
console.log(this) // 指向window
}
fn1()
let fn2 = () => {
console.log(this) // 指向obj1 fn1跟fn2平级作用域
}
fn2()
},
say: () => { // say是箭头函数 this指向上级window
function fn1() {
console.log(this) // 指向window
}
fn1()
let fn2 = () => {
console.log(this) // 指向window
}
fn2()
}
}
obj1.age()
obj1.say()

6. 原型与原型链

  1. 对象原型__proto__的意义是为对象成员查找机制提供一条路线
  2. 原型链是一个查找规则
  3. 可查找一些属性和方法 沿着一条路走
  4. 先看当前原型对象上面有没有
  5. 果没有再往上一层的原型对象查找
  6. 如果有 就可使用
  7. 往上查找最终找到Object为止(null)
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
// 1. 原型链
function fn() {

}
let n1 = new fn()
// new的实例化对象等于fn.prototype
console.log(n1.__proto__ == fn.prototype)

// 因为Object也是构造函数 那么就有prototype和constructor
// fn.protptype里的__proto__就等于 Object.constructor
// Object.constructor 在指回Obj 所以__proto__指向Object
console.log(fn.prototype.__proto__ == Object.prototype)

// Object的对象有prototype 那么protytype就一定有__proto__
// Object对象最大 所以指向null
console.log(Object.prototype.__proto__) // null

// 2. instanceof运算符用于检测构造函数prototype属性
// 是否出现在某个实例对象的原型对象上
// n1 属于 fn
console.log(n1 instanceof fn) // true
// n1 属于 Object
console.log(n1 instanceof Object) // true
// n1 不属于 Array
console.log(n1 instanceof Array) // false
// 数组 属于 Array
console.log([1,2,3] instanceof Array) // true
// 数组 属于 Object
console.log(Array instanceof Object) // true