面试总结之《this深入浅出》

517 阅读3分钟

总结:

以下的几种绑定都离不开广为流传的一句话: this指向调用者

  1. 不论函数定义在哪个作用域, 是作为变量(var attr)还是对象属性, 函数中的this, 只需要关注调用时是否有调用者, 如果有便指向 调用者 , 没有便指向 window, 严格模式下指向 undefined;
  2. call, apply, bind。他们能显示的改变 this 的指向。
  3. new 调用的函数中的 this 指向当前实例。

注意: 不需要关注函数是在作为什么方式创建, 只需要关注调用时的调用者。

看到这里这篇文章已基本算结束了, 下面是一些详细的内容(选看):

1. 默认绑定

独立函数调用时(没有显式的调用者), this 指向全局对象。 thisnullundefined 都会指向 window

严格模式下, 函数无法使用默认绑定, this 绑定至 undefined

//无论foo是在全局作用域, 函数作用域, 或是作为回调函数, 没有显式的调用者都看做默认绑定
function foo() {
    console.log(this);
}
foo();//this 指向 Window 

2. 隐式绑定

当函数作为对象的方法, 或 dom 的事件驱动函数时, 隐式绑定规则会把函数调用中的 this 绑定到相关的上下文对象(也就是我们经常说的 this 指向调用者 )。

var obj = {
    foo() {
        console.log(this);
    }  
};
obj.foo(); //this 指向 obj

事件作为标签的属性定义: <p onclick="myClick"></p>, 不会隐式绑定this, 所以一般我们看到在标签上绑定事件时, 都会传递this作为参数: <p onclick="myClick(this)"></p>


3. 隐式丢失

隐式丢失指函数中的 this 丢失绑定对象, 它会应用第1条的默认绑定规则。以下情况会发生隐式丢失:

  • 作为对象的方法被赋值给一个新的函数, 然后调用这个函数时。
var obj = {
    foo: function() {
        console.log(this);
    }
}
var foo = obj.foo;
foo() // window
  • 对象的方法, 当作回调时:
var obj = {
    foo() {
        console.log(this);
    }
};
function doFoo(fn) {
    fn();
}
doFun(obj.foo); //window

这里就印证了上述所说的, 只需要关注调用时有没有显式的调用者。


4. 显示绑定

  • callapply 指定 this , 称为显示绑定。 如果 callapply 的第一个参数( 指定this的参数 )为基本类型, 则会转为包装类型。

直接使用显示绑定无法解决绑定丢失(隐式丢失)问题。例:

var obj = {};
foo.call(obj);//this 指向 obj
foo();//this 指向 Window
function foo() {
    console.log(this);
}

也就是说显式绑定, 不是永久改变函数的this

  • 硬绑定: 硬绑定是显示绑定的一种方式, 他可以解决隐式丢失的问题。

典型的应用场景为创建一个包裹函数, 传入所有的参数并返回接收到的所有值:

var obj = {};
function foo(something) {
    console.log(this);
}
function bar() {
    return foo.call(obj, arguments)
}
bar(); //this 指向 obj
// 因为bar内部调用的函数绑定了this
// 所以无论如何指定bar的this
// 都不会影响内部指定的this

由于硬绑定比较常见, 所以 ES5 提供了 Function.prototype.bind , 通过 bind 绑定的对象等于硬绑定了 this

var obj = {};
function foo(){
    console.log(this);  
}
var bar = foo.bind(obj);
bar();//this指向obj
bar.call(this);//显式绑定也无法改变bar的this指向
  • API 中的显示绑定: JS 自身以及许多第三方库的函数都提供了一个可选的参数, 用于指定回调函数的 this 其作用和 bind 的相似。
var arr = [];
arr.forEach(function() {
    // 在函数中直接调用的回调一般都会发生隐式丢失,
    // 所以在非严格模式下会指向window
    // 若指定了forEach的第二个参数, 会指定回调中this的指向]
    // 其本质仍是显式绑定
    console.log(this);
}, obj(指定this))

5. new 绑定

this 指向当前实例。

注意: 构造函数中若返回自定义的对象作为实例, 则该实例与构造函数无关:

function P(name) {
    this.name = name;
    return obj; 
}
P.prototype.foo = function() {
    console.log('foo...');
};
let obj = {
    age: 18
};
let obj1 = new P('zs');
// __proto__指向Object的prototype, 没有name属性, 也没法调用foo方法
obj1.name; // undefined
obj1.foo; //undefined