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);
-
this 可以对多个环境对象进行复用, 不用针对每个环节对象做各自的版本。 -
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();
或者是
function foo() {console.log(this);}
var obj = {
fn: foo
}
obj.fn();
虽然第二种情况的 foo 函数不是一开始就在 obj 身上,后来作为引用添加到了 obj 身上,不能说是 obj 有了这个函数,而是当 fn 函数调用的时候,才能说 obj 真正的拥有了 foo 函数。
但是存在一个问题,这种隐式绑定容易丢失。
var obj = {
fn: function () {console.log(this);}
}
var fn = obj.fn;
fn();
或者是
var obj = {
fn: function () {console.log(this);}
}
setTimeout(obj.fn, 1000);
这种情况下, 通过赋值 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);
foo();
我们自己写的函数我们可以控制,但是对于别人的函数我们就不能够控制 this 绑定了
function foo() {console.log(this)}
var obj = {};
function myFun(fn) {console.log('我的函数', fn.call(obj))}
myFunc(foo);
别人的代码, 那么我们就不能够进入到内部去改变指向了
function foo() {console.log(this);}
var obj = {};
setTimeout(foo, 1000);
硬绑定
看上面代码,我们发现 call, apply 存在不是一次性绑定,不能解决 this 丢失问题, 硬绑定是一次性的绑定,并且可以解决 this 指向丢失问题。
一次性问题
function foo() {console.log(this);}
var obj = {};
var bar = foo.bind(obj);
bar();
bar();
丢失问题
function foo() {console.log(this);}
var obj = {};
setTimeout(foo.bind(obj), 1000);
解决了,我们可以理解为,通过 foo.bind 包装了一个绑定到 obj 的函数,然后传入到定时器。
new 绑定
使用 new 来初始化一个类时,这个类的构造器就会被调用。
function Person(name) {
this.name = name;
}
var person = new Person('xx');
我们使用 new 来执行构造器,构造器的 this 会绑定为 person 这个对象。往 person 上添加 name 属性。
这么多绑定,总要有一个优先级排序吧(覆盖)
- 不用说肯定是 默认绑定 最低了吧,毕竟没有人罩着。
- 隐式绑定 obj.foo 方式。
- 明确绑定
function foo() {console.log(this);}
var obj1 = {fn: foo}
var obj2 = {fn: foo}
obj.fn.call(obj2);
- 硬绑定
function foo() {console.log(this.name);}
var obj1 = {fn: foo, name: 'obj1'};
var obj2 = {fn: foo, name: 'obj2'};
foo.bind(obj2).call(obj1);
- new 绑定
function Person() {console.log(this)}
var obj = {};
var Person1 = Person.bind(obj);
var person = new Person1();
这主要是因为在 bind 源码中是: this instanceof fNOP ? this : oThis; 如果是 函数的话,就会返回这个 new 出来的对象,否则才是 bind 绑定的对象。 (要想了解,看 bind 章节)。
|