JS中this的值详细讲解以及面试指向练习

this 的值取决于它出现的上下文:函数、类或全局。

在函数内部,this 的值取决于函数如何被调用,this 是语言在函数体被执行时为你创建的绑定

对于典型的函数,this 的值是函数被访问的对象。换句话说,如果函数调用的形式是 obj.f(),那么 this 就指向 obj,例如

function getThis() {
  return this;
}

const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };

obj1.getThis = getThis;
obj2.getThis = getThis;

console.log(obj1.getThis()); // { name: 'obj1', getThis: [Function: getThis] }
console.log(obj2.getThis()); // { name: 'obj2', getThis: [Function: getThis] }

注意,虽然函数是相同的,但是根据其调用的方式,this 的值是不同的。这与函数参数的工作方式类似。

this到底指向谁

调用函数会创建新的属于函数自身的执行上下文,执行上下文的调用创建阶段会决定this的指向,结论:this的指向,是在调用函数时根据执行上下文所动态确定的,或者说this关键字指向函数(方法)运行时的所有者

1.规则:

  • 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下this绑定到undefined,否则绑定到全局对象window/global
  • 一般构造函数new调用,绑定到新创建的对象上
  • 一般由call/bind/apply方法显式调用,绑定到指定参数的对象上
  • 一般由上下文对象调用,绑定在该对象上
  • 箭头函数中,根据外层上下文绑定的this决定this的指向

2.实例分析练习

(1)全局环境下的this

function f1(){
  console.log(this);
}

function f2() {
  'use strict'
  console.log(this);
}

f1()  //Window对象
f2()  //undefined
const foo = {
  bar: 10,
  fn: function () {
    console.log(this);  //Window
    console.log(this.bar);  //undefined
  }
}

var fn1=foo.fn
fn1()

上题中this指向的是window,虽然fn函数在foo对象中作为方法被引用,但是在赋值给fn1后,fn1的执行仍然是在window的全局环境中

const foo = {
  bar: 10,
  fn: function () {
    console.log(this);  //{bar:10,fn:f}
    console.log(this.bar);  //10
  }
}

foo.fn()

上题中this指向的是最后调用他的对象,在foo.fn()语句中this指向foo对象

注意:在执行函数时,如果函数中的this是被上一级的对象所调用,那么this指向的就是上一级的对象;否则指向全局环境

 (2)上下文对象调用中的this

const student={
  name:'hist',
  fn:function(){
    return this
  }
}

console.log(student.fn()===student); //true
const person = {
  name: 'hist',
  brother: {
    name: 'comp',
    fn: function () {
      return this.name
    }
  }
}

console.log(person.brother.fn());  //comp

上题在这种嵌套关系中,this指向最后调用它的对象

const o1 = {
  text: 'o1',
  fn: function () {
    return this.text
  }
}

const o2 = {
  text: 'o2',
  fn: function () {
    return o1.fn()
  }
}

const o3 = {
  text: 'o3',
  fn: function () {
    var fn= o1.fn
    return fn()
  }
}

console.log(o1.fn()); //o1
console.log(o2.fn()); //o1
console.log(o3.fn()); //undefined

第二个相当于最后调用时还是用o1调用的fn()

第三个将o1.fn()赋值给fn后,直接调用fn(),没有用对象点调用,所以这里this指向window

如果想让第二个输出o2怎么做?

const o2 = {
  text: 'o2',
  fn: o1.fn()
}

此时我们将赋值操作提前,相当于先把o1.fn()变为this.text,此时我们用o2调用fn()指向的就是o2对象

  (3)bind/call/apply改变this指向

补充:bind/call/apply使用方法

const target={}

fn.call(target, 'arg1', 'arg2')
fn.apply(target,['arg1','arg2'])
fn.bind(target, 'arg1', 'arg2')()

示例一

function greet() {
    console.log(`Hello, ${this.name}`);
}

const person = { name: 'Alice' };

// 使用 call 明确指定 this
greet.call(person);  // 输出:Hello, Alice

示例二

function greet(city, country) {
    console.log(`Hello, ${this.name} from ${city}, ${country}`);
}

const person = { name: 'Bob' };

// 使用 apply,参数是数组
greet.apply(person, ['New York', 'USA']);  // 输出:Hello, Bob from New York, USA

示例三

function greet() {
  console.log(`Hello, ${this.name}`);
}

const person = { name: 'Charlie' };

// 使用 bind 创建一个新的函数
const boundGreet = greet.bind(person)();// 输出:Hello, Charlie

  (4)构造函数和this

function Foo(){
  this.bar='hist'
}

const instance=new Foo()
console.log(instance.bar);//hist

new操作符调用构造函数做了什么?

  • 创建一个新的对象
  • 将构造函数的this指向这个新对象
  • 为这个对象添加属性、方法等
  • 最终返回新对象

下面是构造函数中出现显示return的情况,分为两种场景:

function Foo() {
  this.user = 'hist'
  const o = {}
  return o
}

const instance = new Foo()
console.log(instance.user);//undefined

 将会输出 undefined,此时 instance 是返回的空对象 o。

function Foo() {
  this.user = 'hist';
  return 1;
}

const instance = new Foo();
console.log(instance.user);//hist

 将会输出 hist,也就是说此时 instance 是返回的目标对象实例 this。

结论:如果在构造函数中显式返回一个值,且返回的是一个对象,那么 this 就指向这个返回的对象;如果返回的不是一个对象,那么 this 仍然指向实例。

   (5)箭头函数中的this指向

箭头函数和普通函数有一个重要的区别:箭头函数不会有自己的 this,它会继承外部上下文的 this。

const foo = {
  fn: function () {
    setTimeout(function () {
      console.log(this);
    });
  },
};
foo.fn();//window

this 出现在 setTimeout() 中的匿名函数里,因此 this 指向 window 对象

const foo = {
  fn: function () {
    setTimeout(() => {
      console.log(this);
    });
  },
};

foo.fn();//{fn: ƒ}
  • 在 foo.fn() 调用时,this 在 fn 方法内指向 foo 对象。
  • setTimeout 中的箭头函数继承了外部的 this,也就是 foo 对象。

(6)this优先级

通过 call、apply、bind、new 对 this 绑定的情况称为显式绑定

根据调用关系确定的 this 指向称为隐式绑定

function foo(a) {
  console.log(this.a);
}

const obj1 = {
  a: 1,
  foo: foo,
};

const obj2 = {
  a: 2,
  foo: foo,
};

obj1.foo.call(obj2); //2
obj2.foo.call(obj1); //1

call、apply 的显式绑定一般来说优先级更高

function foo(a) {
  this.a = a;
}

const obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a);

上述代码通过 bind,将 bar 函数中的 this 绑定为 obj1 对象。执行 bar(2) 后,obj1.a 值为 2。即经过 bar(2) 执行后,obj1 对象为:{a: 2}。

当再使用 bar 作为构造函数时:

var baz = new bar(3)
console.log(baz.a)//3

将会输出 3。我们看 bar 函数本身是通过 bind 方法构造的函数,其内部已经将 this 绑定为 obj1,它再作为构造函数,通过 new 调用时,返回的实例已经与 obj1 解绑。 也就是说:new 绑定修改了 bind 绑定中的 this,因此 new 绑定的优先级比显式 bind 绑定更高。

function foo() {
  return (a) => {
    console.log(this.a);
  };
}

const obj1 = {
  a: 1,
};

const obj2 = {
  a: 2,
};

const bar = foo.call(obj1);
bar.call(obj2);//1

由于 foo() 的 this 绑定到 obj1,bar(引用箭头函数)的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。

var a = 123;
  const foo = () => (a) => {
    console.log(this.a);
  };

  const obj1 = {
    a: 2,
  };

  const obj2 = {
    a: 3,
  };

  const bar = foo.call(obj1);
  console.log(bar.call(obj2));//123

箭头函数的绑定无法被修改,foo的执行上下文是window

如果将第一处的var a = 123;改为const a = 123;

答案将会输出为 undefined,原因是因为使用 const 声明的变量不会挂载到 window 全局对象当中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值