了解this指向,闭包,递归

118 阅读4分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

this指向

三种this指向

环境对象this : 谁调用'我',我就指向谁(this的指向取决于函数的调用,与声明无关)

  1. 普通函数 : 函数名() this指向window
  2. 对象方法 对象名.方法名() this指向对象
  3. 构造函数 new 函数名() this指向new创建的那个实例对象

this的指向只有这三种情况

平时我们判断this的指向就是三选一

技巧:

(1)先看有没有点. 有点无脑this指向对象

(2)再看有没有new ,有new就是指向实例对象

(3)以上两种都不是就是window

修改this指向

如果我们不手动修改this的指向,那么它是由上下文确定的,不会改变的,但我们在特殊的情况下可能需要修改this的指向

上下文调用:修改函数内this的指向

  1. 函数名.call(修改this, 参数1 , 参数2 ...)
  2. 函数名.apply(修改this,[数组])
  3. 函数名.bind(修改this)

call()

call()方法可以传入多个参数,第一个参数写this指向的对象,剩下的写参数

伪数组转为真数组的三种方式

  1. 数组.slice(start,end)

    slice方法不传参数的话会默认返回真数组自身,但伪数组中却没有真数组的slice方法,那么我们可以将数组的构造函数的原型对象的方法指向伪数组,相当于伪数组调用了这个方法,就会使用slice方法,返回为一个真数组

      let obj = {
                0:88,
                1:60,
                2:90,
                length:3
            }
     let arr = Array.prototype.slice.call(obj);
     console.log( arr );
    
  2. ES6: Array.from(伪数组);

    ES6为我们提供了一个伪数组转真数组的固定语法 Array.from

  3. 第三种使用apply()方法 在下面讲

apply()

这个方法和call()的用法都一样,但是apply的参数要放在一个数组中,apply方法会自动遍历这个数组

接上使用apply将数组转为真数组

    let obj = {
            0:88,
            1:60,
            2:90,
            length:3
        }
let arr = [];
 //第一个参数传arr目的 : 让push方法中的this不变
arr.push.apply(arr,obj);

使用apply求数组最大值

let arr = [10,12,12,451,4,854,51,21];
//使用Math.max方法
let max = Math.max.apply(Math,arr);
//这样的话apply就会依次调用arr里面的值和函数的返回值进行比较,直到比较完,那么就是最大的了//ES6 有一种更方便的做法  ..是ES6新增的一个运算符, 作用与apply类似,会自动遍历数组然后逐一传参
Math.max(...arr);

bind()

这个方法有点不一样,上边的call和apply都是立即执行,而bind只会修改this的指向返回一个修改后的新函数等待调用 且 新函数的this无法被修改(call,apply都无效),如果传入了其他参数,参数也无法修改,固定住了

由于bind()的特点,所以实际开发中主要也用于和它特点相同的场景,比如定时器

      let fn = function(){
        console.log( this )
      } 
      //修改fn的this
      let newFn = fn.bind({name:'张三'})
      // newFn : 变量取值语法      newFn() : 函数调用,得到返回值
​
      //修改定时器的指向
      setTimeout( function(){
        console.log(this)
      }.bind({name:'李四'})  , 1000)
​

call,apply,bind三者的区别

1.call、apply、bind三者区别

(1)相同点 : 作用一致,都是修改函数的this

(2)不同点 :

a.传参方式不同 : call()是逐个传参,apply()是数组/伪数组传参

b.执行机制不同 : call()、apply() 立即执行函数,bind()不会立即执行而是得到修改this后的新函 数

闭包

闭包就是一个函数有权访问另一个函数内部变量函数

mdn官方给的解释是

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment词法环境)的引用的组合

个人理解:函数+上下文引用

闭包无处不在

    function fn(){
        let num = 10
        // fn1函数 + num引用 形成闭包
        function fn1(){
          console.log(num)  //使用了fn里面的局部变量num  所以形成了闭包 closure
        }
        fn1();
      }
      fn();

闭包可以延长局部变量的生命周期

    function fn(){
        let num = 10
        // fn1函数 + num引用 形成闭包
        return function(){
         num++;
          console.log(num);
        }
      }
      
let newfn = fn();
newfn(); //11
newfn(); //12
newfn(); //13
newfn(); //14//销毁
newfn = null;

递归

一个函数内部自己调用自己(递归要有结束条件,否则会死循环)

递归算是一种算法,时间复杂度高,用于遍历树形结构

深拷贝与浅拷贝

浅拷贝 : 拷贝的是(栈)地址. 修改拷贝后的数据,对原数据‘有影响’

深拷贝 : 拷贝的是(堆)数据。 修改拷贝后的数据,对原数据‘没有影响’

let newObj = JSON.parse( JSON.stringify(obj) );//使用json实现深拷贝

或者使用递归....

  let obj = {
      name: '张三',
      age: 20,
      sex: '男',
      hobby: ['吃饭', '睡觉', '学习'],
      student: {
        name: '萌萌',
        age: 18
      }
    }
​
    let newobj = {};
    function copy(newobj, obj) {
      for (const key in obj) {
        console.log(key);
        if (obj[key] instanceof Array) {
          newobj[key] = [];
          copy(newobj[key], obj[key]);
        } else if (obj[key] instanceof Object) {
          newobj[key] = {};
          copy(newobj[key], obj[key]);
        } else {
          newobj[key] = obj[key];
        }
​
      }
    }
    copy(newobj, obj);
    newobj.name = '芒果';
    newobj.hobby[0] = '嘻嘻哈哈';
    newobj.student.name = 'mango';
//修改拷贝后数据的值对原数据没有影响
    console.log(newobj, obj);