关于this
的误解
- 指向自身
- 它的作用域
指向自身
如果要从函数对象内部引用它自身,那只用this
是不够的。一般来说我们需要通过一个指向函数对象的词法标识符(变量)来引用它。
//不懂this的指向
function foo(num) {
console.log('foo:' + num);
// console.log(this); //此时this指向window
this.count++;
// foo.count++; //直接用函数的标识符来代替this引用函数对象,可解决。但是回避了this指向的问题
}
foo.count = 0;
for (var i = 0; i < 10; i++) {
if(i > 5) {
foo(i);
//但是使用call()来强制this指向函数本身,也可解决。 我们在面对this的问题
// foo.call(foo, i);
}
}
console.log(foo.count); //结果为0,而不是4
它的作用域
需要明确的是,this
在任何情况下都不指向函数的词法作用域。每当自己想要把this
和词法作用域的查找混合使用的时候,就应该提醒自己,这是无法实现的。
//完美尴尬案例
function foo() {
var a = 3;
console.log(this.bar());
}
function bar() {
return this.a
}
foo(); // 出错
this
到底是什么
它不是什么
学习this
的第一步就是明白它既不指向函数自身,也不指向函数的词法作用域。
this
的绑定是在运行时进行绑定的,而不是在编写时绑定,和函数声明位置没有任何关系,只取决于函数的调用方式,即调用位置。
调用位置
调用位置,就是函数被调用的位置。而有些编程模式可能会隐藏真正的调用位置。
最重要的是分析调用栈,调用位置就在当前正在执行函数的前一个调用中。
function baz() {
console.log('baz');
bar();
}
function bar() {
console.log('bar');
foo();
}
function foo() {
console.log('foo'); // 当前调用栈是baz ->bar -> foo
}
baz();
this
的绑定规则
默认绑定
最常用的函数调用:独立函数调用。即函数是直接使用不带任何修饰的函数引用进行调用的。
// 默认绑定
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // foo是直接使用不带任何修饰的函数引用所调用的
var width = 600;
var shape = {
width : 100
}
var showWidth = function() {
console.log(this.width);
}
shape.getWidth = showWidth;
shape.getWidth(); // 前面有修饰,结果为100
var myWidth = shape.getWidth;
myWidth(); // 前面没有修饰,结果为600
隐式绑定
当函数引用有上下文对象时,就会通过隐式绑定规则把函数调用中的this
绑定到这个上下文对象上。可以观察调用位置是否被某个对象拥有或者包含。
// 隐式绑定
function foo() {
console.log(this.a);
}
var obj = {
a : 2, //this被隐式绑定在这个对象里
foo : foo
}
obj.foo();
隐式丢失问题
最常见的就是隐式绑定的函数会丢失绑定对象,回到默认绑定。
// 隐式绑定丢失情况
function foo() {
console.log(this.a);
}
var obj = {
a : 2,
foo : foo
}
var bar = obj.foo; //这里创建了一个函数的别名
// 虽然bar是obj.foo的引用,但是它引用的其实是foo本身
var a = '全局下的a';
bar(); // 结果是this又跑到全局去下了
当函数被当作参数传递时,就会发生隐性赋值,从而产生会上面一样的结果。
// 当函数被当作参数传递时,会发生隐性赋值的情况
function foo() {
console.log(this.a);
}
function doFoo(fn) {
fn(); //调用位置在这,其实fn引用的就是foo
}
var a = "a在全局下";
var obj = {
a : "a在对象里",
foo : foo
};
doFoo(obj.foo);
硬绑定
我们可以在某个对象上,强制调用函数。
- 典型使用场景,创建一个包裹函数,负责接收参数并返回值。
function foo(something) {
console.log(this.a , something);
return this.a + something;
}
var obj = {
a : 2
};
var bar = function() {
return foo.apply(obj, arguments); //显式的硬绑定,包裹foo
};
var b = bar(3);
console.log(b);
- 创建一个可以重复使用的辅助函数
function foo(something) {
console.log(this.a , something);
return this.a + something;
}
// 简单的辅助绑定函数
function bind(fn, obj) {
return function() {
return fn.apply(obj, arguments);
};
}
var obj = {
a : 2
};
var bar = bind(foo, obj);
var b = bar(3);
console.log(b);
- 其实我们有内置方法
bind
//ES5给硬绑定提供了一个内置方法Function.prototype.bind
function foo(something) {
console.log(this.a , something);
return this.a + something;
}
var obj = {
a : 2
};
var bar = foo.bind(obj);
var b = bar(3);
console.log(b);
new
绑定
最后一个绑定规则。
function foo(a) {
this.a = a;
console.log(this); // this指向foo函数
}
var bar = new foo(2); // new会创建一个新对象并绑定到函数调用的this上
console.log(bar.a)
绑定规则优先级
new
绑定 => 显示绑定 => 隐式绑定 => 默认绑定
[TOC]
网友评论