详解 this

this 指向

如果我们将环境传入
function foo(context) {
    return context.toUpperCase();
}
function bar(context) {
    return context + '欢迎';
}
foo(环境1);
bar(环境2);


我们使用 this
function foo(context) {
    return this.toUpperCase();
}
function bar(context) {
    return this + '欢迎';
}
foo.call(环境1);
bar.call(环境2);
  1. this 可以对多个环境对象进行复用, 不用针对每个环节对象做各自的版本。

  2. this 以更加优雅的方式来隐含的传递一个引用,更加干净的API设计和更容易的复用。

怎么样来确定 this

this 是根据调用点(函数如何被调用的)而为每次函数调用建立的绑定, 它是影响 this 绑定的唯一因素。

默认绑定

当函数是独立调用的,也就是函数前面没有任何的调用条件。

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

foo();  // 独立调用


或者是
function foo() {
    setTimeout(function => {
        console.log(this);  // 独立调用
    }, 1000);
}

所以上面情况应用到默认绑定,在非严格模式下,this指向全局的 window,在严格模式下,this 是 undefined.

隐式绑定

当函数的调用点是一个环境对象,那么这个时候函数的this会指向这个对象。

var obj = {
    fn: function () {console.log(this);}
}
obj.fn();  // 隐式绑定 obj


或者是
function foo() {console.log(this);}
var obj = {
    fn: foo
}

obj.fn();  // 隐式绑定 obj

虽然第二种情况的 foo 函数不是一开始就在 obj 身上,后来作为引用添加到了 obj 身上,不能说是 obj 有了这个函数,而是当 fn 函数调用的时候,才能说 obj 真正的拥有了 foo 函数。

但是存在一个问题,这种隐式绑定容易丢失。

var obj = {
    fn: function () {console.log(this);}
}

var fn = obj.fn;
fn();  // 丢失了绑定 ---> 默认绑定 window


或者是
var obj = {
    fn: function () {console.log(this);}
}

setTimeout(obj.fn, 1000);  // 丢失了绑定 ---> 默认绑定 window

这种情况下, 通过赋值 fn = obj.fn 知识把 fn 函数的地址给了 fn 变量,虽然二者引用到了一块内存区域,但是调用的时候 fn 是没有绑定规则的。

明确绑定

如上面的,我们不得不改变目标对象使得它自身包含一个对象引用。然后来间接的调用这个函数,改变调用点。
如果你想要强制一个函数使用某个特定对象作为 this 绑定的话,就需要借助 apply,call 了。

function foo() {console.log(this);}
var obj = {};
或者 foo.call(obj);
或者 foo.apply(obj);

这样就可以使得 foo 调用的时候, 就可以我们自己来制定它的 this 是谁。比起需要一个专门的对象环境对象来作为它的 this 好简单的多。

apply、call 存在的问题。它们依然不能够上面的绑定丢失问题。而且它们是一次性绑定。

一次性绑定
function foo() {console.log(this);}
var obj = {};
foo.call(obj); // obj 好使
foo(); // window 又回去了

我们自己写的函数我们可以控制,但是对于别人的函数我们就不能够控制 this 绑定了
function foo() {console.log(this)}  // obj 好使
var obj = {};
function myFun(fn) {console.log('我的函数', fn.call(obj))}
myFunc(foo);


别人的代码, 那么我们就不能够进入到内部去改变指向了
function foo() {console.log(this);}  // 不好使 window
var obj = {};
setTimeout(foo, 1000);

硬绑定

看上面代码,我们发现 call, apply 存在不是一次性绑定,不能解决 this 丢失问题, 硬绑定是一次性的绑定,并且可以解决 this 指向丢失问题。

一次性问题
function foo() {console.log(this);}
var obj = {};

var bar = foo.bind(obj);
bar();  // 好使 obj
bar();  // 好使 obj

丢失问题
function foo() {console.log(this);}
var obj = {};

setTimeout(foo.bind(obj), 1000); // 好使 obj

解决了,我们可以理解为,通过 foo.bind 包装了一个绑定到 obj 的函数,然后传入到定时器。

new 绑定

使用 new 来初始化一个类时,这个类的构造器就会被调用。

function Person(name) {
    this.name = name;  // person
}

var person = new Person('xx');

我们使用 new 来执行构造器,构造器的 this 会绑定为 person 这个对象。往 person 上添加 name 属性。

这么多绑定,总要有一个优先级排序吧(覆盖)

  1. 不用说肯定是 默认绑定 最低了吧,毕竟没有人罩着。
  2. 隐式绑定 obj.foo 方式。
  3. 明确绑定
function foo() {console.log(this);}
var obj1 = {fn: foo}
var obj2 = {fn: foo}
obj.fn.call(obj2);  // obj2 没错了, 明确绑定确实比隐式绑定要强
  1. 硬绑定
function foo() {console.log(this.name);}
var obj1 = {fn: foo, name: 'obj1'};
var obj2 = {fn: foo, name: 'obj2'};
foo.bind(obj2).call(obj1);  // 好使 'obj2' 说明硬绑定要高于明确绑定
  1. new 绑定
function Person() {console.log(this)}
var obj = {};
var Person1 = Person.bind(obj);
var person = new Person1();  // 好使 person  new绑定要高于硬绑定

这主要是因为在 bind 源码中是: this instanceof fNOP ? this : oThis; 如果是 函数的话,就会返回这个 new 出来的对象,否则才是 bind 绑定的对象。 (要想了解,看 bind 章节)。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值