JavaScript之this指针

this介绍

this在JavaScript中是一个关键字,用来代表上下文中的一个对象。因此this就是指向一个对象。

但是在不同的作用域中,this的指向就不同,在全局作用域中,this指向window

console.log(this);  //window
var name = 'simon';
console.log(this.name); //simon
console.log(window.name); //simon

函数在不同的作用域下调用都会绑定不同的this对象

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

//直接调用
foo();  //window

//放到一个对象中,再调用
let obj = {
    name:'simon',
    foo: foo,
}
obj.foo(); //obj对象

//通过call/apply调用
foo.call('abc'); //String{'abc'}对象

上述代码中,foo函数在调用时,会默认给this绑定一个对象,并且this是在函数调用时被绑定的

this的绑定规则

默认绑定

当函数只是当做普通函数调用时,函数的this默认绑定window,箭头函数除外。

function foo(){
    console.log(this);
}
foo();  //window
function foo(func) {
    func()
}

var obj = {
    name: 'simon',
    bar: function() {
        console.log(this); // window
    }
};

foo(obj.bar); 
//结果依然是window,是因为在真正函数调用的位置,并没有任何对象的绑定,只是一个普通函数的调用

隐式绑定

function foo() {
  console.log(this); // obj对象
}

var obj = {
  name: "simon",
  foo: foo
}

obj.foo();  //obj1
//foo是通过obj对象调用的,因此this会隐式被绑定到obj对象上
function foo() {
    console.log(this); // obj1对象
}

var obj1 = {
    name: "obj1",
    foo: foo
};

var obj2 = {
    name: "obj2",
    obj1: obj1
};

obj2.obj1.foo();  //obj1,obj1最后调用foo函数
//通过obj2调用了obj1对象,然后obj1对象调用了foo函数,因此foo函数的this还是指向obj1
//函数的this指向的是.操作符前的对象
function foo() {
  console.log(this);  //window
}

var obj1 = {
  name: "obj1",
  foo: foo
}

var bar = obj1.foo;
bar();
//obj1.foo是在bar赋值的时候被调用,但是bar在调用时没有绑定任何对象,因此bar函数中的this是window

显式绑定

通过call/apply方法直接给函数绑定对象

function foo(){
    console.log(this);
}
foo.call(window);  //window
foo.call({name:'simon'}); //{name:'simon'}
foo.apply(['aaa','bbb']); //["aaa", "bbb"]

通过bind函数来绑定

//自定义bind函数
function foo(){
    console.log(this);
}
let obj = {
    name: 'simon'
};
//自定义bind函数
function bind(fun, obj){
    return function(){
        return fun.apply(obj, arguments);
    }
}
let bar = bind(foo,obj);
bar();  //obj对象
//内置bind函数
function foo(){
    console.log(this);
}
let obj = {
    name: 'simon'
};

let bar = foo.bind(obj);

bar(); //obj对象

JS原生函数的this绑定规则

setTimeout函数会传入一个函数,这个函数中的this是window。

由于setTimeout函数内部是通过apply进行绑定this对象的,并且绑定的是全局对象

setTimeout(function(){
    console.log(this); //window
}, 500);

forEach函数中传入的函数的this也是window对象

let names = ['simon','simoner','jack'];
names.forEach(function(item){
    console.log(this);  //输出三次window
});
//改变forEach内部函数的this指向
let names = ['simon','simoner','jack'];
let obj = {name:'simon'};
names.forEach(function(item){
    console.log(this);  //输出三次{name: "simmon"}
},obj);

new绑定

new关键字来创建对象时,会执行以下步骤

  1. 创建一个新的对象
  2. 将构造函数的作用域赋给新对象(因此this就指向了这个对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象
//实现new操作符
function create(){
	//创建一个新的对象
	let obj = {};
	//将构造函数的作用域赋给新对象
	let con = [].shift.call(arguments);
	obj.__proto__ = con.prototype;
	//执行构造函数中的代码
	let result = con.apply(obj, arguments);
	//返回新的对象
	return typeof result === 'object' ? result : obj;
}

//test
function Person(){
	this.name = 'simon';
	this.age = 24;
	this.job = 'student';
}
let person = create(Person);
console.log(person); //Person {name: "simon", age: 24, job: "student"}
function Person(){
    console.log(this);  //Person()
    this.name = name;
}
const person = new Person();
console.log(p);  //Person()

绑定规则的优先级

this的绑定规则有多种,当对一个函数使用两种或两种以上规则的时候就会产生冲突。因此需要判断哪种规则的优先级更高

  1. 默认规则的优先级最低
  2. 显式绑定优先级高于隐式绑定
  3. new绑定优先级高于隐式绑定
  4. new绑定优先级高于bind,new和call、apply不允许同时使用

new绑定 > 显式绑定(bind) > 隐式绑定 > 默认绑定

//显式绑定优先级高于隐式绑定
function foo(){
    console.log(this)
}

var obj1 = {
  name: "obj1",
  foo: foo
}

var obj2 = {
  name: "obj2",
  foo: foo
}

// 隐式绑定
obj1.foo(); // obj1
obj2.foo(); // obj2

// 隐式绑定和显示绑定同时存在
obj1.foo.call(obj2); // obj2, 说明隐式绑定优先级更高
//new绑定优先级高于隐式绑定
function foo() {
  console.log(this);
}

var obj = {
  name: "simon",
  foo: foo
}

new obj.foo(); // foo对象, 说明new绑定优先级更高
//new和call、apply不允许同时使用
function foo() {
  console.log(this);
}

var obj = {
  name: "obj"
}

var foo = new foo.call(obj);  //将报错,无法同时使用
//new绑定优先级高于bind
function foo() {
  console.log(this);
}

var obj = {
  name: "obj"
}

var bar = foo.bind(obj);
var foo = new bar();  //输出foo, 说明使用的是new绑定

this番外

忽略显式绑定

在显式绑定时,可以传入null或undefined来忽略显式绑定,这时将使用默认绑定

function foo(){
    console.log(this);
}
let obj = {
    name: 'simon',
};
foo.call(obj);  //obj对象
foo.call(null); //window
foo.call(undefined);  //window
let bar = foo.bind(null);
bar(); //window

间接函数引用

当创建一个函数的间接引用时,将使用默认绑定规则

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

var obj1 = {
  name: "obj1",
  foo: foo
}; 

var obj2 = {
  name: "obj2"
};

obj1.foo(); // obj1对象
(obj2.foo = obj1.foo)();  // window
//(obj2.foo = obj1.foo)的结果是foo函数,因此foo函数被直接调用,所以是默认绑定

箭头函数

箭头函数是ES6中新增的函数类型。箭头函数与普通函数的区别是箭头函数不会绑定this,而是根据外层作用域来决定this。

//箭头函数不适用于所有规则
let obj = {
	foo: ()=>{console.log(this);}
};
obj.foo(); //window
obj.foo.call(obj); //window
var obj = {
  data: [],
  getData: function() {
    var _this = this;
    setTimeout(function() {
      // 模拟获取到的数据
      var res = ["abc", "cba", "nba"];
      _this.data.push(...res);
    }, 1000);
  }
}

obj.getData();
//在setTimeout函数中直接拿到的this是window,因此需要在外层定义this才是对象中的this
//箭头函数写法
//由于箭头函数没有绑定this,因此this就会从从上层作用域中找,直到找到为止
var obj = {
  data: [],
  getData: function() {
    setTimeout(() => {
      // 模拟获取到的数据
      var res = ["abc", "cba", "nba"];
      this.data.push(...res);
    }, 1000);
  }
}
obj.getData();
//如下面所示,getData也是一个箭头函数,那将继续从上层作用域找,直到找到了window
var obj = {
  data: [],
  getData: () => {
    setTimeout(() => {
      console.log(this); // window
    }, 1000);
  }
}
obj.getData();
//箭头函数向外查找this,找的是外层作用域中的this,由于obj的this是window,所以箭头函数的this也是window。

参考链接: https://mp.weixin.qq.com/s/hYm0JgBI25grNG_2sCRlTA

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值