this的介绍
为了让对象中的函数有能力访问对象中自己的属性,this
可显著的提升代码质量,减少上下文参数的传递
举个例子看看:
js
let obj = {
myname: "TT",
age: 18,
say: function () {
console.log(this.myname);
}
}
obj.say()//TT
在这里的this
指向了obj
这个例子可能并不太能说明this
的好处,于是接着看:
js
function identify(context) {
return context.name.toUpperCase();//将name属性转换为大写
}
function speek(context){
var greeting = "你好我是" + identify(context);//调用identify函数转换name属性
console.log(greeting);
}
var p = {
name : 'nike'
}
speek(p);
会发现参数context
需要到处传递,避免了context
的污染,可以使用this
进行取代
js
function identify() {
return this.name.toUpperCase();//将name属性转换为大写
}
function speek(){
var greeting = "你好我是" + identify.call(this);
console.log(greeting);
}
var p = {
name : "nike"
}
speek.call(p);
将参数context
完全取代了,也让代码更加容易看得懂了,但是也会产生疑问,那this
到底如何指代的呢??
this的指向
this的指向通过其绑定的方式来确定,其绑定形式具有以下四种:
- 默认绑定:当一个函数独立调用时,不带任何修饰符的调用,该函数的this指向全局对象window
- 隐式绑定:当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时该函数中的this指向该上下文对象
- 隐式丢失:当一个函数被多个函数调用时,this指向最近的那个对象
- 显式绑定:当一个函数独立调用时,使用call、apply、bind等方法,this指向指定的对象
- new绑定:this指向实例对象 //当一个函数通过new关键字调用时,this指向新创建的对象
默认绑定
当一个函数独立调用时,不带任何修饰符的调用,该函数的this
指向全局对象window
js
var a = 2;
function foo(){
var a = 1;
function bar(){
console.log(this.a);
}
bar() // 在foo的作用域里生效 在全局这个词法的词法作用域里生效
}
foo()//独立调用
在该函数中的bar()
,foo()
都为独立调用,不带任何修饰符的调用,因此,即使bar()
在foo
的作用域里产生,但依然在全局这个词法的词法作用域里生效
js
var a = 1;
function foo(){
var a = 2;
function bar(){
var a = 3;
function baz(){
console.log(this.a);
}
baz()// 在bar的作用域里生效 在全局这个词法词法作用域里生效
}
bar() // 在foo的作用域里生效 在全局这个词法词法作用域里生效
}
foo()
同理,无论如何嵌套关系,只要是独立调用那么this就是指向全局(即window
)
this指向分析
函数在哪个词法作用域下生效,函数中的this就指向哪(简述就是只要是默认绑定,this一定指向window)。
隐式绑定
当一个函数被某个对象所拥有,或者函数被某个上下文对象引用时该函数中的this指向该上下文对象
js
var obj = {
a: 2,
foo: function(){
console.log(this.a); //this指向了obj
}
}
obj.foo()
其中obj.foo()
,函数foo
被上下文对象obj
所拥有,于是该函数中的this指向该上下文对象
js
var obj = {
a: 1,
foo:foo //函数声明 foo()执行结果,是引用
}
function foo() {
console.log(this.a);//1 this指向obj 隐式绑定
}
obj.foo()
在对象obj中函数foo被上下文对象obj引用因此该函数中的this指向该上下文对象
在这里需要注意一点:在JavaScript中,函数的调用和引用是两个不同的概念:
函数调用:
- 函数调用是指执行一个函数,这通常通过函数名后跟一对圆括号
()
实现。- 调用函数时,会创建一个新的执行上下文,并将其推入执行栈。
- 函数调用可以传递参数给函数,函数内部可以使用这些参数执行特定的操作。
- 函数调用的结果(如果有返回值的话)可以被赋值给变量或用于其他操作。
函数引用:
- 函数引用是指将函数赋值给一个变量或将函数作为参数传递给另一个函数。
- 引用函数时,并没有执行该函数,只是将函数的引用(内存地址)存储在变量中。
- 引用的函数可以在任何时候被调用,只要它在作用域内。
- 函数引用允许函数作为一等公民(first-class citizen)在JavaScript中使用,即函数可以像任何其他值一样被存储、传递和操作。
js
var obj = {
a: 1,
foo:foo()//函数调用 foo()执行结果,不是引用
}
function foo() {
console.log(this.a);//undefined this指向window
}
obj.foo//这一行压根没用
而这一段代码为函数的调用,和引用不同它相当于:
js
var obj = {
a: 1,
foo:console.log(this.a);
}
因此这里的this不属于隐式绑定,this指向了全局
隐式丢失
当一个函数被多个函数调用时,this指向最近的那个对象
即拥有就近原则
js
var obj = {
a: 1,
foo:foo
}
var obj2 = {
a: 2,
obj:obj
}
obj2.obj.foo()
在这里this指向obj
,函数被多个对象嵌套,存在隐式绑定,而这时候this拥有就近原则,指向最近的引用对象
显式绑定
当一个函数独立调用时,使用call、apply、bind
等方法,this指向指定的对象
js
var obj = {
a: 1,
}
function foo(x,y){
console.log(this.a,x + y);
}
foo.call(obj,1,2)//call会把foo中的this掰弯到obj中
foo.apply(obj,[1,2])//apply会把foo中的this掰弯到obj中
const bar = foo.bind(obj,1,2)
bar()
const bar = foo.bind(obj)
bar(1,2)//也有就近原则,bind优先
call
会把foo
中的this
掰弯到obj
中
apply
和call
差不多,但是区别为,apply
传递其他的参数给foo时是通过数组[]
的形式进行传递的。
bind
会返回一个函数 ,定义一个bar
接收该函数,然后进行调用,这时候就会有多个调用方法
一、(通过bind传参)
jsvar bar = foo.bind(obj, 2, 3); bar();
二、(通过bar传参,函数bar中也可以设置参数)
jsvar bar = foo.bind(obj); bar(2, 3);
三、(bind和bar结合传参,双方都含有参数,会全部选取)
jsvar bar = foo.bind(obj, 2); bar(3);
四、(bind和bar结合传参,但存在多出的参数,会根据就近原则,优先选取bind)
jsvar bar = foo.bind(obj, 2, 3); bar(4);
new绑定
this指向实例对象,即当一个函数通过new
关键字调用时,this指向新创建的对象
js
function Person(){
//new 的五个步骤
//let obj = {
// name:'Tom'
//}
//Person.call(obj)
//obj___proto__ = Person.prototype
//return obj
this.name = 'Tom';
}
let p = new Person() //{name:'Tom'}
在Person
构造函数内部,this
指的是新创建的p
对象,this.name = 'Tom';
这行代码将p
的name
属性设置为'Tom'
箭头函数
箭头函数中没有this这个机制,写在箭头函数中的this,那也是外层非箭头函数的
那什么是箭头函数呢??
js
function foo(){}
var bar = function(){}
const baz = () => {}
前一、二种我们都已经熟悉,第三种便是箭头函数,它和前两种的作用一样都是创建了个函数
接下来看看关于箭头函数的this指向
js
var obj ={
a:1,
baz:baz
}
var baz = () => {
console.log(this.a);
}
obj.baz();
很明显这是个隐式绑定,没有问题,指向的是obj中的a,得到1
接下来改动一下
js
var obj = {
a:1,
foo: function(){
const fn = () => {
console.log(this.a);
}
fn()//独立调用指向全局
}
}
obj.foo();//1
对象obj
中有一个函数foo
,函数foo
中又创建了个函数fn
,而在函数foo
中独立调用了函数fn
,那么可以判断fn
中的this
属于默认绑定指向全局理应输出undefined
,但箭头函数中没有this
的概念,fn
不承认这个this
于是这个this
向外跑找到了最近的函数foo
,便属于foo
的this
了
于是乎相当于foo
中创建了this
,而又存在隐式绑定,因此this
指向的是foo
的上下文对象obj
,输出为1
总结
this的指向通过其绑定的方式来确定
- 默认绑定:当一个函数独立调用时,不带任何修饰符的调用,该函数的
this
指向全局对象window
- 隐式绑定:当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时该函数中的
this
指向该上下文对象- 隐式丢失:当一个函数被多个函数调用时,this指向最近的那个对象
- 显式绑定:当一个函数独立调用时,使用
call、apply、bind
等方法,this指向指定的对象 new
绑定:this指向实例对象 //当一个函数通过new关键字调用时,this指向新创建的对象
还存在箭头函数没有this机制,在遇见时要注意它的存在,将它的this跳出一直找到第一个承认this的,再判断this的指向