学好this,从容的翻过前端三座大山

81 阅读4分钟

this,前端三座大山之一

学会this

  • 前端不论是学习框架还是学习新的东西,都需要前置知识,所以学好this,为今后持续学习打好基础

  • 这里只讨论JavaScript的this,它是JavaScript中的一个关键字

  • 同时也是当前环境执行期上下文对象的一个属性

  • this在不同环境下表现是不同的,例如浏览器环境、node环境,这些未来慢慢补充

1. window和this的关系

  1. 全局作用域下的this -> 全局对象
  2. console.log(this === window) // true全局作用域下this等于window
this.a = 1
this.b = function(){
    return 'hello world'
}
​
console.log(window.a // 1
console.log(b) // f(){return 'hello world'}

this在浏览器、node环境下的表现是什么

  • 先搞清楚不同环境下获取全局对象的方法:

    • web:windowselfframesthis

    • node:global

    • worker:self

    • 通用:globalThis可在任何环境拿到全局对象

      • globalThis是通用的,所以可以在有无窗口的环境放心使用它。
var a = 'global -> a'
global.b = 'global -> b'
var obj = {
    a: 'obj -> a'
    test: function(){
        console.log(this.a) // 浏览器:('obj -> a') node:('obj -> a')
        console.log(global.b) // 浏览器:(报错) node:('global -> b')
        console.log(global.a) // 浏览器:(报错) node:(undefined)
        console.log(self.a) // 浏览器:('global -> a') node:(报错)
        console.log(frames.a) // 浏览器:('global -> a')') node:(报错)
        console.log(globalThis.a) // 浏览器:('global -> a') node:(global -> a)
    }
}
​
obj.test()
​
  • 还要搞清楚在函数中,谁调用函数,函数内部指向默认就是谁
function test() {
    'use strict'
    return this;
}
​
console.log(test()) // undefined
console.log(window.test()) // window{}

2. 类

类的本质其实就是函数

// 类 class -> 容器/作用域/模块 -> 其实就是一个壳子
class Test{
    constructor(){}
    say(){console.log('hi')}
    static do(){}
}
​
// 函数
const Test = (function(){
    function Test2(){} // construcotr
    Test.prototype.say = function(){}
    Test.do = function(){}
    window.Test = Test
})()
  • 上面class和下面函数this指向几乎相等

类的静态方法和动态方法

class Test{
    constructor(){
        // 类的非静态方法 -> new -> this -> {}
        this.test = function(){
            console.log('none-static:' + this)
        }
    }
    // 类的静态方法 -> Test.prototype{...}
    // new this -> {} -> __proto__ -> Test.prototype 一层一层找直到Object.prototype
    // Test -> prototype{} -> test方法
    test(){
        console.log('hi')
    }
}
​
const test = new Test();
test.test()//'none-static:'[object,object]

类的super

class Father{
  constructor(){
    // new -> this -> {} -> age属性
    this.age = 44
  }
  swim(){
    console.log('Go swimming!!')
  }
}
​
class Son extends Father{
  constructor(){
    // 调用Father上的constructor
    // 生成this绑定 -> Father this -> Son的实例
    // this -> new Father() -> {age}
    super()
    // {age.hobby}
    this.hobby = 'traval'
  }
  study(){
    console.log(this)
    this.swim()
  }
}
​
const son = new Son()
son.study()

3. 改变this指向

  • bind、apply、call
var obj1 = {
  a: 1
}
​
var obj2 = {
  a:100
}
​
var a = 2;
​
function test(b,c){
  console.log(this.a);
}
​
test() // 2
test.call(obj) // 1 实参直接写后面
test.apply(obj) // 1 传数组
  • bind只改变一次this指向
var test1 = test.bind(obj1)
test1() // 1var test2 = test1.bind(obj2)
test2() // 1var t = test.bind(obj1).bind(obj2)
t() // 1
// 只有第一次bind生效

4.箭头函数

  • 箭头函数是忽略任何形式的this指向的改变(bind、apply、call)都不能改变
  • 箭头函数一定不是构造器,new一个箭头函数会报错
  • 箭头函数中的this不是谁绑定就指向谁,而是始终指向外部作用域的this指向(外层)
const obj = {
  a: 1,
  b: 2
}
​
obj.test = () => {
  console.log(obj); // obj{...}
  console.log(this); // window{}
}

5.对象

  • 对象方法内部的this指向最近的引用,原则上
const obj = {
  a: 1,
  b: 2,
  test: function(){
      // 指向obj
    console.log(this.a);
  },
  test2: test2,
  c: {
    d: 3,
    e: 4,
    test3(){
        // 指向obj.c
      console.log(this.d);
    }
  }
}
​
function test2(){
  console.log(this.b);
}
​
obj.test() // 1
obj.test2() // 2
obj.c.test3() // 3
  • 对于孤立的函数,它的this就是window
const obj = {
  a: 1,
  b: 2,
  test(){
    function t(){
        // 最近的引用就是window,因为它是孤立的
      console.log(this);
    }
    t()
  }
}
​
obj.test() // window
  • 谁调用,window就指向谁
const a = {
  test: function(){
    console.log(this)
  }
}
a.test() // a
​
let b = a.testb() // window

6. Object.defineProperty()

创建对象的方法

  1. 字面量方法定义对象function Object()
var obj = {
  a: 1,
  b: 2
}
  1. Object.create

    • 可用于创建无链的对象Object.create(null)
var obj2 = Object.create({
  a: 1,
  b: 2
})
  1. Object.defineProperty

    • 相当于拦截器,在访问和变更时会经过get和set方法
var obj3 = {}
Object.defineProperties(obj3,'a',{
  get:function(){
    console.log(this) // 指向Obj3
    return 4
  }
})
  1. 构造函数

    • new的过程

      • 创建一个空对象
      • 绑定原型
      • this指向新造的空对象
      • 在对象里把构造函数里为this赋的值添加到新对象里
      • 把这个新造的对象隐式的返回出去
    • 如果在构造函数里显示的返回了其他东西,那构造出来的对象就是这个其他的东西

function Test(){
  this.a = 1;
  this.b = 2
  console.log(this);
}
​
// this -> 实例化出来的对象
new Test()

7.DOM、BOM

  1. 事件处理函数内部的this,总是指向被绑定的DOM元素

  2. 往往在事件处理函数内想拿到预期的this有两种方法

    • bind()把外部的this传进去
    • 在外部保存this,然后再事件处理函数里使用