JavaScript---简述this的几种绑定形式

绑定规则

下面几种绑定方式, 除了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绑定

使用new来调用函数, 或者说发生构造函数调用时, 会自动执行下面的操作

  1. 创建(或者构造)一个全新的对象
  2. 这个新对象会被执行 [[原型]] 连接, 也就是这个对象的_ proto _指向构造函数的prototype
  3. 这个新对象会绑定到函数调用的this
  4. 如果函数没有返回其他对象, 那么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

this词法作用域es6

就是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也不行)

猜你喜欢

转载自blog.csdn.net/c_kite/article/details/79905308