函数的三种调用方式(this指向)
-
环境对象(this):谁调用我,我就指向谁
-
指向:
-
普通函数----->window (函数名() 指向window)
-
构造函数----->new 创建实例对象 (new 函数名) this指向new创建的实例对象)
-
对象方法----->obj对象 (对象名.方法名( ) this指向对象)
技巧:
没点没new就是window,有new是实例,有点是点左边的对象
-
-
上下文调用:修改函数内部的this
- 函数名.call()
- 函数名.apply()
- 函数名.bind()
- 函数名.slice() 查询数组,默认情况下不传参这个方法会得到数组本身
- Array.from(伪数组) 实际开发中,ES6新增语法用于
伪数组转真数组:
函数名.call(修改后的this,形参1,新参2…)
-
call场景:数据类型检测
-
typeof 数据 : 检测数据类型
typeof有两种数据类型无法检测: null,array 都会得到object
-
检测数据类型固定格式语法: Object.prototype.toString.call(数据)
函数名.apply(修改后的this,数组或伪数组)
-
伪数组 本质是 : 对象
-
借助 apply自动遍历数组/伪数组 逐一传参特点
-
示范:
//伪数组 let obj = { 0: 20, 1: 50, 2: 88, 3: 66, length: 4 } console.log(obj) //声明空数组 let newArr = [] newArr.push.apply(newArr,obj) console.log(newArr)
-
比较最大值:
//求数组最大值 let arr = [10, 20, 50, 60, 88, 30] //1.排序法 : 从大到小排序,取0下标 // arr.sort(function(a,b){ // return b-a // }) // console.log(arr[0])//数组从大到小排序,第一个元素就是最大值 //2.擂台法 // let max1 = arr[0] // for(let i = 1;i<arr.length;i++){ // if( arr[i]>max1){ // max1 = arr[i] // } // } // console.log( max1 ) //3.ES5 : Math.max.apply(Math,数组名) //函数名.apply(修改的this, 数组/伪数组 ) //第一个参数 Math : this本来就是Math,这里也不需要修改this (传Math相当于this不变) //第二个参数 arr : 借助apply特点。 自动遍历数组/伪数组。 逐一传参 console.log(Math.max(10, 20, 50, 60, 88, 30)) console.log(Math.max.apply(Math, arr)) //4.ES6(推荐) : ...和apply类似,也会自动遍历数组,然后逐一传参 console.log(Math.max(...arr))
函数名.bind(修改后的this)
- bind不会立即执行函数,而是得到一个修改this之后的新函数(一次修改,终生受用)
- 细节: 如果你在bind后面传了函数参数,那么参数也会绑定。之后传参无效
- band一般用于修改:定时器函数、事件处理函数
call、apply、bind有什么区别
相同
- 修改函数的this指向
不同点
- 传参方式不同 : call是一 一传参,apply是数组/伪数组传参
- 执行机制不同 : call、apply会立即执行函数, bind不会立即执行函数
闭包
闭包(closure):
- 闭包是一个访问
其他函数
内部变量的函数 - 闭包 = 函数 + 上下文引用
闭包作用:
- 解决变量污染
用处
- 一般用于回调函数
button.onclick = function () {
let num = 100
function fn() {
alert(num)
}
fn()
}
递归
递归
- 一个函数在
内部
调用自己
作用
- 递归作用和循环类似,也需要有结束条件
递归应用
-
深拷贝(利用JSON拷贝,拷贝的是数据,不是地址)
//声明一个对象 //拷贝: 把对象中存储的数据 拷贝一份赋值给其他对象 let obj = { name: '张三', age: 18, sex: '男' } //浅拷贝: 拷贝的是地址 地址内的数据改动,数据全部改动 let objs = obj objs.age = '30' console.log(objs) //{name: '李四', age: '30', sex: '男'} console.log(obj) //{name: '李四', age: '30', sex: '男'} //深拷贝: 拷贝的是数据 数据修改,只改单个数据不影响其他 let ob = JSON.parse(JSON.stringify(obj)) ob.name = '李四' console.log(ob) //{name: '李四', age: '30', sex: '男'} console.log(obj) //{name: '张三', age: '30', sex: '男'} /*
let obj = { name:'张三', age:18, hobby:['看书','唱歌','学习'], friend:{ name:'朋友', sex:'男' } } /* (1)遍历obj,把所有的属性添加给newObj (2)如果obj[key]是引用类型(数组、对象),则不能直接拷贝地址 (2.1)数组:给newObj声明一个空数组,然后遍历obj[key],把里面元素添加给newObj[key] (2.2)对象:给newObj声明一个空对象,然后遍历obj[key],把里面元素添加给newObj[key] (3)如果obj[key]不是引用类型,则直接赋值。结束递归 */ function kaobei(newObj,obj){ for(let key in obj){ //判断 obj[key] 是不是数组类型 if( obj[key] instanceof Array ){ //声明一个空数组,然后继续拷贝数组里面的数据 newObj[key] = [] kaobei(newObj[key],obj[key]) }else if(obj[key] instanceof Object){ //声明一个空对象,然后继续拷贝数组里面的数据 newObj[key] = {} kaobei(newObj[key],obj[key]) }else{ newObj[key] = obj[key] } } } //调用深拷贝函数 let newObj = {} kaobei(newObj,obj) //深拷贝:修改拷贝的数据,对原数据没有影响 newObj.hobby[0] = '111' console.log( newObj,obj)
-
遍历dom树
//服务器返回一个不确定的数据结构,涉及到多重数组嵌套
let arr = [
{
type: '电子产品',
data: [
{
type: '手机',
data: ['iPhone手机', '小米手机', '华为手机']
},
{
type: '平板',
data: ['iPad', '平板小米', '平板华为']
},
{
type: '智能手表',
data: []
}
]
},
{
type: '生活家居',
data: [
{
type: '沙发',
data: ['真皮沙发', '布沙发']
},
{
type: '椅子',
data: ['餐椅', '电脑椅', '办公椅', '休闲椅']
},
{
type: '桌子',
data: ['办公桌']
}
]
},
{
type: '零食',
data: [
{
type: '水果',
data: []
},
{
type: '咖啡',
data: ['雀巢咖啡']
}
]
}
]
//arr:数据 father:父盒子
function addElement(arr,father){
//遍历数组,生成div>p添加到父元素中
for(let i = 0;i<arr.length;i++){
//(1)创建空标签
let div = document.createElement('div')
//(2)设置内容
div.innerHTML = `<p>${arr[i].type || arr[i]}</p>`
//(3)添加到父盒子
father.appendChild(div)
//如果菜单还有data,说明还有子菜单,则需要继续遍历添加
if( arr[i].data ){
addElement(arr[i].data,div)
}
}
}
let menu = document.querySelector('.menu')
//调用函数
addElement(arr,menu)