this的指向关系以及如何修改this的指向

151 阅读5分钟

函数的三种调用方式(this指向)

  1. 环境对象(this):谁调用我,我就指向谁

  2. 指向:

    • 普通函数----->window (函数名() 指向window)

    • 构造函数----->new 创建实例对象 (new 函数名) this指向new创建的实例对象)

    • 对象方法----->obj对象 (对象名.方法名( ) this指向对象)

      技巧:没点没new就是window,有new是实例,有点是点左边的对象

  3. 上下文调用:修改函数内部的this

    1. 函数名.call()
    2. 函数名.apply()
    3. 函数名.bind()
    4. 函数名.slice() 查询数组,默认情况下不传参这个方法会得到数组本身
    5. Array.from(伪数组) 实际开发中,ES6新增语法用于伪数组转真数组:

函数名.call(修改后的this,形参1,新参2...)

  1. call场景:数据类型检测

  2. typeof 数据 : 检测数据类型

    typeof有两种数据类型无法检测: null,array 都会得到object

  3. 检测数据类型固定格式语法: Object.prototype.toString.call(数据)

函数名.apply(修改后的this,数组或伪数组)

  1. 伪数组 本质是 : 对象

  2. 借助 apply自动遍历数组/伪数组 逐一传参特点

  3. 示范:

     //伪数组 
         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)
    
  4. 比较最大值:

     ​
     //求数组最大值
             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)

  1. bind不会立即执行函数,而是得到一个修改this之后的新函数(一次修改,终生受用)
  2. 细节: 如果你在bind后面传了函数参数,那么参数也会绑定。之后传参无效
  3. band一般用于修改:定时器函数、事件处理函数

call、apply、bind有什么区别

相同

  1. 修改函数的this指向

不同点

  1. 传参方式不同 : call是一 一传参,apply是数组/伪数组传参
  2. 执行机制不同 : 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)
 ​

\