Code前端首页关于Code前端联系我们

它指的是 JavaScript 类

terry 2年前 (2023-09-08) 阅读数 150 #Vue

前言

JavaScript中的this问题对于初学者来说是一个重要的问题,但很多人仍然对class中的this问题感到困惑。希望的文章可以清楚地向大家解释这一点。

此绑定优先级

关于

this有很多说法。有人说this指的是谁叫谁。有人说this与作用域无关,只与执行上下文有关。这两种说法似乎意味着同一件事。

1。使用新创建的实例来调用方法,它指向当前实例

class Cat {
    jump() {
        console.log('jump',this)
    }
}
const cat = new Cat()
cat.jump() // jump Cat {}
 

2。透明装订

使用call、apply、bind

function jump() {
  console.log(this.name)
}
const obj = {
  name: '豆芽',
  jump,
}
jump = jump.bind(obj)
jump() // 豆芽
 

3。对象中的方法绑定

function jump() {
  console.log(this.name)
}
const obj = {
  name: '豆芽',
  jump,
}
obj.jump() // 豆芽
 

4。默认绑定

在严格模式下,this

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
 
,否则是全局对象。

在类中绑定属性和方法

class Cat {
  constructor(name) {
    this.name = name
  }
  jump() {
    console.log('jump', this)
  }
  static go() {
    console.log(this)
  }
}
Cat.drink = function() {
  console.log('drink', this)
}
Cat.prototype.eat = function() {
  console.log('eat', this)
}
Cat.prototype.walk = () => {
  console.log('walk', this)
}
let cat = new Cat('豆芽')
 

image.pngimage.png

如上图所示,Cat创建的实例方法挂载在__proto__的实例上,即的原型对象。由于cat.proto和Cat.prototype指向同一个对象,因此当原始方法被安装或由创建的覆盖时,所有都会被共享通过方法,所有实例都通过__proto__属性生成的原型链查找原型对象上的方法。

但是,静态方法不与实例共享,因为它们没有连接到原型对象。

属性是挂载在实例上的,即每个创建的实例都有自己的属性,且属性的值不同。

在课堂上链接

image.pngimage.png

image.pngimage.png

如果我们打印 typeof Cat,我们会看到 Cat 是一个函数类型,并且类本身指向一个构造函数。 ES6 中的类 class 实际上只是语法糖 ,可以使用 ES5 实现。由构造函数 Cat 创建的实例 cat 是一个对象。当实例 cat 初始化时,this 的属性将与实例对象 constructor 合并。

class Cat {
  constructor(name, age) {
    this.name = name
  }
  run() {
    console.log('run', this)
  }
}
let cat = new Cat('豆芽')
cat.name // '豆芽'
cat.run() // run Cat {name: '豆芽'}
 

调用 cat.run() 具有当前上下文 cat,因此其 this 指向实例 cat

class Cat {
  constructor(name) {
    this.name = name
    this.jump = this.jump.bind(this)
    this.drink = () => {
        console.log('drink',this)
    }
  }
  run() {
    console.log('run', this)
  }
  jump() {
    console.log('jump',this)
  }
  static go() { 
    console.log('go',this)
  } 
}
Cat.prototype.walk = () => {
    console.log('walk',this)
}

let cat = new Cat('豆芽')
let run = cat.run
let jump = cat.jump
let go = Cat.go
let walk = cat.walk
let drink = cat.drink

run() // run undefined (严格模式下,this为undefined,可以举一个严格模式下function的例子)
jump() // jump Cat {name: "豆芽", jump: ƒ}
Cat.go() // go class Cat {}
go() // go undefined
cat.walk() // walk Window
walk() // walk Window
cat.drink() // drink Cat {name: "豆芽", jump: ƒ, drink: ƒ}
drink() // drink Cat {name: "豆芽", jump: ƒ, drink: ƒ}
 

分析:

运行方法:当将方法分配给实例变量时,它只是设置了对该方法的引用,因此当该变量运行该方法时,实际上更改了该方法的执行上下文。原始执行上下文是实例cat。后来,赋值和执行后,上下文变成全局的,this默认绑定。 class 使用严格模式。该模式下,全局默认绑定到undefined。当不在严格模式下以及在浏览器中启动时,默认情况下会绑定this。设置window

跳转方法:因为调用构造函数时,执行上下文jump/jump被显式绑定。所以jump的执行上下文仍然是cat实例

go方法:go方法是使用静态方法定义的,不能共享cat的实例。只能在构造函数中直接调用Cat

步行和喝水方法: 这两种方法是使用箭头函数定义的。箭头函数的this是在定义函数时绑定的,而不是在执行时绑定的。简单来说,当定义一个函数时,this继承了定义该函数的对象。如果定义了 Cat.prototype.walk,则

JavaScript 也被定义。目前,this指向window。后面分配的变量只是函数的引用,所以它的this仍然是window。同样,如果定义了drink,则this指向构造函数。

注:

(1)严格制度

类和模块中,严格模式默认为,因此不需要使用来设置运行模式。只要你的代码是在类或模块中编写的,就只能使用严格模式。鉴于所有未来的代码实际上都在模块中运行,ES6 有效地将整个语言升级到严格模式。

(2)this指向

如果类

的方法包含this,则默认指向该类的实例。不过,如果单独使用这种方法,一定要非常小心,很可能会报错。

class Logger {
  printName(name = 'there') {
    this.print(`Hello ${name}`);
  }

  print(text) {
    console.log(text);
  }
}

const logger = new Logger();
const { printName } = logger;
printName(); // TypeError: Cannot read property 'print' of undefined
 

在上面的代码中,方法printName中的this默认指向类Logger的实例。但如果将该方法提取出来单独使用,this指向该方法运行的环境(因为class内部处于严格模式,所以this实际上指向undefined),导致无法使用。找到 Open 方法 print 并报告错误。

一个相对简单的解决方案是在构造函数中绑定this,这样print方法就找不到了。

class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }
  // ...
}
 

另一种解决方案是使用箭头函数。

class Obj {
  constructor() {
    this.getThis = () => this;
  }
  getVal = () => this;
}

const myObj = new Obj();
myObj.getThis() === myObj // true
myObj.getVal() === myObj // true
 

箭头函数内的this始终指向定义它的对象。在上面的代码中,箭头函数位于构造函数内部,其定义在构造函数执行时生效。目前,箭头函数所在的运行环境必须是实例对象,所以this始终指向实例对象。

(3) 静态方法
类相当于实例原型。实例继承类中定义的所有方法。如果在方法前面加上关键字static,则表示该方法不是由实例继承,而是通过类直接调用,称为“静态方法”。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
 

上述代码中,Foo类的方法classMethod前面有关键字static,表明该方法是静态方法() )而不是在实例上调用Foo 类。当在实例上调用静态方法时,会抛出错误,指示该方法不存在。

请注意,当静态方法包含this关键字时,它指的是this类,而不是实例。

在上面的代码中,静态方法bar调用this.baz,其中this指的是

,相当于❓,而不是❓。 。另外,这个例子表明静态方法可以与非静态方法重名

超类的静态方法可以被子类继承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'
 

在上面的代码中,超类Foo有一个静态方法,子类Bar可以调用该方法。

静态方法也可以从super对象调用。

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"
 

参考文档:es6.ruanyifeng.com/#docs/class
developer.mozilla.org/zh-CN/docs/…

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门