this的指向基本在每天的工作开发中经常遇到,本文总结了this几种主要场景的指向,并对call apply bind这三种改变this指向的底层方法进行了实现。
this的几种常见场景及其指向
1.看方法前面是否有点,如果没有严格模式指向undefined 非严格模式指向window; 如果有点,点前面是谁就指向谁。
在立即执行函数中的this也是这种场景的典型代表,如:
//非严格模式下
var num = 1
var obj = {
num: 2,
getNum: function() {
return (function() {
return this.num;
})();
}
}
console.log(obj.getNum()); // 1
//严格模式下
'use strict'
var num = 1
var obj = {
num: 2,
getNum: function() {
return (function() {
return this.num;
})();
}
}
console.log(obj.getNum()); //Cannot read property 'num' of undefined
还有下面的案例:
let o = {
f1: function () {
console.log(this);
var f2 = function () {
console.log(this);
}();
}
}
当执行o.f1()输出:
2.元素点击事件等的绑定回调,this指向这个元素;
3.箭头函数: this 和执行无关,由箭头函数的定义上下文决定,可以认为是指向箭头函数外层的this;
4.构造函数内的this,创建新的实例时,this指向新的实例;
5.call apply bind可以修改this的指向。
call的实现
Function.prototype.mycall = function(context, ...params){
if (typeof this !== 'function') {
throw new TypeError('this is not a function')
}
context == null ? context = window : context;
const self = this;
const key = Symbol('key');
context[key] = self;
const result = context[key](...params)
delete context[key];
return result;
}
// 测试
function aa (a,b){
console.log('this,', this);
console.log(a+b);
}
aa.mycall({wwww:11},1,2)
apply的实现
Function.prototype.myApply = function(context, paramsArr){
if (typeof this !== 'function') {
throw new TypeError('this is not a function')
}
context == null ? context = window : context;
const self = this;
const key = Symbol('key');
context[key] = self;
const result = context[key](...paramsArr)
delete context[key];
return result;
}
// 测试
function aa (a,b){
console.log('this,', this);
console.log(a+b);
}
aa.myApply({wwww:11},[1,2])
bind的实现
Function.prototype.mybind= function(context, ...params){
let _this= this;
return function proxy(args){
_this.apply(context, params.concat(args))
}
}
let aa = function(a,b,e){
console.log('e',e);
console.log("this", this);
console.log(a+b);
}
document.addEventListener('click', aa.mybind({qq:11}, 1,2))
应用call apply
call apply 可以用在长得像鸭子 而自己不是鸭子时,使用鸭子的能力,通过改变this的指向复用一些基础能力,不用自己再去实现一次,如:
function dd (){
console.log(arguments);
// 将arguments转化为一个数组有很多方式 Array.from(arguments) [...arguments]
// const result = [].slice.call(arguments)
const result =[];
[].forEach.call(arguments, (item)=>{
result.push(item)
})
console.log(result);
}