前言:前端现在越来越卷,找工作拿高薪更是不易,因为想要在这个特别卷的时代,我们要想进大厂,拿高薪,首先最重要的就是,知识点得掌握扎实,做到举一反三。
因此
this、call、apply、bind还不是很清楚的,文章干货很多,请先收藏,慢慢阅读,让你直接 拿捏 这些重要经典知识点。
1、this的指向
this的指向是面试中必考且比较难得一个点,本文以最简单最好理解的方式来解读this指向的问题。
在ES5中,this的指向是原则:永远指向最后调用它的那个对象。 这一点非常关键:理解this,必须牢牢记住这句话。
看看一些例子:
例1:
var name = 'windowName';
function example(){
var name = 'object';
console.log(this.name); //windowName
}
example()
这里为什么输出的是 window 而不是object ,原因在于: 函数 example 在调用时,直接调用 example() ,前面没有对象,相对于这样调用 window.example();又:this 永远指向最后调用它的那个对象 ,所以输出 window。
例2:
var name = "windowName";
var o = {
name : "object",
fn : function(){
console.log(this.name); //object
}
}
o.fn()
//window.a.fn()
在这里打印的是 object ,因为函数 是 对象 o 调用的。
这里换成 window.a.fn() 也是同样的结果,因为最后还是 对象a 调用的,印证了那句话 :this永远指向它最后调用的对象 。
例3:面试中很坑的一个例子。
var name = "windowName";
var o = {
name : "object",
fn : function(){
console.log(this.name); //windowName
}
}
const f = o .fn;
f()
这里就让人很懵,大家肯定都很好奇原因,首先要明白 const f = o .fn; 这一步是声明变量f 并赋值的操作,在这里只是将对象 o 的方法 fn 赋值给 f 并没有调用,而最后调用f() ,(实际是调用o .fn()) ,是在window对象下调用的,因此this 指向的是 window , 所有输出的是 :windowName 。
2、改变this的指向?
通常改变this的方法有一下几种方法:
- 使用call、apply、bind。
- ES 6 中的箭头函数。
- 函数内部一般使用
that = this。 new实例化一个对象。- 隐式绑定
2.1 使用 apply、call、bind
1、使用call
var person = {
name:'王涵宇'
}
var o = {
name : "object",
fn : function(){
console.log(this.name); //王涵宇
}
}
o.fn.call(person)
2、使用apply
/*
以call实例为例
*/
o.fn.apply(person) //王涵宇
3、使用bind
/*
以call实例为例
*/
o.fn.bind(person)() // 王涵宇
//注意这里的调用方式与上面的不同
2.2 使用 ES 6 中的箭头函数。
使用箭头函数:
var name = "windowName";
var o = {
name : "object",
fn1 : function(){
console.log(this.name); //object
},
fn2 : function(){
setTimeout(()=>{
this.fn1()
},1000)
}
}
o .fn2();
不使用箭头函数:
var name = "windowName";
var o = {
name : "object",
fn1 : function(){
console.log(this.name); //Uncaught TypeError: this.fn1 is not a function
},
fn2 : function(){
setTimeout(function(){
this.fn1()
},1000)
}
}
o .fn2();
会报错,因为 最后是 window对象 调用 setTimeOut ,而 window对象上没有 fn1 这个方法。
2.3 函数内部使用 that = this。
var name = "windowName";
var o = {
name : "object",
fn1 : function(){
console.log(this.name); // object
},
fn2 : function(){
let that = this; //保存this
setTimeout(()=>{
that.fn1()
},1000)
}
}
o .fn2();
2.4 new 实例化一个对象
var name = "windowName";
function getName(){
this.name = '王涵宇';
console.log(this); //getName {name: '王涵宇'} this指向这个函数(对象)
}
new getName()
2.5 隐式绑定
说白了:就是谁调用指向谁。
const obj = {
name :'王涵宇',
getName : function(){
console.log(this);
console.log(this.name) //王涵宇
}
}
obj.getName()
3、实现call、apply、bind
1、实现call
1、这三种方法都在函数的 原型上 接受两个参数
Function.prototype.myCall = function(obj,...args){
//这里默认不传obj。就是给 window
obj = obj || window;
args = args ? args : [];
//给obj添加一个独一无二的属性,防止与调用函数中有相同的属性变量
const fn = Symbol();
obj[fn] = this;
//通过隐式绑定的方式调用函数
//在这里用一个变量保存调用的结果,完了后删除这个属性,防止造成影响
const res = obj[fn](...args);
delete obj[fn](...args);
return res;
}
const obj1 = {
name:'ustinian',
getName:function(age){
console.log(`${this.name}今年${age}了!`) //王涵宇今年22了!
}
}
const obj2 = {
name:'王涵宇',
}
obj1.getName.myCall(obj2,22)
2、实现apply
Function.prototype.myApply = function(obj,args){
//这里默认不传obj。就是给 window
obj = obj || window;
args = args ? args : [];
//给obj添加一个独一无二的属性,防止与调用函数中变量属性重名
const fn = Symbol();
obj[fn] = this;
//隐式绑定的方式调用函数
const res = obj[fn](args);
delete obj[fn](args);
return res;
}
3、实现bind
bind 极其重要,以及涉及 的知识点很多,在下一文章中将重点且详细的介绍 bind 以及实现bind。
\