绑定规则
this的绑定规则有四种
- 默认绑定 (全局对象或者undefined(严格模式下))
- 隐式绑定(调用时函数上下文对象,obj.fun())
- 显式绑定(call , apply , bind(硬绑定))
- new绑定(新建的对象会被绑定到构造函数中的this上)
详细介绍
1 . 默认绑定
调用没有任何修饰函数时引用默认绑定,this指向全局对象
但是如果定义时使用严格模式(use strict),则不能将全局对象用于默认绑定,因此this会绑定到undefined,而在严格模式下调用却不影响
2.隐式绑定
调用位置的上下文对象决定,调用函数中的this会指向最后一层调用对象
function foo() {
console.log(this.name);
}
var obj = {
name: "obj",
foo: foo
}
obj.foo();//obj
但是隐式绑定存在着一个隐患(隐式丢失)
var name = "global";
var bar = obj.foo;
bar();//global
虽然bar是obj.foo的一个引用,但是实际上引用的是foo函数本身,此时bar()实际上是变成foo(),应用了默认绑定
另一种隐式丢失的情况出现在参数传递中
function doFoo(fn) {
fn();
}
doFoo(obj.foo) //global
因为参数传递时也会发生隐式赋值 fn = obj.foo
3.显式绑定
function.call(...)
function.apply(...)
这两个函数区别在于第二部分的参数,具体可以看我之前的博客,现在先不讨论这个问题
这两个函数的作用都是将第一个参数绑定到function中的this上,如果第一个参数是个原始值(String Boolean 或者Number),这个原始值会转换成对象形式(new String(..)等),这通常被称为装箱
function foo() {
console.log(this.name);
}
var obj = {
name: "obj",
foo: foo
}
foo.call(obj);//obj
然而显式绑定依然存在着我们之前提出的丢失绑定的问题
但是显式绑定的的一个变种可以解决这个问题(硬核绑定)
function foo() {
console.log(this.name);
}
var obj = {
name: "obj",
foo: foo
}
var bar = function() {
foo.call(obj);
}
bar(); //obj
这种方式其实就是ES5中的bind()方法
bind方法与call方法的参数形式是一样的,不同之处在于bind只是返回一个硬绑定了this对象的函数,而call则是绑定了函数中的this对象后执行函数(即使调用call函数的函数本身返回自己,this绑定的对象依然会丢失)
4.new绑定
最后一条this的绑定规则与构造函数有关
JavaScript中的构造函数实际上只是一个使用了new操作符的普通函数
JavaScript实际上不存在所谓的"构造函数",存在的只有对函数的"构造调用"
当函数在new表达式中被调用时,它会初始化新创建的对象
发生的步骤如下
- 创建(构造)一个全新的对象
- 这个新对象会被执行[[Prototype]]连接(不是本篇讨论的问题,暂时可以忽略)
-
这个新对象会被绑定到函数调用的this.
- 如果函数没有返回其他对象,则new表达式会自动返回这个新对象
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);//2