javascript之this绑定
基本知识
call-site
- 函数或子程序的
调用站点
并非是它们声明的地方,而是调用函数或者是通过动态调度调用的位置。
- 函数或子程序的
call-stack
- 调用堆栈是一个方法列表,按调用顺序保存所有在运行期被调用的方法
function baz(){
//调用栈是 `baz`
bar(); //`bar`的调用站点
}
function bar(){
//调用栈是 `baz`->`bar`
foo(); //`foo`的调用站点
}
function foo(){
//调用栈是 `baz`->`bar`->`foo`
console.log("foo");
}
baz(); //baz的调用站点
绑定规则
默认绑定
function foo(){ console.log(this.a); } var a=2; foo(); //2 function bar(){ var a=6; foo(); } bar(); //2
当
foo()
被调用时,this.a
决定全局变量a
。因为在这种情况下,this
与函数调用相关,this
始终指向全局对象如果在
strict mode
的影响下,全局对象将不会是默认绑定,相反this
将会被设置成undefined
function foo(){ "use strict"; console.log(this.a); } var a=2; foo(); //TypeError: this is undefined
隐式绑定
- 如果函数作为对象的方法调用,this指向的是这个上级对象,即调用方法的对象
function foo(){ console.log(this.a); } var obj1={ a:2, foo:foo }; obj1.foo() //2 var obj2={ a=42, obj1:obj1 } obj2.obj1.foo() //2
- 不管
foo
是在obj
中声明的还是作为引用加入的,这个函数都已经属于obj
对象
隐式失绑
- 当一个隐式绑定失去绑定转化为默认绑定时,,this绑定将会根据是否为
strict mode
变为全局对象或者是undefined
function foo(){ console.log(this.a); } function doFoo(fn){ //`fn`仅仅是一个`foo`的引用 fn(); //<-- call-site } var obj={ a:2, foo:foo }; var a="oops,global";//`a`同样也是一个全局对象的一个属性 doFoo(obj.foo); //"oops,global" setTimeout(obj.foo,100);//"oops,global"
- 当一个隐式绑定失去绑定转化为默认绑定时,,this绑定将会根据是否为
显式绑定
function foo(){ console.log(this.a); } var obj={ a:2 }; foo.call(obj); //2
通过显式绑定
foo.call()
,在调用foo
时允许我们强制将this
赋值为obj
如果你将一个基本的类型作为
this
的绑定值,那么这些基本值会转换成相应的形式(new String(..)
,new Boolean(..)
,new Number(..)
),这通常被叫做装箱
硬绑定
- 硬绑定是显式绑定的一种形式,这种绑定易读并且健壮
function foo(){ console.log(this.a); } var obj={ a:2, foo:foo }; var bar=function(){ foo.call(obj); }; bar(); // 2 setTimeout(bar,100); // 2 //`bar`使得`foo`的`this`始终与`obj`相绑定,并且不能被覆盖 bar.call(window); //2
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); // 2 3 console.log(b) // 5
- 硬绑定是一种常用的模式,在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); // 2 3 console.log(b); // 5
API调用环境
- 许多库函数和许多javascript语言里新增的内嵌函数提供了一个可供选择的参数,通常叫做’上下文‘,它被用来确保你的回调函数在不使用
bind(..)
的情况下使用一个指定的this
function foo(el){ console.log(el,this.id); } var obj={ id:"awesome" }; //对于`foo`的调用使用`obj`作为`this` [1,2,3].forEach(foo,obj); // 1 awesome 2 awesome 3 awesome
- 许多库函数和许多javascript语言里新增的内嵌函数提供了一个可供选择的参数,通常叫做’上下文‘,它被用来确保你的回调函数在不使用
new
绑定在传统的面向对象语言中,构造器是类的一个特殊的方法,当类通过
new
运算符来实例化时,构造器通常像这样被调用something = new MyClass(..);
。javascript也有new
运算符,但与此不同。在javascript中,构造器仅仅是和new
运算符一起调用的函数,它不和类关联,也不能初始化对象,仅仅是常规的函数。举个例子,当Number(..)
函数被调用作为new
运算符的一部分,它作为一个构造器初始化一个新创造的对象。当一个函数
Foo
和new
运算符一起被触发,或者说作为构造器被调用,下列的事情将会被自动完成- 一个继承自
Foo.prototype
的新对象被创建。 - 使用指定的参数调用构造函数
Foo
,并将this
绑定到新创建的对象。new Foo
等同于new Foo()
,也就是没有指定参数列表,Foo
不带任何参数调用的情况。 - 由构造函数返回的对象就是
new
表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
function Foo(a){ this.a=a; } var bar=new Foo(2); console.log(bar.a); // 2
- 一个继承自
绑定规则优先级
new
绑定- 显式绑定
- 隐式绑定
- 默认绑定