何时该用 ‘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;
|
理解类的组成部分
类包含以下几种类型的元素:
- 属性:每个实例都会包含的变量。
1 2 3
| class Foo { public $bar = 1; }
|
- 静态属性:在类级别共享的变量,不会被每个实例复制。
1 2 3
| class Foo { public static $bar = 1; }
|
- 方法:每个实例都会包含的函数,用于操作实例。
1 2 3
| class Foo { public function bar() {} }
|
- 静态方法:在整个类中共享的函数,不操作实例,只操作静态属性。
1 2 3
| class Foo { public static function bar() {} }
|
- 常量:类解析常量。
1 2 3
| class Foo { const BAR = 1; }
|
访问状态
从实例/类外部访问
1 2
| $bob = new Person; echo $bob->name;
|
::
(范围解析操作符):用于访问类的静态属性或方法。
从实例/类内部访问
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
:引用被调用的类,支持“后期静态绑定”。
核心代码
正确使用 $this
和 self
访问非静态和静态成员变量
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; } }
|
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();
|
在这种情况下,可以使用 static
关键字来实现“后期静态绑定”。
混淆 self
和 $this
的使用场景
如果错误地使用 self
来访问非静态成员,或者使用 $this
来访问静态成员,会导致代码错误或不符合预期的行为。因此,需要明确区分它们的使用场景。