this指向问题
什么是this
this表示当前函数的执行上下文,this一般存在在函数中,当函数执行时指向当前执行函数的对象,若当前函数无调用对象,则指向全局对象window,开启严格模式则为undefined,
function fn(){
console.log(this)
}
function fn1(){
"use strict" //严格模式
console.log(this)
}
const obj = {
fn2 = function(){
console.log(this)
}
}
fn() // window
fn1() // undefined
obj.fn2() //obj
NEW关键字
New关键字会通过构造函数生成一个空对象并返回this,this指向返回的空对象
箭头函数
箭头函数没有自己的this,他的this指向就是外层第一个对象或普通函数的this,要注意的是箭头函数中的this无法再次修改
事件绑定
事件绑定中的this会指向事件源
定时器绑定
定时器的this指向window,因为定时器方法在window对象中,并且参数使用回调函数作为处理函数,而回调函数的this指向widnow
call,apply,bind函数调用
其中call,apply是临时改变一次this指向 bind则是永久改变
手写call,apply,bind
函数原型的call,apply,bind方法都会改变this指向,call和apply只是类似,但传参不同,bind则可以柯里化形式进行传参,但使用bind会形成闭包
由于es6出来已经相当久了,所以手写时会使用到一些es6语法
- 剩余参数
- 扩展运算符
- 解构
- Symbol
- 三目运算符
- 块级作用域 const,let
其他知识:
- 闭包
- 垃圾回收机制
- 柯里化函数
调用函数原型上提供的call方法:
call方法第一个参数为this指向的上下文,后续1个或多个参数都会以剩余参数的方式输出
const obj = {a,1}
function foo (...args){
console.log(this)
console.log(...args)
}
foo(1,2,3,4,5) //输出 window和1,2,3,4,5
foo.call(obj,1,2,3,4,5) //输出 obj和1,2,3,4,5
实现call
Function.prototype._call = function (context,...args) {
// 将传入的上下文保存,如果传入的上下文为null或者undefined,则将上下文指向window
const ctx = context ? context : window
// 通过symbol创建唯一属性
const key =Symbol()
// 将唯一属性指向调用_call函数的函数
ctx[key] = this
// 由于call方法是函数原型上的方法,所以this的指向一定是函数
// 立即调用一次函数,并将结果保存
const result = ctx[key](...args)
// 将我们将创的key属性删除
delete ctx[key]
// 返回调用结果
return result
}
foo(1,2,3,4,5) //输出 window和1,2,3,4,5
foo._call(obj,1,2,3,4,5) //输出 obj和1,2,3,4,5
实现apply
调用函数原型上提供的apply方法:
apply方法第一个参数为this指向的上下文,第二个参数接收一个数组
const obj = {a,1}
function foo (...args){
console.log(this)
console.log(...args)
}
foo(1,2,3,4,5) //输出 window和1,2,3,4,5
foo.apply(obj,[1,2,3,4,5]) //输出 obj和1,2,3,4,5
apply函数的实现和call大同小异,只不过是将剩余参数换成了数组
Function.prototype._apply_ = function (context,arr) {
const ctx = context ? context : window
const key = Symbol()
ctx[key] = this
const result = ctx[key](arr)
delete ctx[key]
return result
}
调用函数原型上提供的bind方法:
bind方法会返回一个可以柯里化调用的函数
const obj = {a,1}
function foo (...args){
console.log(this)
console.log(...args)
}
foo(1,2,3,4,5)
// bind会返回一个可以柯里化调用的函数,但是在没有参数是情况下不会执行,但使用bind会产生闭包
const fn = foo.bind(obj)
fn(1) // obj 1
const fn1 = foo.bind(obj,1)
fn1(2,3,4) // ojb 1,2,3,4
实现bind
Function.prototype._bind_ = function (context,...args) {
// 保存当前指向
let self = this
// 保存当前参数
let arg = [...args]
// 返回一个函数
return function (...args){
let temp = [...args]
// 函数中调用了参数arg,会形成闭包
// 由于返回的函数中调用了apply,所以返回的函数被调用时this指向依然是bind传入的上下文,而不是指向调用对象
// 并且apply会立即执行当前函数,返回结果,所以函数也会正常执行
self.apply(context,[...arg,...temp])
}
}