调用位置
- 调用位置就是函数在代码中被调用的位置,而不是声明的位置;
- 最重要的是 分析调用栈;
注意是如何从调用栈中分析出真正的调用位置的,它决定了this的绑定;我们关心的调用位置就在当前正在执行的函数的前一个调用中:
function baz() { // 当前调用栈是:baz // 因此,当前调用位置是全局作用域 console.log("baz"); bar(); // bar的调用位置 } function bar() { // 当前调用栈是 baz -> bar // 因此,当前调用位置在baz中 console.log('bar'); foo(); // foo 的调用位置 } function foo() { // 当前调用栈是 baz -> bar -> foo // 因此,当前调用位置在bar中 console.log("foo"); } baz(); // baz的调用位置
绑定规则
- 4种规则:默认绑定、隐式绑定、显式绑定、new绑定
- 4种一则的优先级:new绑定、显示绑定、隐式绑定、默认绑定
默认绑定
- 无法应用其他规则时的默认规则
- 例子
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
隐式绑定
- 下面一段代码中,需要注意foo()的声明方式,无论是直接在obj中定义,还是先定义再添加为引用属性,这个函数严格来说都不属于obj对象。
- 调用位置会使用obj上下文来引用函数
- 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
function foo() {
console.log( this.a );
}
var obj = {
a:2,
foo: foo
}
obj.foo(); //2
- 对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
}
var obj1 = {
a:2,
obj2: obj2
}
obj1.obj2.foo(); // 42
- 隐式丢失
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
}
var bar = obj.foo;
var a = "opps";
bar(); // opps
function foo() {
console.log( this.a );
}
function doFoo(Fn) {
// fn其实引用的是foo
fn(); // 调用位置
}
var obj = {
a: 2,
foo: foo
}
var a = "opps";
doFoo( obj.foo ); // "opps"
显示绑定
- JavaScript提供的绝大多数函数已经自己创建的函数都可以使用call(…)和apply(…)方法。
- 它们到第一个参数时一个对象,是给this准备的,接着在调用函数时将其绑定到this。
- 硬绑定
function foo() {
console.log(this.a);
}
var obj = {
a : 2
}
var bar = function() {
foo.call(obj);
}
bar(); //2
setTimeout(() => {
bar(); //2
}, 100);
bar.call(window); //2
- 硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值
function foo(something) {
console.log(this.a, something);
return this.a + something;
}
var obj = {
a:2
}
var bar = function(){
return foo.apply(obj, arguments);
}
var b = bar(3); // 2 3
console.log(b); // 5
- 创建一个可以重复使用的辅助函数
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。
new绑定
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面操作:
- 创建(构造)一个全新的对象
- 这个新对象会执行[[Prototype]]连接
- 这个新对象会绑定到函数调用时的this
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); //2