八. 数组
1. 数组的简介
- 数组(Array)
- 数组也是一种复合数据类型, 数组可以存储多个不同类型的数据
- 数组中存储的是有序的数据, 数组中的每一个数据都有唯一的索引
- 可以通过索引来操作获取数据
- 数组中存储的数据叫元素(对象叫属性)
- 索引(index)是一组大于0的整数
- 创建数组
- 通过Array来创建数组, 也可以通过[]来创建数组
-
向数组中添加元素
语法:
数组[索引] = 元素 -
读取数字中的元素
语法
数组[索引]
- 如果读取了一个不存在的元素, 不会报错而是返回undefined -
length
- 获取数组中元素的数量(长度)
- 获取的实际值就是数组的最大索引 + 1
- 向数组最后添加元素:
数组[数组.length0] = 元素 - length是可以修改的
-
- 通过Array来创建数组, 也可以通过[]来创建数组
const arr = new Array()
arr[0] = 10
arr[1] = 22
arr[2] = 44
arr[3] = 88
arr[4] = 99
// 使用数组是, 应该避免非连续数组, 因为它的性能不好
// arr[10] = 99
// const arr2 = [1, 2, 3, 4, 5] // 数组字面量
// console.log(arr[1])
// console.log(arr2)
// console.log(typeof arr) // object
// console.log(arr.length)
arr[arr.length] = 33
arr[arr.length] = 55
arr.length = 10
console.log(arr)
2. 数组的遍历
- 遍历数组
- 遍历数组简单理解, 就是获取到数组中的每一个元素
// 任何类型的值都可以称为数组中的元素
let arr = [1, "hello", true, null, { name: "熏悟空" }, () => { }]
// 正着遍历数组
for(let i = 0; i < arr.length; i++) {
console.log(arr[i])
}
// 倒着遍历数组
for (let i = arr.length -1; i >= 0; i--) {
console.log(arr[i])
}
3. 数组练习
- 定义一个Person类, 类中有两个属性 name和age
- 然后创建几个Person对象, 将其添加到一个数字中
- 遍历数组, 并打印未成年人的信息
- 然后创建几个Person对象, 将其添加到一个数字中
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
const personArr = [
new Person("熏悟空", 18),
new Person("沙悟净", 38),
new Person("猪悟能", 28),
new Person("唐玄奘", 28),
new Person("小白龙", 18),
new Person("红孩儿", 8),
new Person("女儿国国王", 17),
]
for (let i = 0; i < personArr.length; i++) {
console.log(personArr[i])
}
console.log("----")
for (let i = 0; i < personArr.length; i++) {
if (personArr[i].age < 18) {
console.log(personArr[i])
}
}
4. for of语句4
-
for-of语句可以用来遍历可迭代对象
-
语法
for(变量 of 可迭代对象) {
语句
} -
执行流程
- for-of的循环体会执行多次, 数组中有几个元素会执行几次
- 每次执行时都会将一个元素赋值给变量
-
const arr = ["熏悟空", "猪悟能", "沙悟净", "小白龙", "唐玄奘", "女儿国国王"]
for(value of arr) {
console.log(value)
}
for(value of "hello") {
console.log(value)
}
5. 数组方法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array
-
Array.isArray()
- 用来检查一个对象是否为数组
-
at()
- 可以组中的指定根据索引获取数元素
- at可以接收负索引作为参数 -1 就是白骨精 -2 就是红孩儿倒序
-
concat()
- 用来链接两个或多个数组
- 非破坏性方法, 不会影响原数组, 而是返回一个新的数组
// console.log(Array.isArray({ name: "孙悟空" })) // false
// console.log(Array.isArray([1, 2, 3])) // true
const arr = ["孙悟空", "猪八戒", "红孩儿", "白骨精"]
const arr2 = ["蜘蛛精", "玉兔精", "牛魔王", "黑熊精"]
// console.log(arr.at(-1))
const result = arr.concat(arr2, ["白龙马", "宝贝袈裟"], ["悟空, 师傅被妖怪抓走了"])
console.log(result)
6. 数组方法
- indexOf()
-
获取元素在数组第一次出现的索引
-
参数:
- 要查询的元素
- 要查询的起始位置
-
lastIndexOf()
-
获取元素数组最后一次出现的位置
-
返回值
- 找到了返回元素的索引, 没有找到返回-1
-
-
join()
- 将一个数组中的元素链接为一个字符串
- [“小白龙”, “沙悟净”, “唐三藏”, “小白龙”] -> “小白龙”, “沙悟净”, “唐三藏”, “小白龙”
- 参数:
- 指定一个字符串作为连接符
-
slice()
- 用来截取数组(非破坏性方法)
- 参数
1. 截取的起始位置(包含该位置),
2. 截取的结束位置(不包含该位置) - 第二个参数可以省略不写, 如果省略就会截取到最后
- 索引可以是复制
- 如果将两个参数全都省略, 可以对数组进行浅拷贝,(浅复制)
-
let arr = ["孙悟空", "猪悟能", "小白龙", "孙悟空", "小白龙", "沙悟净", "唐三藏", "小白龙"]
// const result = arr.indexOf("小白龙", 3)
// const result = arr.lastIndexOf("小白龙", 3)
// const result = arr.indexOf("至尊宝")
let result = arr.join("$")
arr = ["孙悟空", "猪八戒", "沙和尚", "唐玄奘"]
result = arr.slice(1, -1)
result = arr.slice()
// console.log(arr)
console.log(result)
7. 对象的复制
const arr = ["孙悟空", "猪八戒", "沙和尚"]
// const arr2 = arr // 不是复制
// arr2[0] = "唐僧"
// 如何复制一个对象, 复制必须产生新的对象
// console.log(arr)
// console.log(arr2)
// 当调用slice时, 会产生一个新的数组对象, 从而完成对数组的复制
const arr3 = arr.slice()
arr3[1] = "局八戒"
console.log(arr === arr3) // false
console.log(arr) // 没有修改
console.log(arr3) // 修改了猪八戒为局八戒
8. 深拷贝和浅拷贝
-
浅拷贝 (shallow copy)
- 通常对对象的拷贝都是浅拷贝
- 浅拷贝顾名思义, 只对对象的浅层进行复制(只复制一层)
- 如果对象中存储的数据是原始值, 那么拷贝的深浅不重要
- 浅拷贝只会对对象本身进行复制, 不会复制对象中的属性(或元素)
-
深拷贝 (deep copy)
- 深拷贝不仅复制对象本身, 还要复制对象中的属性(或元素)
- 因为性能问题, 通常情况不太使用深拷贝
- 通常情况下不太使用深拷贝 structuredClone() 专门用来深拷贝的方法
// 创建一个数组
const arr = [{name: "孙悟空"}, {name: "猪八戒"}]
const arr2 = arr.slice() // 浅拷贝
const arr3 = structuredClone(arr) // 专门用来深拷贝的方法
console.log(arr)
console.log(arr3)
9. 对象的复制
const arr = ["孙悟空", "猪八戒", "沙和尚"]
const arr2 = arr.slice()
// console.log(arr === arr2)
- … (展开运算符)
- 可以将一个数组中的元素展开到另一个数组中或者作为函数的参数
- 通过它也可以对数组进行浅拷贝
// const arr3 = [arr[0], arr[1], arr[2]]
const arr3 = [...arr]
// const arr3 = ["唐僧", ...arr, "白骨精"]
// console.log(arr)
// console.log(arr3)
function sum(a, b, c) {
return a + b + c
}
const arr4 = [10, 20, 30]
let result = sum(...arr4)
// console.log(result)
- 对象的复制
- Object.assign(目标对象, 被复制的对象)
- 将被复制对象中的属性复制到目标对象, 并将目标对象返回
- 重复的属性会被覆盖
- 可以使用展开运算符对对象复制
const obj = {name: "孙悟空", age: 18}
const obj2 = {address: "花果山", age: 28, name: "局八戒"}
Object.assign(obj2, obj)
// const obj2 = Object.assign({}, obj)
// console.log(obj2)
const obj3 = {address: "高老庄", name: "局八戒",...obj, age: 28} // 将obj的属性在新对象中展开
console.log(obj3)
- 都是破坏性方法
-
push()
- 向数组的末尾添加一个或者多个元素, 并返回新数组的长度
-
pop()
- 删除并返回数组最后一个元素, 返回值删除的是谁就是谁
-
unshift()
- 向数组的最前面添加一个或者多个元素, 并返回新数组的长度
-
shift()
- 删除并返回数组最前面一个元素, 返回值删除的是谁就是谁
-
splice()
-
可以删除, 插入, 替换数组中的元素
-
参数
- 删除的起始位置(索引)
- 删除的个数(数量)
- 要插入的元素
- 返回值
- 返回被删除的元素
-
reverse()
- 反转数组
-
-
let arr = ["孙悟空", "猪八戒", "沙悟净"]
let result = arr.push("唐僧", "白骨精", "女儿国国王")
// console.log(arr)
arr.pop()
// arr.pop()
// result = arr.pop()
reuslt = arr.unshift("牛魔王")
result = arr.shift()
// console.log(result)
arr = ["孙悟空", "猪八戒", "沙悟净", "唐僧"]
// result = arr.splice(0, 0, "牛魔王", "铁扇公主", "红孩儿")
// console.log(arr)
// console.log(result) // 返回值, 返回被删除的元素, 因为删除的什么都没有, 所以空数组
arr = ["a", "b", "c", "d", "e", "f", "g"]
arr.reverse()
console.log(arr)
11. 数组去重
let arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 4, 5, 5, 6, 7]
// 编写代码, 祛除数组重复的元素
// 分别获取数组中的元素
for (let i = 0; i < arr.length; i++) {
// console.log(arr[i])
// 获取当前值后面的所有值
for (let j = i + 1; j < arr.length; j++) {
// console.log("--", arr[j])
// 判断两个数是否相等
if (arr[i] === arr[j]) {
arr.splice(j, 1)
/*
当arr[i]和arr[j]相同时, 它会自动的删除j位置的元素, 然后j + 1 位置的元素会变成j + 1 位置的元素
而j位置的元素已经比较过了, 不会重复比较, 所以会出现漏比较严重的情况
解决办法, 当删除一个元素, 需要将该位置的元素在比较
*/
j--
}
}
}
console.log(arr)
12. 数组去重
let arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 4, 5, 5, 6, 7]
// 获取数组中的元素
for (let i = 0; i < arr.length; i++) {
let index = arr.indexOf(arr[i], i + 1)
if(index != -1) {
// 出现重复内容
arr.splice(index, 1)
i--
}
}
console.log(arr)
const newArr = []
for(ele of arr) {
if(newArr.indexOf(ele) === -1) {
newArr.push(ele)
}
}
console.log(newArr)
13. 冒泡排序
-
思路一:
-
9,1,3,2,8,0,5,7,6,4
-
比较相邻的两个元素, 然后根据大小来决定是否交换它们的位置
-
这种排序方式, 被称为冒泡排序, 冒泡排序是最慢的排序方式, 数字少还可以凑合用, 不适用于数据量较大的排序
-
-
思路二:
- 取出一个元素, 将其他元素和该元素进行比较, 如果其他元素比该元素小则交换两个元素的位置
- 例子:
- 0 9 3 2 8 1 5 7 6 4
- 0 1 9 3 8 2 5 7 6 4
- 0 1 2 9 8 3 5 7 6 4
- 选择排序
let arr = [9, 1, 3, 2, 8, 0, 5, 7, 6, 4]
// for (let j = 0; j < arr.length - 1; j++) {
// for (let i = 0; i < arr.length - 1; i++) {
// // arr[i]是前面的元素 arr[i+1]是后面的元素
// if (arr[i] > arr[i + 1]) {
// // 大数在前, 小数在后, 需要交换两个数的位置
// let temp = arr[i] // 临时变量用存储arr[i]的值
// arr[i] = arr[i + 1] // arr[i + 1]赋值给arr[i]
// arr[i + 1] = temp // 修改arr[i + 1]的值
// }
// }
// }
// console.log(arr)
14. 选择排序
- 选择排序
- 取出一个元素, 然后将其他元素和该元素进行比较, 如果其他元素比该元素小, 则交换两个元素的位置
for(let i = 0; i < arr.length; i++) {
for(let j = i + 1; j < arr.length; j++) {
if(arr[i] > arr[j]) {
// 交换两个元素位置
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
}
}
console.log(first)
15. 函数
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
const personArr = [
new Person("熏悟空", 18),
new Person("沙悟净", 38),
new Person("猪悟能", 28),
new Person("唐玄奘", 28),
new Person("小白龙", 18),
new Person("红孩儿", 8),
new Person("女儿国国王", 17),
]
// filter()函数用来对数组进行过滤
function filter(arr) {
const newArr = []
for (let i = 0; i < arr.length; i++) {
if (arr[i].age < 18) {
newArr.push(arr[i])
}
}
return newArr
}
let result = filter(personArr)
console.log(result)
问题
目前我们的函数只能过滤出数组中age属性小于18的对象,
我们希望过滤更加灵活:
比如: 过滤数组中大于18的对象
age大于60的对象
age大于n的对象
过滤数组中name为xxx的对象
过滤数组中的偶数
…
一个函数的参数也可以是函数
如果将函数作为参数, 那么我们就称这个函数为回调函数(callback)
function filter(arr, cb) {
const newArr = []
// console.log(cb)
// cb()
for (let i = 0; i < arr.length; i++) {
// arr[i].age >= 18
// arr[i].age > 60
// arr[i].age > n
// arr[i].age === "xxx"
// arr[i] % 2 === 0
if (cb(arr[i])) {
newArr.push(arr[i])
}
}
return newArr
}
function fn(a) {
return a.age < 18
}
let result = filter(personArr, fn)
console.log(result)
16. 高阶函数
- 高阶函数
- 如果一个函数的参数或返回值是函数, 则这个函数就称为高阶函数
- 为什么要将函数作为参数传递? (回调函数有什么作用)
- 将函数作为参数, 意味着可以对另一个函数动态的传递代码
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}
const personArr = [
new Person("熏悟空", 18),
new Person("沙悟净", 38),
new Person("猪悟能", 28),
new Person("唐玄奘", 28),
new Person("小白龙", 18),
new Person("红孩儿", 8),
new Person("女儿国国王", 17),
]
function filter(arr, cb) {
const newArr = []
for (let i = 0; i < arr.length; i++) {
if (cb(arr[i])) {
newArr.push(arr[i])
}
}
return newArr
}
// 我们这种定义回调函数的形式比较少见, 通常回调函数都是匿名函数
function fn(a) {
return a.age < 18
}
result = filter(personArr, a => a.name === "熏悟空")
result = filter(personArr, a => a.age >= 18)
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
result = filter(arr, a => a % 2 === 0)
console.log(result)
17. 高阶函数
- 希望someFn()函数在执行时, 可以记录一条日志
- 在不修改原函数的基础上, 为其增加记录日志的功能
- 可以通过高阶函数, 来动态的生成一个新函数
function someFn() {
console.log(111)
return "hello"
}
function outer(cb) {
let a = 1
return () => {
console.log("记录", a++)
const result = cb()
return result
}
}
let result = outer(someFn)
// console.log(result)
function test() {
console.log("test")
return "test"
}
let newTest = outer(test)
newTest()
18. 函数
-
创建一个函数, 第一次调用时, 打印1, 第二次调用打印二, 以此类推
- 可以利用函数来隐藏不希望被外部访问到的变量
-
闭包:
- 闭包就是能访问到外部函数作用域中变量的函数
- 什么时候使用闭包
- 当我们需要隐藏一些不希望被别人访问的内容时就可以使用闭包
- 构成闭包的要件
- 函数的嵌套
- 内部函数要引用外部函数的变量
- 内部函数要作为返回值返回
function outer() {
let num = 0 // 位于函数作用域中
return () => {
num++
console.log(num)
}
}
const newFn = outer()
19. 闭包的原理
-
函数的外层的作用域, 在函数创建时就已经确定了(词法作用域)
-
和调用的位置无关
-
闭包利用的就是词法作用域
let a = "全局变量a"
function fn() {
console.log(a)
}
function fn2() {
let a = "fn2中的a"
// console.log(a)
fn()
// console.log(a)
}
// fn2()
function fn3() {
let a = "fn3中的a"
function fn4() {
console.log(a)
}
return fn4
}
let result = fn3()
result()
20. 闭包的注意事项
-
闭包的生命周期:
- 闭包在外部函数调用时产生, 外部函数每次调用都会产生全新的闭包
- 在内部函数丢失时销毁(内部函数被垃圾回收了, 闭包才会消失)
-
注意事项:
-
闭包主要用来隐藏一些不希望被外部访问的内容
-
这就意味着闭包需要占用一定的内存空间
-
相较于类来说, 闭包比较浪费内存空间(类可以使用原型而闭包不能)
-
需要执行次数较少时, 使用闭包
-
需要大量创建实例时, 使用类
-
function outer2() {
let num = 0
return () => {
num++
console.log(num)
}
}
let fne = outer2() // 独立闭包
let fnee = outer2() // 独立闭包
fne()
fnee()
21. 递归
-
递归:
- 调用自身的函数我们称为递归
- 递归的作用和循环基本一致
-
1! 1
- 2! 2 x 1 = 2
- 3! 1 x 2 x 3 = 6
- 4! 1 x 2 x 3 x 4 = 24
- …
- 10! 1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10 = xxx
-
如何用递归来解决阶乘
- 5! = 4! x 5
- 4! = 3! x 4
- 3! = 2! x 3
- 2! = 1! x 2
- 1! = 1
- 递归的核心思想就是将一个大的问题拆分成一个个小的问题, 小的问题解决了, 大的问题也就解决了
-
编写递归函数, 一定要包含两个要件:
- 基线条件 – 递归的终止条件
- 递归条件 – 如何对问题进行拆分
- 递归的作用和循环是一致的, 不同点在于, 递归思路比较清晰简洁, 循环的执行性能比较好
- 在开发中一般的问题都用循环解决, 尽量使用循环, 少用递归
- 只在一些使用循环解决比较麻烦的情况场景下, 才使用递归
function jieCheng(num) {
// 创建一个变量用来记录结果
let result = 1
for (let i = 2; i <= num; i++) {
result *= i
}
return result
}
let result = jieCheng(5)
// console.log(result)
function jieCheng2(num) {
// 基线条件
if (num === 1) {
return 1
}
// 递归条件
// num! = (num-1)! x num
return jieCheng2(num - 1) * num
}
result = jieCheng2(10)
console.log(result)
22. 菲波那切数列
// 求斐波那契数列第n个数
function fib(n) {
// 确定基线条件
if(n < 3) {
return 1
}
// 设置递归条件
// 第n个数 = 第n个数-1 + 第n-2
return fib(n - 1) + fib(n - 2)
}
let result = fib(10)
console.log(result)
23. 数组的方法
-
sort()
- sort用来对数组进行排序, (会改变原数组)
- sort默认会将数组升序排列
- 注意: sort默认会按照Unicode编码进行排序, 如果直接通过sort对数字进行排列,
- 可能会得到一个不正确的结果
- 参数:
- 可以传递一个回调函数作为参数, 通过回调函数指定排序的规则
- sort((a, b) => a - b) 升序排列
- sort((a, b) => b - a) 降序排列
-
forEach()
- 用来遍历数组
- 它需要一个回调函数作为参数, 这个回调函数会被调用多次
- 数组中有几个元素, 回调函数就会调用几次
- 每次调用都会将数组中的数据当参数传
- 回调函数中有三个参数:
- element 当前的元素
- index 当前元素的索引
- array 被遍历的数组
-
filter()
- 将数组中符合条件的元素保存到一个新数组中返回
- 也是需要一个回调函数作为参数, 会为每一个元素去调用回调函数 并根据返回值来决定是否将元素添加到新数组中
- 非破坏性方法, 不会影响原数组
- 和上面forEach一样三个回调函数
-
map()
- 根据当前数组生成一个新数组
- 需要回调函数作为参数 和上面forEach一样三个回调函数
- 回调函数的返回值会成为新数组中的元素
- 非破坏性方法, 不会影响原数组
-
reduce()
- 可以用来将一个数组中的所有元素整合为一个值
- 参数:
1. 回调函数, 通过回调函数来指定合并的规则
2. 可选的参数, 初始值
let arr = ["a", "c", "b", "e", "d", "g", "f"]
arr = [3, 2, 1, 9, 0, 4, 5, 6, 7, 8, 10]
// console.log(arr.sort())
arr.sort((a, b) => b - a)
// console.log(arr)
arr = ["孙悟空", "猪八戒", "沙悟净", "唐僧", "小白龙"]
// arr.forEach((element, index, array) => {
// console.log(array)
// })
arr = [1, 2, 3, 4, 5, 6, 7, 8]
// 获取数组中的所有偶数
// 这个和上面也是当前元素
let result = arr.filter(element => element % 2 === 0)
// ele是当前数组 当前数组 * 2 ele * 2就是当前数组的二倍
result = arr.map(ele => ele * 2)
arr = ["孙悟空", "猪八戒", "沙和尚"]
result = arr.map(ele => "<li>" + ele + "</li>")
arr = [1, 2, 3, 4, 5, 6, 7, 8]
result = arr.reduce((a, b) => {
/*
a, b
1, 2,
3, 3,
6, 4,
10, 5,
*/
// console.log(a, b)
return a % b
})
// result = arr.reduce((a, b) => a + b, 10)
/*
a, b
10, 1
*/
console.log(result)
24. 可变参数
- arguments
- arguments是函数中的又一个隐含参数
- arguments是类数组对象(伪数组)
- 和数组类似, 可以通过索引来读取元素, 也可以通过for循环遍历, 但是它不会一个数组元素, 不能调用数组元素
- arguments用来存储函数的实参
- 无论用户是否定义形参, 实参都会存储到argument对象中
- 可以通过该对象直接访问实参
// console.log(arguments[2])
// console.log(Array.isArray(arguments)) // false
// for(let i = 0; i < arguments.length; i++) {
// console.log(arguments[i])
// }
// for(v of arguments) {
// console.log(v)
// }
// 报错 因为不是数组不能调用数组方法
arguments.forEach((ele) => {
console.log(ele)
})
}
// fn(1, 10, 33)
// 定义一个函数, 可以求任意数值的和
function sum() {
// 通过arguments, 可以不受参数数量的限制更加灵活的创建函数
let result = 0
for (let num of arguments) {
result += num
}
return result
}
// sum()
- 可变参数, 在定义函数时可以将参数指定为可变参数
- 可变参数可以接收任意实参, 并将它们统一存储到一个数组中
- 可变参数的作用和arguments的作用基本是一样的, 但是它也有不同点
- 可变参数的名字可以自己指定
- 可变参数就是数组, 可以直接使用数组的方法
- 可变参数可以配合其他参数一起使用
function fn2(...args) {
console.log(args)
}
function sum2(...num) {
return num.reduce((a, b) => a + b, 0)
}
// 当可变参数和其他参数一起使用时, 要将可变参数放在最后
function fn3(a, b, ...args) {
// for(let v of arguments) {
// console.log(v)
// }
console.log(args)
}
fn3(123, 456, 789, "hello", true, false)
25. 函数
- 根据函数调用方式不同this指向不同
- 以函数形式调用, this是window
- 以方法形式调用. this是调用方法的对象
- 构造函数中, this是新建的对象
- 箭头函数没有自己的this, 由外层作用域决定
- 通过call和apply调用的函数, 它们的第一个参数就是函数的this
- 通过bind返回的函数, this由bind第一个参数决定(无法修改)
function fn() {
console.log("函数执行了", this)
}
const obj = {
name: '孙悟空',
fn
}
// obj.fn()
- 调用函数除了函数()这种情况外, 还可以通过其他方式来调用函数
- 比如: 我们可以通过调用函数的call()和apply()两个方法来调用函数
- 函数.call()
- 函数.apply()
- call和apply除了可以调用函数, 还可以用来指定函数中的this
- call和apply的第一个参数将会成为函数的this
- 通过call方法调用函数, 函数的实参直接在第一个参数后面一个一个列出来
- 通过apply方法调用函数, 函数的实参需要用一个数组传递
function sum(a, b, c, d) {
console.log("a = ", a , "b = ", b, this)
}
// sum(123, 456)
// sum.call(obj, 1, 2)
sum.apply(obj, ["hellow", true, 123, "你好"])
26. bind
-
根据函数调用方式不同this指向不同
- 以函数形式调用, this是window
- 以方法形式调用. this是调用方法的对象
- 构造函数中, this是新建的对象
- 箭头函数没有自己的this, 由外层作用域决定
- 通过call和apply调用的函数, 它们的第一个参数就是函数的this
- 通过bind返回的函数, this由bind第一个参数决定(无法修改)
-
bind()是函数的方法, 可以用来创建新的函数
- bind可以为新函数绑定this
- bind也可以为新函数绑定参数
-
箭头函数没有自身的this, 它的this由外层作用域决定,
- 也无法通过call, apply, bind修改它的this
- 箭头函数当中没有arguments
function fn(a, b, c) {
console.log("fn执行了", this)
console.log(a, b, c)
}
const obj = { name: "孙悟空" }
const newFn = fn.bind(obj, 10)
newFn()
const arrowFn = () => {
console.log(arguments)
}
arrowFn() // 不能调用了有问题
// const arr = arrowFn.bind(obj)
// arr()
class Myclass {
fn = () => {
console.log(this)
}
}
const mc = new Myclass()
mc.fn.call(window)