• 辅助理解 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);
    
  • 相关阅读:
    十五、函数助手
    REST介绍以及常用的返回状态码
    十四、配置元件之计数器
    十三、参数化
    十二、jmeter目录结构
    十一、HTTP请求之Content-Type
    十、元件的作用域与执行顺序
    九、配置命令行模式
    04-传输层(3)
    03-传输层(2)
  • 原文地址:https://www.cnblogs.com/aisowe/p/15245704.html
Copyright © 2020-2023  润新知