在PHP 5中,使用之间有什么区别 self
和 $this
?
什么时候适合?
在PHP 5中,使用之间有什么区别 self
和 $this
?
什么时候适合?
使用
$this
参考当前 目的。使用self
参考 现在的课程。换句话说,使用$this->member
对于非静态成员, 使用self::$member
对于静态成员。
这是一个例子 正确 用法 $this
和 self
对于非静态和静态成员变量:
<?php
class X {
private $non_static_member = 1;
private static $static_member = 2;
function __construct() {
echo $this->non_static_member . ' '
. self::$static_member;
}
}
new X();
?>
这是一个例子 不正确 用法 $this
和 self
对于非静态和静态成员变量:
<?php
class X {
private $non_static_member = 1;
private static $static_member = 2;
function __construct() {
echo self::$non_static_member . ' '
. $this->static_member;
}
}
new X();
?>
这是一个例子 多态性 同 $this
对于成员函数:
<?php
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();
?>
这是一个例子 抑制多态行为 通过使用 self
对于成员函数:
<?php
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->foo()
打电话给foo()
成员函数of whatever>是当前对象的确切类型。如果对象是type X
,因此>打电话X::foo()
。如果对象是type Y
,它打电话Y::foo()
。但是使用> self :: foo(),X::foo()
总是被称为。
从 http://www.phpbuilder.com/board/showthread.php?t=10354489:
关键字self确实如此 不 仅仅指“当前类”,至少不是以限制静态成员的方式。在非静态成员的上下文中, self
还提供了绕过vtable的方法(在vtable上看到wiki)对于当前对象。就像你可以使用一样 parent::methodName()
调用父版本的函数,这样就可以调用了 self::methodName()
调用当前类的方法实现。
class Person {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function getTitle() {
return $this->getName()." the person";
}
public function sayHello() {
echo "Hello, I'm ".$this->getTitle()."<br/>";
}
public function sayGoodbye() {
echo "Goodbye from ".self::getTitle()."<br/>";
}
}
class Geek extends Person {
public function __construct($name) {
parent::__construct($name);
}
public function getTitle() {
return $this->getName()." the geek";
}
}
$geekObj = new Geek("Ludwig");
$geekObj->sayHello();
$geekObj->sayGoodbye();
这将输出:
你好,我是路德维希的极客
再见路德维希这个人
sayHello()
使用 $this
指针,因此调用vtable来调用 Geek::getTitle()
。
sayGoodbye()
使用 self::getTitle()
,所以不使用vtable,并且 Person::getTitle()
叫做。在这两种情况下,我们都在处理实例化对象的方法,并且可以访问 $this
指针在被调用的函数中。
不使用 self::
, 使用 static::
自我的另一个方面::值得一提。恼人地 self::
指定义点的范围而不是执行点。考虑这个简单的类有两种方法:
class Person
{
public static function status()
{
self::getStatus();
}
protected static function getStatus()
{
echo "Person is alive";
}
}
如果我们打电话 Person::status()
我们会看到“人还活着”。现在考虑当我们创建一个继承自this的类时会发生什么:
class Deceased extends Person
{
protected static function getStatus()
{
echo "Person is deceased";
}
}
调用 Deceased::status()
我们希望看到“Person is deceased”但是我们看到的是“Person is alive”,因为范围包含调用时的原始方法定义 self::getStatus()
被定义了。
PHP 5.3有一个解决方案。该 static::
resolution运算符实现了“后期静态绑定”,这是一种奇特的方式,它说它被绑定到被调用类的范围。改变行 status()
至 static::getStatus()
结果就是你所期望的。在旧版本的PHP中,你必须找到一个kludge来做到这一点。
看到 PHP文档
所以回答问题不是问...
$this->
指的是当前对象(类的实例),而 static::
是指一个班级
要真正理解我们在谈论时所谈论的内容 self
与 $this
,我们需要从概念和实践层面深入挖掘正在发生的事情。我真的觉得没有任何答案适当地做到这一点,所以这是我的尝试。
让我们从谈论什么开始吧 类 和 目的 是。
所以呢 是 一个 类?很多人把它定义为 蓝图 或者a 模板 对象。事实上,你可以阅读更多 关于PHP中的类。在某种程度上,这就是它的真实含义。我们来看一堂课:
class Person {
public $name = 'my name';
public function sayHello() {
echo "Hello";
}
}
如您所知,该类上有一个属性叫做 $name
和一个叫做的方法(功能) sayHello()
。
它的 非常 重要的是要注意到 类 是一个静态结构。这意味着班级 Person
一旦定义,在你看到的任何地方总是一样的。
另一方面,一个物体就是所谓的物体 例 一类。这意味着我们采用类的“蓝图”,并使用它来制作动态副本。此副本现在专门与其存储的变量相关联。因此,对该变量进行任何更改 例 是该实例的本地。
$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"
我们创造新的 实例 一个班级使用 new
运营商。
因此,我们说Class是一个全局结构,Object是一个本地结构。不要担心这很有趣 ->
语法,我们将稍微介绍一下。
我们应该谈论的另一件事是,我们可以 查 如果一个实例是一个 instanceof
一个特定的课程: $bob instanceof Person
如果是,则返回一个布尔值 $bob
实例是使用 Person
类, 要么 一个孩子 Person
。
那么让我们深入了解一个类实际包含的内容。一个类包含5种类型的“东西”:
属性 - 将这些视为每个实例将包含的变量。
class Foo {
public $bar = 1;
}
静态属性 - 将这些视为在班级共享的变量。这意味着它们永远不会被每个实例复制。
class Foo {
public static $bar = 1;
}
方法 - 这些是每个实例将包含的函数(并对实例进行操作)。
class Foo {
public function bar() {}
}
静态方法 - 这些是在整个班级共享的功能。他们是这样 不 对实例进行操作,而只对静态属性进行操作。
class Foo {
public static function bar() {}
}
常量 - 类解析常量。这里没有更深入,但添加完整性:
class Foo {
const BAR = 1;
}
所以基本上,我们使用“提示”来存储关于类和对象容器的信息 静态的 它识别信息是否被共享(因此是静态的)(因而是动态的)。
在方法内部,对象的实例由 $this
变量。该对象的当前状态就在那里,并且改变(改变)任何属性将导致对该实例的改变(但不改变其他属性)。
如果一个方法被静态调用,那么 $this
变量 没有定义。这是因为没有与静态调用相关联的实例。
这里有趣的是如何进行静态调用。那么让我们谈谈我们如何访问状态:
所以现在我们已经存储了那个状态,我们需要访问它。这可能会有点棘手(或 办法 不止一点,所以让我们将它分成两个视点:从实例/类的外部(比如来自普通函数调用,或来自全局范围),以及实例/类的内部(来自一个方法内部)目的)。
从实例/类的外部,我们的规则非常简单且可预测。我们有两个运算符,每个运算符都会立即告诉我们是否正在处理实例或静态类:
->
- 对象操作 - 当我们访问实例时总是使用它。
$bob = new Person;
echo $bob->name;
请注意调用 Person->foo
没有意义(因为 Person
是一个类,而不是一个实例)。因此,这是一个解析错误。
::
- 范围分辨率运营商 - 这始终用于访问Class静态属性或方法。
echo Foo::bar()
另外,我们可以用相同的方式在对象上调用静态方法:
echo $foo::bar()
它的 非常 重要的是要注意,当我们这样做 从外面,对象的实例是隐藏的 bar()
方法。这意味着它与运行完全相同:
$class = get_class($foo);
$class::bar();
因此, $this
未在静态调用中定义。
事情在这里有所改变。使用相同的运算符,但它们的含义变得非常模糊。
该 对象操作 ->
仍然用于调用对象的实例状态。
class Foo {
public $a = 1;
public function bar() {
return $this->a;
}
}
打电话给 bar()
方法 $foo
(一个例子 Foo
)使用对象操作符: $foo->bar()
将导致实例的版本 $a
。
这就是我们的期望。
的含义 ::
运营商虽然有变化它取决于对当前函数的调用的上下文:
在静态上下文中
在静态上下文中,使用任何调用 ::
也将是静态的。我们来看一个例子:
class Foo {
public function bar() {
return Foo::baz();
}
public function baz() {
return isset($this);
}
}
调用 Foo::bar()
会打电话给 baz()
方法静态,因此 $this
将 不 填充。值得注意的是,在PHP(5.3+)的最新版本中,这将触发一个 E_STRICT
错误,因为我们静态地调用非静态方法。
在实例上下文中
另一方面,在实例上下文中,使用调用 ::
取决于呼叫的接收者(我们正在呼叫的方法)。如果方法定义为 static
,然后它将使用静态调用。如果不是,它将转发实例信息。
所以,看看上面的代码,调用 $foo->bar()
将返回 true
,因为“静态”调用发生在实例上下文中。
合理?不这么认为。这令人困惑。
因为使用类名将所有内容捆绑在一起相当脏,所以PHP提供了3个基本的“快捷方式”关键字,使范围解析更容易。
self
- 这是指当前的类名。所以 self::baz()
是相同的 Foo::baz()
在...内 Foo
class(任何方法)。
parent
- 这是指当前类的父级。
static
- 这指的是被叫类。由于继承,子类可以覆盖方法和静态属性。所以叫他们用 static
而不是类名允许我们解决呼叫来自哪里,而不是当前级别。
理解这一点的最简单方法是开始查看一些示例。我们选一节课:
class Person {
public static $number = 0;
public $id = 0;
public function __construct() {
self::$number++;
$this->id = self::$number;
}
public $name = "";
public function getName() {
return $this->name;
}
public function getId() {
return $this->id;
}
}
class Child extends Person {
public $age = 0;
public function __construct($age) {
$this->age = $age;
parent::__construct();
}
public function getName() {
return 'child: ' . parent::getName();
}
}
现在,我们也在看这里的继承。暂时忽略这是一个糟糕的对象模型,但让我们来看看当我们玩这个时会发生什么:
$bob = new Person;
$bob->name = "Bob";
$adam = new Person;
$adam->name = "Adam";
$billy = new Child;
$billy->name = "Billy";
var_dump($bob->getId()); // 1
var_dump($adam->getId()); // 2
var_dump($billy->getId()); // 3
所以ID计数器在两个实例和子节点之间共享(因为我们正在使用它) self
访问它。如果我们使用 static
,我们可以在子类中覆盖它)。
var_dump($bob->getName()); // Bob
var_dump($adam->getName()); // Adam
var_dump($billy->getName()); // child: Billy
请注意,我们正在执行 Person::getName()
例 方法每次。但我们正在使用 parent::getName()
在其中一个案例(儿童案件)中这样做。这就是使这种方法变得强大的原因。
请注意,调用上下文决定了是否使用了实例。因此:
class Foo {
public function isFoo() {
return $this instanceof Foo;
}
}
不是 总是 真正。
class Bar {
public function doSomething() {
return Foo::isFoo();
}
}
$b = new Bar;
var_dump($b->doSomething()); // bool(false)
现在它是 真 这里很奇怪。我们打电话给另一个班级,但是 $this
那传递给了 Foo::isFoo()
方法是实例 $bar
。
这可能会导致各种错误和概念性的WTF-ery。所以我强烈建议避免 ::
从实例方法中的运算符除了那三个虚拟“快捷”关键字之外的任何东西(static
, self
,和 parent
)。
请注意,每个人都共享静态方法和属性。这使它们基本上成为全局变量。与全局变量相同的问题。因此,我会非常犹豫是否将信息存储在静态方法/属性中,除非您对其真正的全局性感到满意。
一般来说,你会想要使用所谓的Late-Static-Binding static
代替 self
。但请注意,它们不是一回事,所以说“总是使用 static
代替 self
真的是近视。相反,停下来思考你想要进行的调用,并思考你是否希望子类能够覆盖它 静态解决 呼叫。
太糟糕了,回去看看吧。它可能太长了,但它很长,因为这是一个复杂的话题
好的。简而言之, self
用来参考 当前的班级名称在课堂上,在哪里 $this
指当前的对象 例。注意 self
是一个复制/粘贴捷径。您可以安全地用您的班级名称替换它,它会正常工作。但 $this
是一个动态变量,无法提前确定(甚至可能不是您的班级)。
如果使用对象运算符(->
), 然后你 总是 知道你正在处理一个实例。如果使用范围解析运算符(::
),您需要有关上下文的更多信息(我们是否已经在对象上下文中?我们是否在对象之外?等等)。
self
(不是$ self)指的是 类型 班级,如何 $this
指当前 例 班上的。 self
用于静态成员函数,以允许您访问静态成员变量。 $this
用于非静态成员函数,并且是对调用成员函数的类的实例的引用。
因为 this
是一个对象,你使用它像: $this->member
因为 self
它不是一个对象,它基本上是一个自动引用当前类的类型,你可以像下面这样使用它: self::member
$this->
用于引用类的变量(成员变量)或方法的特定实例。
Example:
$derek = new Person();
$ derek现在是Person的特定实例。 每个Person都有first_name和last_name,但$ derek有一个特定的first_name和last_name(Derek Martin)。在$ derek实例中,我们可以将它们称为$ this-> first_name和$ this-> last_name
ClassName ::用于引用该类型的类及其静态变量,静态方法。如果有帮助,您可以在心理上将“静态”替换为“共享”。因为它们是共享的,所以它们不能引用$ this,它引用特定的实例(不共享)。静态变量(即静态$ db_connection)可以在一种对象的所有实例之间共享。例如,所有数据库对象共享一个连接(静态$连接)。
静态变量示例: 假设我们有一个带有单个成员变量的数据库类:static $ num_connections; 现在,把它放在构造函数中:
function __construct()
{
if(!isset $num_connections || $num_connections==null)
{
$num_connections=0;
}
else
{
$num_connections++;
}
}
正如对象具有构造函数一样,它们也具有析构函数,这些析构函数在对象死亡或未设置时执行:
function __destruct()
{
$num_connections--;
}
每次我们创建一个新实例时,它都会将我们的连接计数器增加一个。每次我们销毁或停止使用实例时,它都会将连接计数器减一。通过这种方式,我们可以监视我们使用的数据库对象的实例数:
echo DB::num_connections;
因为$ num_connections是静态的(共享),所以它将反映活动数据库对象的总数。您可能已经看到过这种技术用于在数据库类的所有实例之间共享数据库连接。这样做是因为创建数据库连接需要很长时间,因此最好只创建一个并共享它(这称为单例模式)。
静态方法(即公共静态View :: format_phone_number($ digits))可以在没有首先实例化其中一个对象的情况下使用(即它们不在内部引用$ this)。
静态方法示例:
public static function prettyName($first_name, $last_name)
{
echo ucfirst($first_name).' '.ucfirst($last_name);
}
echo Person::prettyName($derek->first_name, $derek->last_name);
如您所见,public static function prettyName对该对象一无所知。它只是处理你传入的参数,就像一个不属于对象的普通函数。那么,为什么我们可以把它作为对象的一部分呢?
自:: 如果你正在编码 外 如果对象具有您想要引用的静态方法,则必须使用对象的名称View :: format_phone_number($ phone_number)调用它; 如果你正在编码 内 你可以使用你想要引用的静态方法的对象 或 使用对象的名称View :: format_phone_number($ pn),或者你可以使用self :: format_phone_number($ pn)的快捷方式
静态变量也是如此: 例: 查看:: templates_path与self :: templates_path
在DB类中,如果我们引用某个其他对象的静态方法,我们将使用该对象的名称: 例: 会议:: getUsersOnline();
但是如果DB类想要引用它自己的静态变量,它只会说自己: 例: 自::连接;
希望有助于清理事情:)
从 这篇博文:
self
指的是当前的类self
可用于调用静态函数和引用静态成员变量self
可以在静态函数中使用self
也可以通过绕过vtable来关闭多态行为$this
指当前的对象$this
可用于调用静态函数$this
不应该用来调用静态成员变量。使用self
代替。$this
不能在静态函数中使用