函数this三种指向 函数上下文调用 闭包 递归

153 阅读4分钟

this函数三种指向

环境对象 this : 谁 调用我 我就指向谁

普通函数:函数名() this 指向window

对象方法 对象名.方法名() this指向对象

构造函数 new函数名() this指向new创建实列对象

小技巧:没电没new是window,不是new是实列,有点是点左边的对象

let fn=function(){
        console.log(this)
    }
    //1普通函数
    fn()
    //构造函数
    new fn()
    //对象方法
    let obj={
        name:'张三',
        eat:fn
    }
    obj.eat()

测试

 let obj = {
        name: "张三",
        eat: function() {
          //1级链
          console.log(this) //1.obj
          function fn() {
            //2级链
            console.log(this) //2.window
          }
          fn()
        }
      }

      let eat = obj.eat
      obj.eat()

函数上下文

上下人调用:修改函数内部的this

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

2.2函数名 .applay()

2.3函数名.bind()

call()调用函数

  let fn=function(a,b){
               console.log(this)
               console.log(a+b)
              }
            //   函数名.call(修改的this,参数,参数2...)
            fn.call({name:'张三'},10,20)

call() 伪数组转真数组

   /* 伪数组 : 有数组三要素(下标,元素,长度),但是没有数组的方法
            伪数组本质是对象
         */
          let weiArr={
              0:88,
              1:20,
              2:50,
              3:60,
              length:4
          }
          console.log(weiArr)

        //如果希望伪数组也可以调用数组的方法(排序、拼接),就需要把伪数组转成真数组

        /* 
        1. slice可以查询数组,默认情况下不传参这个方法会得到数组本身
        2. 但是伪数组由于原型不是Array,所以无法调用slice
        3. slice方法存储在哪里? : Array.prototype
        */
       Array.prototype.slice.call(weiArr)
        console.log(weiArr)
     
        //ES6新增语法用于伪数组转真数组 :  Array.from(伪数组)
          let arr=Array.from(weiArr)
          console.log(arr)

call() 万能数据类型检测

        1. typeof 数据 : 有两种数据类型无法检测
            null和数组无法检测,结果都是 'object'

        2. 解决方案:万能数据类型检测
            Object.prototype.toString.call(数据)
         */
      

        //值类型
        let str = 'abc'
        let num = 123
        let bol = true
        let und = undefined
        let nul = null
        //引用类型
        let arr = [10,20,30]
        let fn = function(){}
        let obj = {name:'张三'}

        console.log( typeof str )//'string'
        console.log( typeof num )//'number'
        console.log( typeof bol )//'boolean'
        console.log( typeof und )//'undefined'
        console.log( typeof nul )//'object'
        console.log( typeof arr )//'object'
        console.log( typeof fn )//'function'
        console.log( typeof obj )//'object'


        /* Object.prototype.toString() 返回固定格式字符串 '[object 数据类型]' */
        console.log( Object.prototype.toString.call( str ) )//[object String]
        console.log( Object.prototype.toString.call( num ) )//[object Number]
        console.log( Object.prototype.toString.call( bol ) )//[object Boolean]
        console.log( Object.prototype.toString.call( und ) )//[object Undefined]
        console.log( Object.prototype.toString.call( nul ) )//[object Null]
        console.log( Object.prototype.toString.call( arr ) )//[object Array]
        console.log( Object.prototype.toString.call( fn ) )//[object Function]
        console.log( Object.prototype.toString.call( obj ) )//[object Object]

applay()调用函数

function ab(a,b){
            console.log(a+b)
            console.log(this)
        }
        ab.apply({name:'李四'},[30,40])

applay 伪数组转数组

         let obj={
             0:50,
             0:30,
             0:20,
             0:10,
             0:20,
             length:4
         }
         console.log(obj)
        //  需求 伪数组转真数组
        let arr=[]
        // 思路一把伪数组每一个元素push到数组中
        // arr.push(obj[0],obj[1],obj[2],obj[3])
        // for(let i = 0;i<obj.length;i++){
        //     arr.push(obj[i])
        // }
        // 思路二 使用 apply arr.push.apply(arr,伪数组)
        arr.push.apply(arr,obj)
        console.log(arr)

        //Es6中提供一个更加简洁的静态方法,把伪数组转成真数组
        let arr1=Array.from(obj)
        console.log(arr)

applay 求数组最大值

//1js基础 :擂台思想
     let arr=[20,30,40,50]

     let max=arr[0]
     for(let i = 1;i<arr.length;i++){
         if(arr[i]>max){
           max=arr[i]
         }
     }
     console.log(max)

     //2Math.max()
     let max1=Math.max(arr[0],arr[1],arr[2],arr[3])
     console.log(max1)
     //3Math.max.apply(Math,数组/伪数组)
       let max2=Math.max.apply(Math,arr)
       console.log(max2)
    //    es6中更加简单的方式可以求数组最大值
    // 展开运算符 自动遍历数组传参 (类似apply传参特点)

   let max3=Math.max(...arr)
   console.log(max3)

bind()调用函数

function fn(a,b){
            console.log( a + b )
            console.log( this )
        }

        // 函数名.call(修改后的this,形参1,形参2…………)
        fn.call({name:'张三'},10,20)
        
        // 函数名.apply(修改后的this, 数组或伪数组 )
        // apply会自动帮你遍历数组,然后按照顺序逐一传参
        fn.apply({name:'李四'}, [30,40] )

        //函数名.bind(修改后的this)
        // bind不会立即执行函数,而是返回一个修改this之后的新函数
        let newFn = fn.bind({name:'王五'})
        newFn(100,200)
        newFn(10,20)

bind() 修改定时器

   /*
        定时器中的this : 默认指向window ,要想修改继续用Bing
        +
        */
        // (1)声明函数
        let fn=function(){
          console.log(this)
        }
        //(2)修改fnthis指向
             let newfn=fn.bind({name:'张三'})
            //  (3)定时器
            setTimeout(newfn,2000)


            //这种写法和上面三个步骤等价
            // setTimeout(function(){
            //   console.log(this)
            // }.bind({name:'小齐'}),2000)

call,apply,bind区别

     /* 
        1.环境对象 this : 谁'调用'我,我就指向谁 
            普通函数;  函数名()           this指向window
            对象方法:   对象名.方法名()    this指向对象
            构造函数;  new 函数名()       this指向new创建实例对象

        2.上下文调用 : 修改函数内部的this
            2.1 函数名.call(修改后的this,形参1,形参2…………)
            2.2 函数名.apply(修改后的this, 数组或伪数组 )
            2.3 函数名.bind(修改后的this)
                * 不会立即执行函数,而是得到一个修改this之后的新函数。
                * bind一般用于修改: 定时器函数、事件处理函数

        3. call 和 apply 和 bind 三者区别
            相同点 : 作用一致,修改函数this指向
            不同点 :  
                传参方式不同 : call是按照顺序传参, apply是数组/伪数组传参
                执行机制不同 : call和apply会立即执行函数,而bind不会立即执行而是得到修改this的新函数
        */  

闭包

   /* 
        1.闭包closure是什么 : 
            a. 闭包 是一个 访问其他函数内部变量 的 函数
            b. 闭包 = 函数 + 上下文引用

        2.闭包作用 : 解决变量污染

        3.在浏览器中调试闭包 :
      */

      //局部作用域 :  在函数内部声明

      function fn() {
        let num = 10
        // fn1 + 访问num 组合才叫闭包
        function fn1() {
          console.log(num)
        }
        fn1()
      }

      fn()

闭包案列

 document.querySelector('.btn').onclick=function(){
            //1获取用户搜索框的内容
            let txt =document.querySelector('input').value
            // 2网路请求:不是立即能出结果,网路请求需要时间

            //使用定时器来模式请求
            setTimeout(function(){
                alert(`${txt}的搜索结果如下:123条`)
            },1000)
        }

递归

1递归函数 :一个函数 在内部调用自己

递归函数 功能和循环类似(优先用循环,递归少用)

  function fn(){
            console.log('今天学得很开心')
            fn()
        }

        // fn()

        //双函数递归 : 两个函数互相调用
        function fn1(){
            console.log('哈哈')
        
            fn2()
        }

        function fn2(){
            console.log('呵呵')
            fn1()
        }

        // fn1()

2浅拷贝与深拷贝json实现

1.1浅拷贝 拷贝的是地址 修改的拷贝的数据对原来的数据有影响

1.2深拷贝 :拷贝的数据,修改数据对原来没有影响

let obj = {
            name:'张三',
            age:20,
            sex:'男',
            hobby:['吃饭','睡觉','学习']
        }


        // 浅拷贝: 拷贝地址
        let newObj = obj
        // 修改拷贝后的数据,原数据也会修改
        newObj.name = '李四'
        console.log( obj,newObj)
        
        
        
        
          let obj1={
          name:'张三',
          age:20,
          hobby:['吃饭','睡觉','打豆豆']
      }


    //    js 对象转json
    let jsonStr=JSON.stringify(obj1)
    // json转js
    let newObj=JSON.parse(jsonStr)
    newObj.name='小齐'
    newObj.hobby[0]='游戏'
    console.log(obj1,newObj)

浅拷贝与深拷贝 递归实现

 2.递归应用:
            浅拷贝与深拷贝 : 
                方式一(推荐) : JSON方式实现
                    * let newObj = JSON.parse( JSON.stringify( obj ) )
                方式二(递归) : 了解
            遍历dom树
        */  

         let obj={
              name:'张三',
              age:20,
              sex:'男',
              hobby:['吃饭','睡觉','学习'],
              student:{
                  name:'班长',
                  score:90
              }
         }
        //  使用递归函数
        function kaobei(obj,newObj){
           for(let key in obj){
               if(obj[key] instanceof Array){
                //    声明一个空数组,然后拷贝数据里面的数据
                newObj[key]=[]
                //递归继续调用拷贝数据
                kaobei(obj[key],newObj[key])
                
               }else if(obj[key] instanceof Object){
                  newObj[key]={}
                  //递归继续调用拷贝数据
                  kaobei(obj[key],newObj[key])
               } else{
                   newObj[key]=obj[key]//obj地址拷贝给newobj
               }
           }
        }
        // 创建一个空对象,然后深拷贝
        let newObj={}
        kaobei(obj,newObj)
        newObj.name='李四'
        newObj.hobby[0]='唱歌'
        newObj.student.name='小齐齐'
        console.log(obj,newObj)
        

递归遍历dom书


 <style>
      * {
        padding: 0;
        margin: 0;
      }

      .menu p {
        width: 100px;
        border: 3px solid;
        margin: 5px;
      }

      .menu > div p {
        margin-left: 10px;
        border-color: red;
      }

      .menu > div > div p {
        margin-left: 20px;
        border-color: green;
      }

      .menu > div > div > div p {
        margin-left: 30px;
        border-color: yellow;
      }
    </style>
  </head>
  <body>
    <div class="menu">
      <!-- <div>
        <p>第一级菜单</p>
        <div>
          <p>第二级菜单</p>
          <div>
            <p>第三级菜单</p>
          </div>
        </div>
      </div> -->
    </div>
    <script>
      //服务器返回一个不确定的数据结构,涉及到多重数组嵌套
      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: ["雀巢咖啡"]
            }
          ]
        }
      ]

     //封装一个遍历don树函数
     function addElement(arr,father){//arr 遍历那个数组 father你要加到那个dom树里面去
       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)
        }
        //arr[i].data 是二级菜单
     
       }
     }
    
      addElement(arr,document.querySelector('.menu'))