javascript中的this讲解
javascript中的this讲解
引言
- 关于this,很多地方都有他的身影,比如构造函数(可能说法有误,应该是对于
函数的构造调用
),亦或是为了面试,去手写一些方法
,也会用到this,(比如forEach, map, find, filter, bind, call, apply, new)。或者平时会用一个变量,将this保存起来,再或者es6的"胖箭头" => ,箭头函数
.框架层面,vue2中调用方法,要用this,而vue3 中,无需this,就可以直接调方法,诸如此多this,让我想重新学习一下this
什么是this?
- 当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文),这个记录会包含函数在哪里调用(调用栈),函数的调用方式,传入参数的信息等。this就是这个记录的一个属性,会在函数执行(调用)的时候用到
总结
- 从this的这段机制来说,
this实际上是在函数被调用时发生的绑定,他的指向完全取决于函数在哪里被调用(被调用的时执行上下文)
我们这么执着于this,他解决了什么问题?
- 看一段代码
function identify() {
return this.name.toUpperCase();
}
let me = {
name: '英俊',
};
let you = {
name: '潇洒',
};
console.log(identify.call(me)); // 英俊;
console.log(identify.call(you)); // 潇洒;
- 这段代码可以在不同的上下文对象(me和you) 中重复使用函数identify(),不用针对每个对象编写不同版本的函数。如果不使用this,那就需要给identify() 显示的传递一个上下文对象
function identify(content) { // 显示传递上下文对象
return content.name.toUpperCase();
}
let me = {
name: '英俊',
};
let you = {
name: '潇洒',
};
console.log(identify(me)); // 英俊;
console.log(identify(you)); // 潇洒;
总结
- this提供了一种更加优雅的方式来隐
式"传递"一个对象的引用
,因此可以将API设计的更加简洁并且易于复用。类似的还有工厂函数和构造函数
。工厂函数
要自己声明对象
,为对象赋值
,并且需要显示的将对象返回
,而构造函数
,无需声明对象,无需显示返回。只需要使用函数的构造调用
,就可以实现类似工厂函数的功能
工厂函数和构造函数链接https://juejin.cn/post/7119428250635337742
this的绑定规则(重点)
this的绑定,主要分为隐式绑定和显式绑定。
隐式绑定:决定于函数的调用上下文,而这里面的水很深。
显式绑定:通过call,apply,bind 强行绑定this
下面罗列了几种判断this执行的总结
- 作为对象的方法调用
- 作为普通函数调用
- 构造器调用
- Function.prototype.call 或者 Function.prototype.call 调用
- 作为对象的方法调用
- 当函数作为对象的方法被调用时,this指向该对象
let obj = {
a: 1,
getA: function () {
console.log(this === obj); // true
console.log(this.a); // 1
},
};
console.log(obj.getA());
- 作为普通函数调用
- 当函数不能作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的this总是指向全局对象。在javascript中,这个全局对象就是window
window.a = 'globalA';
let obj = {
a: 1,
getA: function () {
console.log(this === obj); // false
console.log(this.a); // globalA
},
};
let getName = obj.getA;
console.log(getName());
- tips: 在严格模式下,这种情况this已经被规定不会指向全局对象,而是undefined
- 构造器调用
- javascript中没有类,但是可以从
构造器(构造函数)
中创建对象,同时也提供了new 运算符,使得构造器看起来更像一个类。构造器的外表跟普通函数一模一样,区别在于他们被调用的方式,与其说构造函数,不如说函数的构造调用
let Person = function (name) {
this.name = name;
};
let p = new Person('珍珍');
console.log(p.name); // 珍珍
使用new 调用构造器时,还要注意一个问题。
3.1 new会隐式的返回当前对象
,如果构造器显示的返回了一个 object对象
,那么此次运算结果,最终是返回的这个对象
,而不是我们期待的this
let Person = function (name) {
this.name = name;
return {
// 显式的返回一个对象
name: '显示返回数据',
};
};
let p = new Person('珍珍');
console.log(p.name); // 显示返回数据
3.2 如果构造器不显示地返回任何数据,或者返回一个一个非对象类型的数据,就不会造成上述问题
let Person = function (name) {
this.name = name;
return '显示返回数据';
};
let p = new Person('珍珍');
console.log(p.name); // 珍珍
- Function.prototype.call 或者 Function.prototype.call 调用
跟普通的函数调用相比,用Function.prototype.call 或者 Function.prototype.call 可以动态地改变传入函数的this
let obj = {
a: 1,
getA: function () {
return this.a;
},
};
let getName = obj.getA();
console.log(getName); // 1
通过call 来强行改变this执行
let obj = {
a: 1,
getA: function () {
return this.a;
},
};
let obj1 = {
a: 2,
};
let getName = obj.getA.call(obj1);
console.log(getName); // 2
总结
- this的绑定规则,主要是
js内在机制,决定于函数的调用上下文
。有时我们并不想遵循js的内在机制,去绑定this()
,所以出了call,apply,bind
来强行绑定this的指向。通常我们把js的内在绑定机制称为,隐性绑定。把call,apply,bind
称之为显性绑定
this绑定的实际应用
- 比如在div节点的事件函数中,有一个局部的callback方法,callback被当做普通函数调用时,callback内部的this指向了windlow。但我们往往是想让他指向该div节点
<body>
<div id="div1">我是div</div>
</body>
<script>
window.id = 'window'
document.getElementById('div1').onclick = function(){
console.log(this.id) // div1
let callback = function(){
console.log(this.id) // window
}
callback()
}
</script>
方案1:可以用一个变量保存div节点的引用(其实就是闭包)
<body>
<div id="div1">我是div</div>
</body>
<script>
window.id = 'window'
document.getElementById('div1').onclick = function(){
console.log(this.id)
let that = this
let callback = function(){
console.log(that.id)
}
callback()
}
</script>
方案2: 使用bind,强行绑定
<body>
<div id="div1">我是div</div>
</body>
<script>
window.id = 'window'
document.getElementById('div1').onclick = function(){
console.log(this.id)
let callback = function(){
console.log(this.id)
}.bind(this)
callback()
}
</script>
使用call来强行绑定
<body>
<div id="div1">我是div</div>
</body>
<script>
window.id = 'window'
document.getElementById('div1').onclick = function(){
console.log(this.id)
let callback = function(){
console.log(this.id)
}
callback.call(this)
}
</script>
方案3:可以使用箭头函数
<body>
<div id="div1">我是div</div>
</body>
<script>
window.id = 'window'
document.getElementById('div1').onclick = function(){
console.log(this.id)
let callback = () => {
console.log(this.id)
}
callback()
}
</script>
箭头函数
-
上面说到了一个箭头函数。我们上面介绍的四条法则包含了所有正常函数,但是ES6中介绍了一种无法使用这些规则的特殊函数类型:
箭头函数
。其根据外层(函数或者全局)作用域来决定this
-
箭头函数
也可以向bind(...)一样,确保函数的this被绑定到指定对象。此外,最重要的一点是,用更常见的词法作用域代替了传统的this机制
。
最佳实践
虽然that = this 和箭头函数看起来都可以取代bind(...).但从本质上来说,他们是想替代的是this的机制
。如果你·经常编写this风格的代码,最好是统一绑定方式
,如果再一个函数或者同一个程序中混入这几种风格,通常会是你的代码难以维护。
转载自:https://juejin.cn/post/7135836448036487199