下面几种绑定方式, 除了es6的箭头函数, 都属于动态作用域, es6箭头函数里面的this采用的静态作用域
function foo() {
console.log(this.a);
}
var a = 2;
foo(); // 2
其实很简单, 这里只要看foo这个函数是由谁来调用的即可, 明显是window, 所以输出的是2, 如果使用严格模式(strict mode), 那么全局对象将无法使用默认绑定, 因此this会绑定到undefined, 参考下面代码
function foo() {
"use strict"
console.log(this.a);
}
var a = 2;
foo(); // TypeError: this is undefined
(多提一嘴TypeError和ReferenceError的区别, ReferenceError同作用域判别失败相关, TypeError则代表作用域判别成功了, 但是对结果操作是非法或不合理的)
和上一种默认绑定差不多, 主要看是谁调用的函数, 下面看一下例子
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
}
obj.foo(); // 2
因为调用foo时, this被绑定到obj, 因此this.a和obj.a是一样的, 下面看一个长一点的例子
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo,
}
var obj1 = {
a: 2,
obj2: obj2,
}
obj1.obj2.foo(); // 42 因为是obj2调用的函数
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo,
}
var bar = obj1.foo;
var a = 111;
bar(); // 111 因为是window调用的bar, bar拿到的只是foo函数的一个引用而已
call和apply以及es5的bind;
关乎call和apply的详细用法可以参考我的这篇文章
https://blog.csdn.net/c_kite/article/details/77860103
下面是一个小例子
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
}
foo.call(obj) // 2
使用new来调用函数, 或者说发生构造函数调用时, 会自动执行下面的操作
// 例子
function foo(a) {
this.a = a
}
var bar = new foo(2);
console.log(bar.a) // 2
new > 显示绑定 > 隐式绑定
下面是一个new和显示绑定比较的例子
function foo(a) {
this.a = a
}
var obj = {}
var bar = foo.bind(obj);
bar(2)
console.log(obj.a) // 2
var baz = new bar(3)
console.log(obj.a) // 2 重点
console.log(baz.a) // 3 重点
如果把null或者undefined作为this的绑定对象传入call, apply或者bind, 这些值在调用时会被忽略, 实际应用的事默认绑定规则
// 例子
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null); // 2
function foo(a) {
console.log(this.a, a);
return this.a + a
}
var obj = {
a: 3
}
var obj2 = {
a: 2
}
var bar = function () {
return foo.apply(obj, arguments)
}
var b = bar.call(obj2, 2); // 3 2 可以看到obj2的值没有用到
console.log(b); // 5
硬绑定这种方式可以把this强制绑定到指定的对象, 问题在于, 硬绑定会大大降低函数的灵活性, 使用硬绑定之后就无法使用隐式绑定或者显示绑定来修改this, 下面是一个软绑定的例子.
if (!Function.prototype.softBind) {
Function.prototype.softBind = function (obj) {
var fn = this;
// 捕获所有参数
var curried = [].slice.call(arguments, 1);
var bound = function () {
return fn.apply(
(!this || this === (window || global)) ? obj : this,
curried.concat.apply(curried, arguments)
)
}
bound.prototype = Object.create(fn.prototype);
// 等同于bound.prototype.__proto__ = foo.prototype
return bound
}
}
function foo() {
console.log(this.name)
}
var obj = {name: 'obj'}
var obj1 = {
name: 'obj1'
}
var obj2 = {
name: 'obj2'
}
var fooOBJ = foo.softBind(obj)
fooOBJ(); // obj
obj1.foo = foo.softBind(obj);
obj1.foo(); // obj1
fooOBJ.call(obj2); // obj2
setTimeout(obj1.foo, 10); // obj
就是es6的箭头函数, 看下面的例子
function foo() {
return (a) => {
console.log(this.a)
}
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = foo.call(obj1);
bar.call(obj2); // 2, 不是3
foo()内部创建的箭头函数会捕获调用时foo的this, 由于foo()的this绑定到obj1, bar(引用箭头函数)的this也会绑定到obj1, 箭头函数的绑定无法被修改, (new也不行)