(四)js中this绑定规则

90 阅读4分钟

前提说明

1.js中this绑定和作用域时机不同。作用域是在code被解析时就会确定,而this是执行时才会确定。

2.js中this绑定规则分以下五种:

  • 独立函数调用绑定的this
  • 隐式调用函数绑定的this
  • 显示调用函数绑定的this
  • 通过new关键字调用函数绑定的this
  • 箭头函数中的this

1.独立函数调用绑定的this

独立函数调用:在调用的时候没有主题

this指向:this指向的window (通过以独立调用的方式来调用bind绑定后的函数除外)


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

function foo2() {
  console.log(this);
  foo1();
}

function foo3() {
  console.log(this);
  foo2();
}

foo3();

//window
//window
//window

2.隐式调用函数绑定的this

隐式调用函数:通过对象.的方式来调用

this指向:指向调用的对象。(通过隐式调用方式来调用bind绑定后的函数除外)

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

const obj = {
  name: "obj",
  bar: foo,
};

obj.bar(); //Object { name: "obj", bar: foo() }

3.显示调用函数绑定的this

显示调用函数:通过callapplybind函数来调用

this指向:代码说明

function foo(num1, num2) {
  const num3 = num1 + num2;
  console.log(this);
}

foo.call({ name: "call" }, 1, 2); //Object { name: "call" }

foo.apply({ name: "apply" }, [1, 2]); //Object { name: "apply" }


const bind = foo.bind({ name: "bind" }, 1, 2); 

bind();//Object { name: "bind" }

4.通过new关键字调用函数绑定的this

通过new关键字来调用函数

  1. 先创建一个新对象,该对象是个空对象
  2. 新对象的隐式原型__proto__指向函数的显式原型prototype
  3. 函数内部的this指向该对象
  4. 执行函数内部代码
  5. 如果该函数没有 复杂数据类型 的返回值则默认返回this指向的对象,否则返回引用类型的返回值;如果有简单数据类型的话也是返回this指向的对象
function Foo(name) {
  this.name = name;
  console.log(this);
}

// 通过new方式来调用的函数,这个函数会创建一个新的对象,该函数内部的this会指向该对象。

new Foo("new"); //Object { name: "new" }

 

// 如果该函数没有返回值则默认返回this指向的对象

const o = new Foo("o");
console.log(o); //Object { name: "o" }




function Bar(name) {
  this.name = name;
  console.log(this);
  // 如果该函数有返回值则为返回值
  return { age: 18 };
}

const o2 = new Bar("o2") //Object { name: "o2" }

console.log(o2); //Object { age: 18 }

5.箭头函数中的this

箭头函数比较特殊,其不会绑定this,在箭头函数中使用this会向外层作用域中寻找this,与在函数内部使用一个未定义的变量会向外层作用域寻找原理相同。

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

obj.foo(); //Window

obj.bar(); //Object { foo: foo(), bar: bar() }

6.特殊规则(一):忽略显式绑定

结论:显式绑定nullundefined时,this指向window

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

foo.call(null); //Window

foo.call(undefined); //Window

foo.apply(null); //Window

foo.apply(undefined); //Window 


const bindNull = foo.bind(null);
const bindUndefined = foo.bind(undefined);
bindNull(); //Window
bindUndefined(); //Window

7.特殊规则(二):间接函数调用

const obj1 = {
  name: "obj",
  foo: function (){
    console.log(this);
  },
};

const obj2 = { name: "obj2" };
obj2.foo = obj1.foo;
obj2.foo(); //Object { name: "obj2", foo: foo() }

//赋值表达式把obj1.foo作为结果来进行调用,为独立函数调用
(obj2.foo = obj1.foo)(); //Window

8.this绑定优先级

先说结论:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定

  • 显式与隐式绑定比较; 显式 > 隐式
//1.隐式绑定与call和apply比较
const obj = {
  name: "obj",
  foo: function () {
    console.log(this);
  },
};

obj.foo.call({ name: "call" }); //Object { name: "call" }

obj.foo.apply({ name: "apply" });//Object { name: "apply" }
//2.隐式绑定与bind比较
function foo() {
  console.log(this);
}

const obj = {
  name: "obj",
  foo: foo.bind({ name: "bind" }),
};

obj.foo();//Object { name: "bind" }
  • 显式绑定与new比较; new>显式

    注意:new关键字不能与applycall关键字一起使用,所以只用bind来进进行比较

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

const bind = foo.bind({ name: "bind" });
new bind(); //Object {  }

9.this面试题

var name = "window"
var person = {
  name: "person",
  sayName: function () {
    console.log(this.name);
  },
};

function foo() {
  var sayName = person.sayName;
  sayName(); //window
  person.sayName(); //person
  (person.sayName)(); //person
  (bar = person.sayName)(); //window
}

foo();
var name = "window";
var person1 = {
  name: "person1",
  foo1: function () {
    console.log(this.name);
  },
  
  foo2: () => console.log(this.name),
  
  foo3: function () {
    return function () {
      console.log(this.name);
    };
  },

  foo4: function () {
    return () => {
      console.log(this.name);
    };
  },
};

var person2 = { name: "person2" };

console.log("==================== foo1 ====================");

person1.foo1(); //person1 独立函数调用

person1.foo1.call(person2); //person2 显式绑定

  
console.log("==================== foo2 ====================");

person1.foo2(); //window 箭头函数

person1.foo2.call(person2); //window 箭头函数



console.log("==================== foo3 ====================");

person1.foo3()(); //window 独立函数调用

person1.foo3.call(person2)(); //window 独立函数调用

person1.foo3().call(person2); //person2 显式绑定

  
console.log("==================== foo4 ====================");

person1.foo4()(); //person1 箭头函数

person1.foo4.call(person2)(); //person2 箭头函数

person1.foo4().call(person2); //person1 箭头函数
var name = "window";
function Person(name) {
  this.name = name;
  this.foo1 = function () {
    console.log(this.name);
  };
  this.foo2 = () => {
    console.log(this.name);
  };
  this.foo3 = function () {
    return function () {
      console.log(this.name);
    };
  };
  this.foo4 = function () {
    return () => {
      console.log(this.name);
    };
  };
}


var person1 = new Person("person1");

var person2 = new Person("person2");


console.log("========================= foo1 =========================");

person1.foo1(); //person1

person1.foo1.call(person2); //person2


console.log("========================= foo2 =========================");

person1.foo2(); //person1

person1.foo2.call(person2);//person1


console.log("========================= foo3 =========================");

person1.foo3()(); //window

person1.foo3.call(person2)(); //window

person1.foo3().call(person2); //person2


console.log("========================= foo4 =========================");

person1.foo4()(); //person1

person1.foo4.call(person2)(); //person2

person1.foo4().call(person2); //person1
var name = "window";
function Person(name) {
  this.name = name;
  this.obj = {
    name: "obj",
    foo1: function () {
      return function () {
        console.log(this.name);
      };
    },
    foo2: function () {
      return () => {
        console.log(this.name);
      };
    },
  };
}

var person1 = new Person("person1");
var person2 = new Person("person2");

console.log("======================== foo1 =================================");

person1.obj.foo1()(); //window

person1.obj.foo1.call(person2)(); //window

person1.obj.foo1().call(person2); //person2


console.log("======================== foo2 =================================");

person1.obj.foo2()(); //obj

person1.obj.foo2.call(person2)(); //person2

person1.obj.foo2().call(person2); //obj