JS高级—04—this;

 一、为什么需要this

 

 

 

 

二、this指向什么

2.1绑定时机:

普通函数的this不是在代码解析阶段被确定的,是在代码执行阶段,才能确定this应该指向那个对象;

箭头函数的不创造this,在代码解析阶段就确定了借用谁的this;

2.2绑定方式: 

总的来说:

普通函数的this是在代码执行阶段被绑定的;谁调用这个函数,函数的this就指向谁。

(默认绑定、隐士绑定)fo对象,被谁调用,那么在函数执行上下文创建的时候,this就指向谁;

(new绑定)如果fo对象被当作构造器,被new了,那么会根据fo对象在堆内存中创建一个新的对象,新对象里默认有this绑定到自己身上;

具体来说:

4种绑定规则:

4种优先级:

 

2.3注意:

注意:目前感觉this只能指向一个对象,无论是隐士绑定的window对象,还是{}对象,还是new创造的实例对象,this会在执行上下文中一直找,直到找到对象为止;

注意:只有全局执行上下文和函数执行上下文才会产生this(不包括arrow),

创建箭头函数的执行上下文的时候,就没有this,箭头函数要用的this只能去外层作用域找this,外层作用域有this就找到了,外层作用域没有this继续往外找;

比如obj ={

name:'kobe',

run:()=>{  console.log(this)}

}

obj对象是一个ao对象,是不产生this的,那么箭头函数只能找obj之外的作用域了,那就是全局;

比如obj ={

name:'kobe',

run:function(){

     settimeout(()=>{ console.log(this)},1000)

  }

}

这个时候,箭头函数找到了run函数,run函数有this,所以

 从这里也能看出,箭头函数的this的是去绑定外层作用域的this的,外层作用域run函数的this指向的对象发生了变化,箭头函数的this也发生了变化;

 

注意:

  • 普通函数有this,它的this是去指向某个对象;
  • 箭头函数没有this所以只能去借用外层的this;
  • 箭头函数借用的this应该在解析阶段就分析好了要借用谁,而普通函数的this在执行阶段才知道要绑定谁;

 

 

 

 

 

 

 

call和apply的区别是要传的参数,当要传多个参数时,call是一个个传入,而apply是都包裹进一个数组,将这个数组传入;

 

 

 2.4内置函数的this

经过测试,我们发现settimeout应该是默认绑定,应该是独立函数调用

元素的监听事件调用的回调函数的this绑定到了元素上;

foreach不加参数时也是默认绑定,但加了参数thisArg后绑定到了参数上;

 

 

 

 

 

2.5call和apply无法绑定null和undefiend;

 

 

 这个主要还是window直接指向函数调用的;

 

 

 

 

三、箭头函数

 

 

 

 

 


 

 

四、面试题

 

 

五、自己实现apply和call函数

object()可以将基本数据类型转化为对象,比如将数字123转化为new Number(123);

 

第一步:明确this指向了要执行的函数

第二步:将传过来的参数对象化

第三部:对象里调用要执行的函数即可;

 

三元运算符替换成这个,不然的话thisArgs传递一个0,那么会使用window的,这显然不对,这就是一个edgecase; 

 

 

 

 

六、补充:arguments参数和...args剩余参数

arguments参数是es5的使用,就是在一个函数里,可以使用arguments参数获取函数的形参;

arguments是一个类数组的对象,但是毕竟还不是一个数组,所以很多时候我们要用一些方法将其转化为数组;(注意箭头函数是没有arguments的,和this一样只能去上层作用域借;)

 

...args剩余参数是es6的用法,剩余参数出来后,arguments参数就不推荐使用了,因为太麻烦看着也不直观;

剩余参数,args是一个数组,经过展开运算符后可以接受函数的形参;

 

function foo(num1, num2, num3) {
  // 类数组对象中(长的像是一个数组, 本质上是一个对象): arguments
  // console.log(arguments)

  // 常见的对arguments的操作是三个
  // 1.获取参数的长度
  console.log(arguments.length)

  // 2.根据索引值获取某一个参数
  console.log(arguments[2])
  console.log(arguments[3])
  console.log(arguments[4])

  // 3.callee获取当前arguments所在的函数
  console.log(arguments.callee)
  // arguments.callee()
}

foo(10, 20, 30, 40, 50)

 

function foo(num1, num2) {
  // 1.自己遍历
  // var newArr = []
  // for (var i = 0; i < arguments.length; i++) {
  //   newArr.push(arguments[i] * 10)
  // }
  // console.log(newArr)

  // 2.arguments转成array类型
  // 2.1.自己遍历arguments中所有的元素

  // 2.2.Array.prototype.slice将arguments转成array
  var newArr2 = Array.prototype.slice.call(arguments)
  console.log(newArr2)

  var newArr3 = [].slice.call(arguments)
  console.log(newArr3)

  // 2.3.ES6的语法
  var newArr4 = Array.from(arguments)
  console.log(newArr4)
  var newArr5 = [...arguments]
  console.log(newArr5)
}

foo(10, 20, 30, 40, 50)


 

 

注意,不要用arguments参数了,了解即可,后续都改用剩余参数;

 

posted @ 2022-05-11 21:55  Eric-Shen  阅读(21)  评论(0编辑  收藏  举报