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参数了,了解即可,后续都改用剩余参数;