this

265 阅读3分钟

this的绑定规则

1. 默认绑定

  • 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;
//1.该函数直接被调用,并没有进行任何的对象关联;
//2.这种独立的函数调用会使用默认绑定,通常默认绑定时,函数中的this指向全局对象(window)

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

foo();// window

2. 隐式绑定

  • 另外一种比较常见的调用方式是通过某个对象进行调用的:也就是他的调用位置中,是通过某个对象发起的函数调用。
//案例1
function foo() {
  console.log(this); 
}

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

obj.foo();// obj对象

//案例2
function foo() {
  console.log(this); 
}

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

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

obj2.obj1.foo();// obj1对象


//案例3:隐式丢失
function foo() {
  console.log(this);
}

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

// 将obj1的foo赋值给bar
var bar = obj1.foo;
bar(); //window

3. 显示绑定

  • 隐式绑定有一个前提条件:必须在调用的对象内部有一个对函数的引用(比如一个属性),如果没有这样的引用,在进行调用时,会报找不到该函数的错误;正是通过这个引用,间接的将this绑定到了这个对象上;
  • 如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,我们应该使用==call,apply方法==,JavaScript所有的函数都可以使用call和apply方法
    • ==call和apply的区别==:call第二个参数需要把实参按照形参的个数传进去,apply则传入一个形参数组。
function foo() {
  console.log(this);
}

foo.call(window); // window
foo.call({name: "why"}); // {name: "why"}
foo.call(123); // Number对象,存放时123
  • 使用bind函数,让一个函数总是显示的绑定到一个对象上
  1. 手写bind()
function bind(func, obj){
    return function() {
        return func.apply(obj, arguments)
	}
}

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

var obj = {
  name: "why"
}

var bar = bind(foo, obj);
bar(); // obj对象
  1. 使用 Function.prototype.bind
function foo() {
  console.log(this);
}

var obj = {
  name: "why"
}

var bar = foo.bind(obj);

bar(); // obj对象

4. 内置函数的this指向

// 1. setTimeout内部是通过apply进行绑定的this对象,并且绑定的是全局对象
setTimeout(function() {
  console.log(this); // window
}, 1000);


// 2. 数组的forEach:默认情况下传入的函数是自动调用函数(默认绑定)
var names = ["abc", "cba", "nba"];
names.forEach(function(item) {
  console.log(this); // 三次window
});

var obj = {name: "why"};
names.forEach(function(item) {
  console.log(this); // 三次obj对象
}, obj);

// 3.dom事件
var box = document.querySelector(".box");
box.onclick = function() {
  console.log(this); // box对象:这是因为在发生点击时,执行传入的回调函数被调用时,会将box对象绑定到该函数中
}

5. new绑定

  • 使用new关键字来调用函数时:会执行如下操作:

    1. 创建一个全新的对象;
    2. 这个新对象会被执行Prototype连接;
    3. 这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成)
    4. 如果函数没有返回其他对象,表达式会返回这个新对象
    // 创建Person
    function Person(name) {
      console.log(this); 
      this.name = name; 
    }
    
    var p = new Person("why");
    console.log(p);// Person {name: "why"}
    

6. 规则的优先级

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

this规则之外的绑定

1. 忽略显示绑定

  • 如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则:
function foo() {
  console.log(this);
}

var obj = {
  name: "why"
}

foo.call(obj); // obj对象
foo.call(null); // window
foo.call(undefined); // window

var bar = foo.bind(null);
bar(); // window

2. 间接函数引用

  • 创建一个函数的间接引用,这种情况使用默认的绑定规则。
var num1 = 100;
var num2 = 0;
var result = (num2 = num1);
console.log(result); // 100

function foo() {
  console.log(this);
}
var obj1 = {
  name: "obj1",
  foo: foo
}; 
var obj2 = {
  name: "obj2"
}
obj1.foo(); // obj1对象
(obj2.foo = obj1.foo)();  // window

3. ES6箭头函数

  • 箭头函数没有自己的this对象,函数内部的this指向外层作用域的this