一:this为什么存在?
1.1:利用this
访问对象属性
阅读下面的代码,我们想要在在对象内部方法中使用对象内部的属性,但在js中没有下面这样的语法,必须将第五行的console.log(myname)
改为console.log(this.myname)
或者console.log(obj.myname)
,才能输出‘midsummer’
let obj={
myname:'midsummer',
age:18,
bar: function(){//在对象内部方法中使用对象内部的属性
console.log(myname);
}
}
obj.bar()
在JavaScript等面向对象的编程语言中,
this
关键字允许对象中的函数(或方法)访问其所属的对象的属性。通过this
,方法可以在不显式传递对象本身作为参数的情况下,引用并操作对象的内部状态。
1.2:使用this
提升代码质量
this可以显著的提升代码质量,减少上下文参数的传递,请读者比较下面两份代码,其中toUpperCase()方法是把传入参数转为大写
function identify(context){
return context.name.toUpperCase();
}
function speak(context){
var greeting = identify(context) + ' is beautiful';
console.log(greeting);
}
var me = {
name: 'midsummer'
};
speak.call(me);
第一份代码,我们需要向identify函数和speak函数显式的传参。这样的写法不够优雅,在有多个函数进行该参数的传递时,倘若其中一环参数出了问题,会带来一系列的影响,用这种方法,难以维护。接下来我们用this
来修改代码,可以显著的提升代码质量,减少上下文参数的传递
function identify(){
return this.name.toUpperCase();
}
function speak(){
var greeting = identify.call(this) + ' is beautiful';
console.log(greeting);
}
var me = {
name: 'midsummer'
};
speak.call(me);
this
避免了在调用对象方法时显式传递对象本身作为参数,减少了代码的冗余,增强了代码的可读性和可维护性。此外,this
还有助于实现封装和隐藏对象内部状态,增强数据的安全性。
二:this的绑定规则
this是一个关键字,是一个代词,也就是有指向的
2.1:默认绑定
- 默认绑定:当一个函数独立调用,不带任何修饰符的时候
- 函数在哪个词法作用域下生效,函数中的this就指向哪里
function foo(){//foo独立调用
console.log(this);//this指代window(全局)
}
foo();
以上代码中,foo独立调用,foo的词法作用域在全局,故this指代window(全局)。这里的函数作用域与词法作用域相同,都是全局,但在更复杂一点的代码中,容易混淆,请读者们阅读下面代码,注意函数作用域与词法作用域的区别
词法作用域:由函数声明的位置来决定,跟函数在哪里调用没有关系
function foo(){//foo独立调用
console.log(this);//this指代window(全局)
}
function bar(){
foo()
}
function baz(){
bar()
}
baz()
函数foo,bar,baz,的词法作用域都是全局,所以,这里的this,还是指代window(全局)。其实,只要在默认绑定规则下,读者只需要记住一句话:只要是默认绑定,this一定指向windows
2.2:隐式绑定
- 隐式绑定:当函数的引用有上下文对象时(当函数被某个对象所拥有时)
- 函数的this指向引用它的对象
请读者阅读下面代码,其中函数foo在对象obj中是引用,而非独立调用,属于隐式绑定,故函数的this指向对象obj,输出1
var obj = {
a:1,
foo:foo//引用foo
}
function foo(){
console.log(this.a)//1
}
obj.foo()
在隐式绑定中,要认准函数的this指向引用它的对象,上述代码中,函数foo在对象obj中引用,那对象obj是函数foo的‘老大’。倘若对象obj也有‘老大’,那就是函数foo‘老大的老大’,那this指向会指向‘老大的老大’吗?答案是不会的
隐式丢失:当一个函数被多个对象链式引用时,函数的this指向就近的那个对象
2.3:显式绑定
同样一份代码,现在我们用显式绑定方法实现使函数foo中的this指向对象obj。foo.call(obj)
,利用call()方法,强行将foo的this指向对象obj。(至于call()是如何实现的也是我们需要掌握的,这涉及原型相关概念,会在之后的文章中详细解释。)
var obj = {
a:1
}
function foo(){
console.log(this.a)//1
}
foo.call(obj)
如果需要传入参数,显示绑定要如何实现?简单修改上例代码:
var obj = {
a:1
}
function foo(x,y){
console.log(this.a,x+y)
}
foo.call(obj,2,3)
以上展示的是用call进行显式绑定,并接收参数,实际上显示绑定有以下三种方法:
-
call:
foo.call(obj,2,3)
,直接将参数以逗号隔开,接在obj后 -
apply:
foo.apply(obj,[2,3])
,与call唯一不同点就是参数以数组的形式展示 -
bind:
var bar = foo.bind(obj,2,3,4) bar()
,bind中放实参,只读取前几需要的参数var bar = foo.bind(obj,2,3) bar()
bind中放全部的参数var bar = foo.bind(obj,2) bar(3,4)
bind中放一部分,bar中放一部分,当参数多了,就近选择需要个数的代码,这个例子即选取2和3
2.4:new绑定
- new绑定:this指向创建出来的实例对象
阅读以下代码,思考构造函数中的this指向谁
function Person(){
this.name = 'midsummer'
}
let p = new Person()//实例对象
在这份代码执行时,会发生以下:
- new会在函数中创建一个对象,假设这个对象叫obj,
var obj = { name:'midsummer'}
Person.call(obj)
,让构造函数的this,显示绑定到对象上- 执行构造函数中的
this.name = 'midsummer'
- 实例对象继承构造函数原型上的方法
Object_proto_ = Person.prototype
- 返回对象
return obj
三:箭头函数与this
- 普通函数:
function foo(){}
- 箭头函数:
var bar = () => {}
this写在全局和函数体内,(箭头函数)不承认this,没有this这个概念,但可以有this
写在箭头函数中的this是它外层非箭头函数的this
根据以上准则,阅读以下代码,试判断,箭头函数中的this指向谁
var obj = {
a: 1,
b: function(){
const fn =() =>{
console.log(this.a);
}
fn()
}
}
obj.b()
分析可知,箭头函数中的this是b:function(){}
的,即在问b:function(){}
中的this指向哪?那自然是指向对象obj。obj在调用b时,执行console.log(this.a)
,相当于打印obj.a
,所以输出1
四:知识点get:
-
为什么要有this:
- 为了让对象中的函数有能力访问对象有自己的属性
- this可以显著的提升代码质量,减少上下文参数的传递
-
this的绑定规则
-
默认绑定:当一个函数独立调用,不带任何修饰符的时候
- 函数在哪个词法作用域下生效,函数中的this就指向哪里(只要是默认绑定,this一定指向windows)
-
隐式绑定:当函数的引用有上下文对象时(当函数被某个对象所拥有时)
- 函数的this指向引用它的对象
-
隐式丢失:当一个函数被多个对象链式调用时,函数的this指向就近的那个对象
-
显式绑定:
- call
- apply
- bind
-
new绑定:
- this指向创建出来的实例对象
-
-
箭头函数 箭头函数没有this这个机制,写在箭头函数中的this也是它外层非箭头函数的this