- 作者:老汪软件技巧
- 发表时间:2024-10-10 10:04
- 浏览量:
this的身世
JavaSctipt中的this指向是一个非常让人困惑的问题,他是JavaScript中重要的关键字,也是面试官们爱翻牌的“宠妃”。
首先我们需要知道this他是一个代名词,不管他走到哪里,他都是代表着某个东西,他代表的东西,或者说他的含义会在不同的语境中有所不同。
我们先来看一段简单的代码
let obj = {
myname: '段总 ',
age: 18,
say: function () {
console.log(obj.myname);
console.log(this.myname);
}
}
obj.say()
可以看到我们的obj.name = this.name,this代表obj,是可以这样用,但是这个例子我们看不到this关键字的作用体现在哪。
我们再看一个例子,下述有两段代码
function identify(context) {
return context.name.toUpperCase();
}
function speek(context) {
var greeting = 'Hello i am ' + identify(context)
console.log(greeting);
}
var p = {
name: 'tom'
}
speek(p)
上面的代码没有使用this,上下文中,参数p一直在传递,先传给speek(),再传递给identify()
function identify() {
return this.name.toUpperCase();
}
function speek() {
var greeting = 'Hello i am ' + identify.call(this)
console.log(greeting);
}
var p = {
name: 'tom'
}
speek.call(p)
下面的代码中使用了一个call()方法,我们先不讲解这个call方法(后面会提),我们能看到第二段代码减少了上下文参数的传递,而且很明显代码质量提升了,也很简洁.
this虽好,但是不是所有地方都可以使用这个关键字,呢么哪里可以使用this呢
哪里可以写this1.全局
this虽然可以使用在全局中,但是他在全局中的使用很鸡肋,因为它在全局中的使用,在node环境下就是global,在浏览器环境下就是window
function foo() {
var a = 1
console.log(this);
}
foo()
2.函数体内
在函数体内就有很多门道了,下面我们一一来讲解
我们分this的指向来讨论
一.默认绑定
当一个函数被独立调用时,不加任何修饰符的调用
var a =2
function foo() {
var a = 1
console.log(this.a); //函数在哪个词法作用域里生效
}
foo()//独立调用
什么是独立调用,就是一个函数被直接调用,而不是作为一个对象的方法被调用,当时独立调用的时候,就为默认绑定,this的指向取决于函数在哪个词法作用域里生效,词法作用域就是该函数声明的地方,他声明于全局中所以输出2
var a = 2
function foo() {
var a = 1
function bar() {
console.log(this.a);
}
bar()// 在foo的作用域里生效的 在foo所处在的词法作用域里生效
}
foo()
上面的bar函数在foo的作用域里生效,所以bar在foo所处在的词法作用域里生效,所以this还是全局,输出仍为2
var a = 1
function foo() {
var a = 2
function bar() {
var a = 3
function baz() {
console.log(this.a);
}
baz() //在bar的作用域里生效. baz this ==> bar this
}
bar()
}
foo()
因为baz在bar的作用域里生效. baz this ==> bar this又 bar在 foo的作用域里生效 bar this ==> foo this我们就可以这样理解 词法作用域是有指向性的 你在bar的词法作用域里,而bar在foo的词法作用域里,呢么你就在foo的词法作用域里,就像你在北京,呢么你也在中国.所以最后我们的指向都是全局,该所以输出的是1
最后我们可以得到一个结论
默认绑定 : 当一个函数独立调用时,不带任何修饰符的调用,该函数的this指向window
二.隐式绑定
当一个函数作为对象的方法被调用时,该函数中的"this"会隐式绑定到调用他的对象上 详细的说就是 当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时,该函数中的this指向该上下文对象
const obj = {
name: 'Alice',
sayName: function() {
console.log(this.name);
}
};
obj.sayName(); // 输出 'Alice'
在这个例子中,函数sayName作为对象obj的方法被调用,所以在sayName函数内部,“this” 指向了调用它的对象obj。
三.隐式丢失
但是当一个函数被多个对象链式调用时,this指向最近的呢个对象,他也遵守隐式绑定.
function foo() {
console.log(this.a);
}
var obj = {
a: 1,
foo: foo
}
var obj2 = {
a: 2,
obj: obj
}
obj2.obj.foo() //听老大的不听老大的老大的
输出的是离函数最近的对象
四.显示绑定
通过 call apply bind 将函数的this掰弯到一个对象中
我们先来看一段代码
var obj = {
a: 1
}
function foo() {
console.log(this.a);
}
foo()
毫无疑问foo是默认绑定,所以this是指向全局对象,全局对象里没有a,所以输出undefined,这个时候我们就非要打印出obj对象里a的值我们可以怎么做呢,这个时候就需要用到开头写的代码里的call().
var obj = {
a: 1
}
function foo() {
console.log(this.a);
}
foo.call(obj)
call可以改变this的指向,foo.call(obj)就将foo的this从全局变量转向了对象obj
function.call(thisArg, arg1, arg2,...)
let obj = {
num1: 1,
num2: 1
}
function add(a, b) {
console.log(this.num1 + this.num2 + a + b);
}
add.call(obj, 1, 1)
用法如上.
function.apply(thisArg, [argsArray])
apply()的用法与上述无异,就是第二个传入的参数,需要指定为数组
然后就是bind(),,它创建一个新的函数,这个新函数在被调用时会具有指定的this值和初始参数。就是他会返回一个函数对象,其他的与 call()无异
5.New绑定
我们先来看一段代码
function Person(){
this.name="彭于晏"
}
let p = new Person()
new Person()可以分为以下几个步骤
如果上述操作是返回obj,如果我在Person函数体内return 'HELLO'的话,他还会返回一个obj对象吗我们来试一试
返回的仍然是obj对象如果是return []数组呢
他就返回数组了再换成{},和function试试
所以我们可以得到一个结论,如果构造函数里如果有return,return的是引用类型就会覆盖掉new里面的return,不是引用类型就无关痛痒**
补充
如果出现了箭头函数,我们需要另行判断,首先我们需要知道匿名函数里没有this,他不承认有this这个关键字,但他有this这套机制.
var obj = {
a: 1,
foo: function () {
const fn = () => {
console.log(this.a);
}
fn()
}
}
obj.foo()
他只能从外层的执行环境中继承this的值,所以他会继承foo()的,输出1
const a = () => {
const b = () => {
const c = () => {
console.log(this);
}
c()
}
b()
}
a()
这里会输出全局对象,因为全是箭头函数都不存在this,所以我们得到了这个结论
箭头函数中没有this这个机制,写在箭头函数中的this在也是外层非箭头函数的
Ending
讲到这里我们就学完了this的五种基本判断,相信你如果搞懂了,就不会再被他的指向问题搞得晕头转向了,有问题可以在评论区大家一起讨论!