zhangdizhangdi

this

定义

当一个函数被调用时,会创建执行上下文,包含函数在哪里被调用、函数的调用方式、传入的参数等信息。this就是某中一个属性,会在函数执行的过程中用到。

this是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

参考

绑定规则

正常函数

优先级由高到低

  • new绑定,绑定创建的对象
  • 显示绑定(call,apply),绑定指定的对象
  • 隐式绑定,某个上下文对象中调用,绑定上下文对象
  • 默认绑定,绑定全局对象

箭头函数

编译时绑定的,根据外层(函数或全局)作用域来决定,箭头函数的绑定无法被修改

  • 没有this、arguments、super
  • this指向永远不变,
  • 不能用作构造函数,没有new.target
  • 没有原型
参考
参考

题目

js
var name = 'window'
var obj = {
  name: 'obj',
  prop: {
    getName: function () {
      return this.name
    },
  },
  getName: function () {
    return this.name
  },
}
// console.log(obj.prop.getName())     undefined
// console.log(obj.getName())          obj
var a = obj.prop.getName
// console.log(a())                    window
js
var val = 10
let a = function () {
  console.log(this.val)
}
a.prototype.val = 9

val = 3
a() // 3
let b = new a() // 9
console.log(b.val) // 9
js
var name = 'window'

var person1 = {
  name: 'person1',
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  },
}

var person2 = { name: 'person2' }

person1.show1() // person1
person1.show1.call(person2) // person2

person1.show2() // window
person1.show2.call(person2) // window

person1.show3()() // window
person1.show3().call(person2) // person2
person1.show3.call(person2)() // window

person1.show4()() // person1
person1.show4().call(person2) // person1
person1.show4.call(person2)() // person2
js
var name = 'window'

function Person(name) {
  this.name = name
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person('personA')
var personB = new Person('personB')

personA.show1() // personA
personA.show1.call(personB) // personB

personA.show2() // personA
personA.show2.call(personB) // personA

personA.show3()() // window
personA.show3().call(personB) // personB
personA.show3.call(personB)() // window

personA.show4()() // personA
personA.show4().call(personB) // personA
personA.show4.call(personB)() // personB
js
var num = 1
var myObject = {
  num: 2,
  add: function () {
    this.num = 3
    ;(function () {
      console.log(this.num)
      this.num = 4
    })()
    console.log(this.num)
  },
  sub: function () {
    console.log(this.num)
  },
}
myObject.add() // 1 3
console.log(myObject.num) // 3
console.log(num) // 4
var sub = myObject.sub
sub() // 4
js
var a = 2
var obj = {
  a: 4,
  fn1: (function () {
    this.a *= 2
    var a = 3
    return function () {
      this.a *= 2
      a *= 3
      console.log(a)
    }
  })(),
}

var fn1 = obj.fn1 // 【this -> window】
console.log(a) // 4
fn1() // 9 【闭包 this-> window】
obj.fn1() // 27
console.log(a) // 8
console.log(obj.a) //8
js
var n = 2 // -> 4 -> 8
var obj = {
  n: 3, // 6
  fn: (function (n) {
    n *= 2
    this.n += 2 // window 下的 n 变成 4
    var n = 5 // 这一步不会再重新声明,因为已经参数赋值,就不会再声明而是直接赋值 n = 5
    console.log('window.n', window.n)
    return function (m) {
      console.log('n:', n, 'm', m) // n:5  m:3  这里的 n 向上查找是 5   //
      this.n *= 2 // fn(3): 2 * 4 =8  //  obj.fn(3): 2 * 3 = 6
      console.log(m + ++n) // 3 + (++5) ++n 导致上级作用域的n变成了6    // 3 + (++6)
    }
  })(n),
}

var fn = obj.fn
fn(3) // 9
obj.fn(3) // 10
console.log(n, obj.n) // 8 6
js
var num = 10 // 60; 65
var obj = {
  num: 20,
}
obj.fn = (function (num) {
  this.num = num * 3
  num++ // 21
  return function (n) {
    this.num += n // 60 + 5 = 65;20 + 10 =30
    num++ // 21 + 1 = 22;22 + 1 = 23
    console.log(num)
  }
})(obj.num)

var fn = obj.fn
fn(5) // 22
obj.fn(10) // 23
console.log(num, obj.num) // 65, 30

call、apply

js
Function.prototype._call = function (ctx) {
  // 如果不为空,则需要进行对象包装
  const o = ctx ? Object(ctx) : window
  // 给 ctx 添加独一无二的属性
  const fn = Symbol()
  o[fn] = this

  // 执行函数,得到返回结果
  const args = [...arguments].slice(1)
  const result = o[fn](...args)

  // 删除该属性
  delete o[fn]
  return result
}
js
Function.prototype._apply = function (context, arr) {
  context = context ? Object(context) : window
  context.fn = this //用symbol

  let result
  if (!arr) {
    result = context.fn()
  } else {
    result = context.fn(...arr)
  }

  delete context.fn
  return result
}
参考

bind

js
Function.prototype._bind = function (context) {
  if (typeof this !== 'function') {
    throw new Error(
      'Function.prototype.bind - what is trying to be bound is not callable',
    )
  }

  var self = this
  var args = Array.prototype.slice.call(arguments, 1)

  var fNOP = function () {}

  var fBound = function () {
    var bindArgs = Array.prototype.slice.call(arguments)
    return self.apply(
      this instanceof fNOP ? this : context,
      args.concat(bindArgs),
    )
  }

  fNOP.prototype = this.prototype
  fBound.prototype = new fNOP()
  return fBound
}
js
var bar = function () {
  console.log(this.x)
}
var foo = {
  x: 3,
}
var sed = {
  x: 4,
}
var func = bar.bind(foo).bind(sed)
func() // 3

var fiv = {
  x: 5,
}
var func = bar.bind(foo).bind(sed).bind(fiv)
func() // 3
参考