何时该用 'self' 而非 '$this'?

何时该用 ‘self’ 而非 ‘$this’?

技术背景

在 PHP 的面向对象编程中,self$this 是两个常用的关键字,它们在不同的场景下有着不同的用途。理解何时使用 self 以及何时使用 $this 对于编写高效、可维护的代码至关重要。

实现步骤

区分类和对象

  • :是一个静态的结构,是对象的蓝图或模板。例如:
1
2
3
4
5
6
class Person {
public $name = 'my name';
public function sayHello() {
echo "Hello";
}
}
  • 对象:是类的实例,是动态的。可以使用 new 操作符创建对象:
1
2
3
4
$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"

理解类的组成部分

类包含以下几种类型的元素:

  1. 属性:每个实例都会包含的变量。
1
2
3
class Foo {
public $bar = 1;
}
  1. 静态属性:在类级别共享的变量,不会被每个实例复制。
1
2
3
class Foo {
public static $bar = 1;
}
  1. 方法:每个实例都会包含的函数,用于操作实例。
1
2
3
class Foo {
public function bar() {}
}
  1. 静态方法:在整个类中共享的函数,不操作实例,只操作静态属性。
1
2
3
class Foo {
public static function bar() {}
}
  1. 常量:类解析常量。
1
2
3
class Foo {
const BAR = 1;
}

访问状态

从实例/类外部访问

  • ->(对象操作符):用于访问实例的属性或方法。
1
2
$bob = new Person;
echo $bob->name;
  • ::(范围解析操作符):用于访问类的静态属性或方法。
1
echo Foo::bar();

从实例/类内部访问

  • ->(对象操作符):仍然用于调用对象的实例状态。
1
2
3
4
5
6
7
8
class Foo {
public $a = 1;
public function bar() {
return $this->a;
}
}
$foo = new Foo;
echo $foo->bar();
  • ::(范围解析操作符):其含义取决于当前函数的调用上下文。
    • 在静态上下文中,使用 :: 进行的调用也是静态的。
1
2
3
4
5
6
7
8
9
class Foo {
public function bar() {
return Foo::baz();
}
public function baz() {
return isset($this);
}
}
Foo::bar();
- 在实例上下文中,使用 `::` 进行的调用取决于被调用的方法。如果方法被定义为 `static`,则使用静态调用;否则,将传递实例信息。

使用快捷关键字

PHP 提供了三个基本的“快捷”关键字来简化范围解析:

  • self:引用当前类名。例如,在 Foo 类的任何方法中,self::baz() 等同于 Foo::baz()
  • parent:引用当前类的父类。
  • static:引用被调用的类,支持“后期静态绑定”。

核心代码

正确使用 $thisself 访问非静态和静态成员变量

1
2
3
4
5
6
7
8
9
10
11
class X {
private $non_static_member = 1;
private static $static_member = 2;

function __construct() {
echo $this->non_static_member . ' '
. self::$static_member;
}
}

new X();

方法调用的多态性示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class X {
function foo() {
echo 'X::foo()';
}

function bar() {
$this->foo();
}
}

class Y extends X {
function foo() {
echo 'Y::foo()';
}
}

$x = new Y();
$x->bar();

抑制多态行为的示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class X {
function foo() {
echo 'X::foo()';
}

function bar() {
self::foo();
}
}

class Y extends X {
function foo() {
echo 'Y::foo()';
}
}

$x = new Y();
$x->bar();

最佳实践

  • 使用 $this:在非静态成员函数中,使用 $this 来引用当前对象的实例,调用非静态属性和方法。
  • 使用 self:在静态成员函数中,使用 self 来引用当前类,调用静态属性和方法。如果不想让子类覆盖当前方法,也可以使用 self
  • 使用 static:当需要实现“后期静态绑定”时,使用 static 关键字,它会根据实际调用的类来解析方法或属性。

常见问题

$this 在静态方法中未定义

静态方法没有关联的实例,因此在静态方法中使用 $this 会导致错误。例如:

1
2
3
4
5
class Foo {
public static function bar() {
echo $this->a; // 错误:$this 在静态方法中未定义
}
}

self 引用的是定义时的类

self 引用的是定义时的类,而不是调用时的类。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
public static function status() {
self::getStatus();
}

protected static function getStatus() {
echo "Person is alive";
}
}

class Deceased extends Person {
protected static function getStatus() {
echo "Person is deceased";
}
}

Deceased::status(); // 输出 "Person is alive"

在这种情况下,可以使用 static 关键字来实现“后期静态绑定”。

混淆 self$this 的使用场景

如果错误地使用 self 来访问非静态成员,或者使用 $this 来访问静态成员,会导致代码错误或不符合预期的行为。因此,需要明确区分它们的使用场景。


何时该用 'self' 而非 '$this'?
https://119291.xyz/posts/when-to-use-self-over-this/
作者
ww
发布于
2025年5月19日
许可协议