辅助理解 this 的一些代码片段

构造函数式实例化对象、数组和函数
typeof new Object() === 'object' // true
Array.isArray(new Array()) // true
typeof new Function() === 'function' // true
字面量写法实例化对象、数组和函数
typeof {} === 'object' // true
Array.isArray([]) // true
typeof function () {} === 'function' // true
构造函数 return 可以返回狭义对象、数组和函数等,用于说明“ this 总是返回一个引用类型的数据,如:object、array、function 等。”
function NumberList(...args) {
  if (args.some((item) => typeof item !== 'number')) {
    throw new Error('input params must be number!')
  } else {
    return [...args]
  }
}

new NumberList(1, 2) // [1, 2]
new NumberList(1, '') // Error
apply 可以动态切换 this 指向
const publicArea = {
  zhangsan: ['路人张三1', '路人张三2', '路人张三3'],
}

const zhangxiaohua = {
  zhangsan: '张三',
}

function sayZhangsan() {
  console.log(this.zhangsan)
}


sayZhangsan.apply(publicArea) // ['路人张三1', '路人张三2', '路人张三3']
sayZhangsan.apply(zhangxiaohua) // '张三'
sayZhangsan() // undefined
自由变量逐渐和顶层对象(全局对象)脱钩
function foo() {
  console.log(this.bar)
}

// const bar = 'bar' // 在 foo() 中无法获取
bar = 'bar' // 等价于: globalThis.bar = 'bar'

foo() // 'bar'
this 在“老式”的表单自动校验方法中的应用,用于说明:“JavaScript 中一切皆对象,函数都是在某个对象中运行,this 就是函数运行时所在的对象(环境、上下文)。”
<input type="text" name="age" size=3 onChange="validate(this, 18, 99);">

<script>
function validate(obj, lowval, hival){
  if ((obj.value < lowval) || (obj.value > hival))
    console.log('Invalid Value!');
}
</script>
引用类型与属性描述对象演示
const obj = { foo: 0 }
{
  foo: {
    [[value]]: 5
    [[writable]]: true
    [[enumerable]]: true
    [[configurable]]: true
  }
}
对象方法在内存中的存储情况
const obj = { foo: 0 }
{
  foo: {
    [[value]]: 函数的地址
    ...
  }
}
演示对象方法的 this 灵活变化时的效果
const lilei = {
  name: 'lilei',
  toString() {
    return this.name.toUpperCase()
  },
}

const zhangsan = {
  name: 'zhangsan',
}

lilei.toString() // 'LILEI'
lilei.toString.call(zhangsan) // 'ZHANGSAN’
通过 this 获取顶层对象
// node
console.log(this) // global

// browser
console.log(this) // window
通过 this 生成实例对象
function _new(/* 构造函数 */ constructor, /* 构造函数参数 */ params) {
  // 将 arguments 对象转为数组
  var args = [].slice.call(arguments);
  // 取出构造函数
  var constructor = args.shift();
  // 创建一个空对象,继承构造函数的 prototype 属性
  var context = Object.create(constructor.prototype);
  // 执行构造函数
  var result = constructor.apply(context, args); // --- 这句是重点 ---
  // 如果返回结果是对象,就直接返回,否则返回 context 对象
  return (typeof result === 'object' && result != null) ? result : context;
}

// 实例
var actor = _new(Person, '张三', 28);
对象方法中使用 this 时不太好掌握的情况
const obj = {
  foo() {
    console.log(this)
  },
}

obj.foo(); // obj

;(obj.foo = obj.foo)() // window
;(false || obj.foo)(); // window
;(1, obj.foo)(); // window
this 不会继承更高一层,只会到当前层为止
const obj = {
  foo: {
    bar() {
      console.log(this)
    },
  },
}

obj.foo.bar() // bar
多层 this 容易造成混淆
const obj = {
  foo() {
    console.log(this) // obj
    ;(function () {
      console.log(this) // window
    })()
  },
}
用 that 固定 this,常用在某个函数中需要编写回调函数逻辑时
const obj = {
  foo() {
    console.log(this) // obj
    const that = this
    ;(function () {
      console.log(that) // obj
    })()
  },
}

obj.foo()
使用“严格模式”避免函数内部 this 指向全局对象
const obj = {
  foo() {
    'use strict'
    console.log(this) // obj
    ;(function () {
      console.log(this) // undefined or Error
    })()
  },
}

obj.foo()
map、forEach 等数组方法中使用 this 也得小心
const obj = {
  list: ['a', 'b', 'c'],
  val: 'X',
  toUpperCase() {
    console.log(
      this.list.map(function (item) {
        return `${this.val} ${item.toUpperCase()}`
      }),
    )
  },
}

obj.toUpperCase() // [ 'undefined A', 'undefined B', 'undefined C' ]
用中间变量固定 this 解决上例问题
const obj = {
  list: ['a', 'b', 'c'],
  val: 'X',
  toUpperCase() {
    const that = this
    console.log(
      this.list.map(function (item) {
        return `${that.val} ${item.toUpperCase()}`
      }),
    )
  },
}

obj.toUpperCase() // [ 'X A', 'X B', 'X C' ]
用 map、forEach 等的第二个参数固定 this 解决上上例问题
const obj = {
  list: ['a', 'b', 'c'],
  val: 'X',
  toUpperCase() {
    console.log(
      this.list.map(function (item) {
        return `${this.val} ${item.toUpperCase()}`
      }, this),
    )
  },
}

obj.toUpperCase() // [ 'X A', 'X B', 'X C' ]
用箭头函数解决上上上例问题
const obj = {
  list: ['a', 'b', 'c'],
  val: 'X',
  toUpperCase() {
    console.log(this.list.map((item) => `${this.val} ${item.toUpperCase()}`))
  },
}

obj.toUpperCase() // [ 'X A', 'X B', 'X C' ]
演示 Function.prototype.call() 的用法
function foo() {
  console.log(this.name)
}

const obj = {
  name: 'lilei',
}

globalThis.name = 'hanmeimei' // 在顶层对象中添加属性

foo.call(obj) // 'lilei'
foo.call(null) // 'hanmeimei'
Function.prototype.call() 中第一个参数应该是一个对象,如果传入 null、undefined 或不传,则自动指向全局对象
function foo() {
  console.log(this.name)
}

const obj = {
  name: 'lilei',
}

globalThis.name = 'hanmeimei' // 在顶层对象中添加属性

foo.call(obj) // 'lilei'
foo.call(globalThis) // 'hanmeimei'
foo.call() // 'hanmeimei'
foo.call(null) // 'hanmeimei'
foo.call(undefined) // 'hanmeimei'
Function.prototype.call() 第一个参数传入“值类型”时会将该值类型转为其对应的“包装对象”,然后让方法(函数)在包装对象内执行。这可以看成是一种获取“值类型”的“包装对象”的方法
function foo() {
  console.log(this)
}

foo.call(true) // [Boolean: true]
foo.call(5) // [Number: 5]
foo.call('five') // [String: 'five']
foo.call(Symbol(1)) // [Symbol: Symbol(1)]
foo.call(BigInt(2)) // [BigInt: 2n]
Function.prototype.call() 方法的一个应用场景是:调用对象的原生方法,用来解决某些场景下原生方法被覆盖的情况
const obj = {}
console.log(obj.hasOwnProperty('toString')) // false

// 覆盖掉继承的 hasOwnProperty 方法
obj.hasOwnProperty = function () {
  return true
}
console.log(obj.hasOwnProperty('toString')) // true

console.log(Object.prototype.hasOwnProperty.call(obj, 'toString')) // false
Function.prototype.apply() 的一些使用场景
Math.max.apply(null, [1, 2, 3, 4, 5, 6, 1]) // 获取数字数组中的最大值
Array.apply(null, [, , , , , 1, , , , ,]) // 将空元素变为 undefined
Array.prototype.slice.apply({ 0: 1, length: 1 }) // 将类数组对象转换为真正的数组
Function.prototype.bind() 在绑定事件监听函数时的注意点
element.addEventListener('click', o.m.bind(o)) // 每次都返回一个新函数,且匿名
element.removeEventListener('click', o.m.bind(o)) // 造成 remove 事件监听时是无效操作

// 下面是正确做法
var listener = o.m.bind(o);
element.addEventListener('click', listener)
//  ...
element.removeEventListener('click', listener)
Function.prototype.bind() 在回调函数固定 this 中的应用
let callback = function () {
  console.log(this.name)
}

const obj = {
  name: '张三',
  times: [1, 2, 3],
  print: function () {
    this.times.forEach(callback)
  },
}

callback = callback.bind(obj)

obj.print() // 张三 x 3
Function.prototype.call 结合 bind 方法改写某些原生方法的调用形式,注意,下面括号中的 Array.prototye.slice 是当作对象来看的,因为 function 也是对象,它有一个方法叫:bind,所以,下面的代码其实是这句的通用形式:Array.prototype.slice.call([1,2,3], 0, 1)
const slice = Function.prototype.call.bind(Array.prototype.slice)
const a = slice([1, 2, 3], 0, 1) // [1]

// 同理
const push = Function.prototype.call.bind(Array.prototype.push)
const pop = Function.prototype.call.bind(Array.prototype.pop)
const bind = Function.prototype.call.bind(Function.prototype.bind);
原文地址:https://www.cnblogs.com/aisowe/p/15245704.html