在PHP 5中,使用self
和$this
之间的区别是什么?
什么时候合适?
答案 0 :(得分:1618)
使用
$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()
成员函数,无论当前对象的确切类型是什么。如果对象属于type X
,则会调用X::foo()
。如果对象属于type Y
,则会调用Y::foo()
。但是使用self :: foo(),总是会调用X::foo()
。
来自http://www.phpbuilder.com/board/showthread.php?t=10354489:
答案 1 :(得分:726)
关键字self NOT 仅指“当前类”,至少不会限制您使用静态成员。在非静态成员的上下文中,self
还提供了绕过当前对象的vtable(see wiki on vtable)的方法。正如您可以使用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
指针。
答案 2 :(得分:443)
请勿使用self::
,请使用static::
自我的另一个方面::值得一提。恼人地 self::
是指定义点而不是执行点的范围。考虑这个简单的类有两种方法:
class Person
{
public static function status()
{
self::getStatus();
}
protected static function getStatus()
{
echo "Person is alive";
}
}
如果我们致电Person::status()
,我们会看到“人还活着”。现在考虑当我们创建一个继承自此的类时会发生什么:
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::
解析运算符实现了“后期静态绑定”,这是一种说法,它被绑定到被调用类的范围。将status()
中的行更改为static::getStatus()
,结果就是您所期望的。在旧版本的PHP中,你必须找到一个kludge来做到这一点。
所以回答问题并不是问...
$this->
引用当前对象(类的实例),而static::
引用类
答案 3 :(得分:238)
要真正了解我们在讨论self
与$this
时所谈论的内容,我们需要真正深入了解在概念和实践层面上发生的事情。我真的觉得任何答案都没有做到,所以这是我的尝试。
让我们先谈谈类和对象是什么。
那么, 类是什么?很多人将其定义为对象的蓝图或模板。实际上,您可以阅读更多About Classes In PHP Here。在某种程度上,这是真的。让我们来看一个班级:
class Person {
public $name = 'my name';
public function sayHello() {
echo "Hello";
}
}
正如您所知,该类上有一个名为$name
的属性和一个名为sayHello()
的方法(函数)。
非常非常重要的是要注意类是一个静态结构。这意味着,一旦定义,类Person
在您看到的任何地方始终都是相同的。
另一方面,对象是一个类的实例。这意味着我们采取了#34;蓝图&#34;该类,并使用它来制作动态副本。此副本现在特别与其存储的变量相关联。因此,对实例的任何更改都是该实例的本地更新。
$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"
我们使用new
运算符创建类的新实例。
因此,我们说Class是一个全局结构,Object是一个本地结构。不要担心这种有趣的->
语法,我们会稍微介绍一下。
我们应该讨论的另一件事是,如果一个实例是instanceof
一个特定的类,我们可以检查:$bob instanceof Person
如果$bob
返回一个布尔值1}}实例是使用Person
类,或 Person
的子项制作的。
因此,让我们深入了解一个类实际包含的内容。有5种类型的&#34;东西&#34;一个类包含:
属性 - 将这些视为每个实例将包含的变量。
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;
}
基本上,我们使用&#34; hints&#34;来存储关于类和对象容器的信息。关于 static ,它识别信息是否被共享(因而是静态的)(因此是动态的)。
在方法内部,对象的实例由$this
变量表示。该对象的当前状态就在那里,并且改变(改变)任何属性将导致对该实例的改变(但不改变其他属性)。
如果静态调用方法,则未定义$this
变量 。这是因为没有与静态调用关联的实例。
这里有趣的是如何进行静态调用。因此,让我们谈谈我们如何访问该州:
现在我们已经存储了该状态,我们需要访问它。这可能会有点棘手(或者方式不止一点),所以让我们将它分成两个视点:来自实例/类的外部(比如来自正常的函数调用,或者来自全局范围),以及实例/类的内部(来自对象上的方法)。
从实例/类的外部,我们的规则非常简单且可预测。我们有两个运算符,每个运算符都会立即告诉我们是否正在处理实例或静态类:
->
- object-operator - 我们在访问实例时始终使用此功能。
$bob = new Person;
echo $bob->name;
重要的是要注意调用Person->foo
没有意义(因为Person
是一个类,而不是一个实例)。因此,这是一个解析错误。
::
- scope-resolution-operator - 这始终用于访问Class静态属性或方法。
echo Foo::bar()
此外,我们可以以相同的方式在对象上调用静态方法:
echo $foo::bar()
非常重要要注意,当我们从外部执行 时,对象的实例对bar()
隐藏了方法。这意味着它与运行完全相同:
$class = get_class($foo);
$class::bar();
因此,静态调用中未定义$this
。
事情在这里有所改变。使用相同的运算符,但它们的含义变得非常模糊。
object-operator ->
仍然用于调用对象的实例状态。
class Foo {
public $a = 1;
public function bar() {
return $this->a;
}
}
使用对象运算符bar()
在$foo
(Foo
的实例)上调用$foo->bar()
方法将导致实例的$a
版本{1}}。
这就是我们的期望。
::
运算符的含义虽然有所改变。它取决于对当前函数的调用的上下文:
在静态环境中
在静态上下文中,使用::
进行的任何调用也都是静态的。让我们看一个例子:
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
,因为&#34;静态&#34;调用发生在实例上下文中。
有意义吗?没想到。这令人困惑。
因为使用类名将所有内容捆绑在一起相当脏,所以PHP提供了3个基本&#34;快捷方式&#34;使范围更容易解决的关键字。
self
- 这是指当前的班级名称。因此self::baz()
与Foo::baz()
类中的Foo
相同(任何方法)。
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。所以我强烈建议在实例方法中避免使用::
运算符,除了那三个虚拟&#34; short-cut&#34;关键字(static
,self
和parent
)。
请注意,每个人都共享静态方法和属性。这使它们基本上成为全局变量。与全局变量相同的问题。因此,我非常犹豫是否要将信息存储在静态方法/属性中,除非您对其真正的全局性感到满意。
一般情况下,您希望使用static
而不是self
来使用所谓的Late-Static-Binding。但请注意,它们不是一回事,所以说&#34;始终使用static
代替self
是非常短视的。相反,请停止并考虑您要进行的调用,并考虑是否希望子类能够覆盖静态解析调用。
太糟糕了,回去看看吧。它可能太长了,但它很长,因为这是一个复杂的话题
好的,好的。简而言之,self
用于引用类中的当前类名,其中$this
引用当前对象实例。请注意,self
是复制/粘贴快捷方式。您可以安全地将其替换为您的班级名称,并且它可以正常工作。但$this
是一个动态变量,无法提前确定(甚至可能不是您的班级)。
如果使用了对象运算符(->
),那么总是知道您正在处理实例。如果使用范围解析运算符(::
),则需要有关上下文的更多信息(我们是否已经在对象上下文中?我们是否在对象之外?等等。)
答案 4 :(得分:115)
self
(不是$ self)指的是类的类型,其中$this
指的是类的当前实例。 self
用于静态成员函数,以允许您访问静态成员变量。 $this
用于非静态成员函数,并且是对调用成员函数的类的实例的引用。
由于this
是一个对象,因此您可以使用它:$this->member
因为self
不是一个对象,它基本上是一个自动引用当前类的类型,你可以像下面这样使用它:self::member
答案 5 :(得分:96)
$this->
用于指代类变量(成员变量)或方法的特定实例。
Example:
$derek = new Person();
$ derek现在是Person的特定实例。 每个Person都有first_name和last_name,但$ derek有一个特定的first_name和last_name(Derek Martin)。在$ derek实例中,我们可以将它们称为$ this-&gt; first_name和$ this-&gt; last_name
ClassName ::用于引用该类型的类及其静态变量,静态方法。如果有帮助,您可以在心理上将“静态”替换为“共享”。因为它们是共享的,所以它们不能引用$ this,它引用特定的实例(不共享)。静态变量(即静态$ db_connection)可以在一种对象的所有实例之间共享。例如,所有数据库对象共享一个连接(静态$ 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对该对象一无所知。它只是处理你传入的参数,就像一个不属于对象的普通函数。那么,为什么我们不能把它作为对象的一部分呢?
<强> SELF :: 强> 如果您在外部编码具有您想要引用的静态方法的对象,则必须使用对象的名称View :: format_phone_number($ phone_number)调用它; 如果你在里面编码你想要引用静态方法的对象,你可以 使用对象的名称View :: format_phone_number($ pn),或者你可以使用self :: format_phone_number($ pn)快捷方式
静态变量也是如此: 示例:查看:: templates_path与self :: templates_path
在DB类中,如果我们引用某个其他对象的静态方法,我们将使用该对象的名称: 示例: Session :: getUsersOnline();
但是如果DB类想要引用它自己的静态变量,它只会说自己: 示例: self :: connection;
希望有助于澄清事情:)
答案 6 :(得分:29)
self
指的是当前的课程self
可用于调用静态函数和引用静态成员变量self
可以在静态函数中使用- 来关闭多态行为
self
也可以绕过vtable$this
指的是当前对象$this
可用于调用静态函数$this
不应该用于调用静态成员变量。请改用self
。$this
不能在静态函数中使用
答案 7 :(得分:25)
在PHP中,您使用self关键字来访问静态属性和方法。
问题在于,您可以将$this->method()
替换为self::method()
,无论method()
是否为静态。那么你应该使用哪一个?
考虑以下代码:
class ParentClass {
function test() {
self::who(); // will output 'parent'
$this->who(); // will output 'child'
}
function who() {
echo 'parent';
}
}
class ChildClass extends ParentClass {
function who() {
echo 'child';
}
}
$obj = new ChildClass();
$obj->test();
在此示例中,self::who()
将始终输出'parent',而$this->who()
将取决于对象具有的类。
现在我们可以看到self指的是调用它的类,而$this
指的是当前对象的类。
因此,只有在$this
不可用时,或者当您不希望允许后代类覆盖当前方法时,才应使用self。
答案 8 :(得分:21)
在类定义中,$ this引用当前对象,而self引用当前类。
有必要使用self引用类元素,并使用$ this引用对象元素。
self::STAT // refer to a constant value
self::$stat // static variable
$this->stat // refer to an object variable
答案 9 :(得分:20)
根据http://www.php.net/manual/en/language.oop5.static.php,没有$self
。只有$this
用于引用类的当前实例(对象)和self,它可用于引用类的静态成员。对象实例和类之间的区别在这里发挥作用。
答案 10 :(得分:20)
这是一个正确使用$ 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();
?>
答案 11 :(得分:15)
self
表示当前类(在其中调用它),
$this
指当前对象。
你可以使用static而不是self。
参见示例:
class ParentClass {
function test() {
self::which(); // output 'parent'
$this->which(); // output 'child'
}
function which() {
echo 'parent';
}
}
class ChildClass extends ParentClass {
function which() {
echo 'child';
}
}
$obj = new ChildClass();
$obj->test();
输出: 亲 子
答案 12 :(得分:14)
请参阅以下显示重载的示例。
<?php
class A {
public static function newStaticClass()
{
return new static;
}
public static function newSelfClass()
{
return new self;
}
public function newThisClass()
{
return new $this;
}
}
class B extends A
{
public function newParentClass()
{
return new parent;
}
}
$b = new B;
var_dump($b::newStaticClass()); // B
var_dump($b::newSelfClass()); // A because self belongs to "A"
var_dump($b->newThisClass()); // B
var_dump($b->newParentClass()); // A
class C extends B
{
public static function newSelfClass()
{
return new self;
}
}
$c = new C;
var_dump($c::newStaticClass()); // C
var_dump($c::newSelfClass()); // C because self now points to "C" class
var_dump($c->newThisClass()); // C
var_dump($b->newParentClass()); // A because parent was defined *way back* in class "B"
大多数情况下,您要引用当前类,这就是您使用static
或$this
的原因。但是,有时您需要 self
因为您想要原始类而不管扩展它的内容。 (非常,很少)
答案 13 :(得分:14)
我认为问题不在于你是否可以通过调用ClassName::staticMember
来调用类的静态成员。问题是使用self::classmember
和$this->classmember
之间的区别。
例如,无论您使用self::
还是$this->
class Person{
private $name;
private $address;
public function __construct($new_name,$new_address){
$this->name = $new_name;
$this->address = $new_address;
}
}
class Person{
private $name;
private $address;
public function __construct($new_name,$new_address){
self::$name = $new_name;
self::$address = $new_address;
}
}
答案 14 :(得分:14)
由于这里没有人谈到表演,这里有一个小基准(5.6):
Name | Time | Percent
----------|---------|---------
$this-> | 0.99163 | 106.23%
self:: | 0.96912 | 103.82%
static:: | 0.93348 | 100%
这是2 000 000次运行的结果,这是我使用的代码:
<?php
require '../vendor/autoload.php';
// My small class to do benchmarks
// All it does is looping over every test x times and record the
// time it takes using `microtime(true)`
// Then, the percentage is calculated, with 100% being the quickest
// Times are being rouned for outputting only, not to calculate the percentages
$b = new Tleb\Benchmark\Benchmark(2000000);
class Foo
{
public function calling_this()
{
$this->called();
}
public function calling_self()
{
self::called();
}
public function calling_static()
{
static::called();
}
public static function called()
{
}
}
$b->add('$this->', function () { $foo = new Foo; $foo->calling_this(); });
$b->add('self::', function () { $foo = new Foo; $foo->calling_self(); });
$b->add('static::', function () { $foo = new Foo; $foo->calling_static(); });
$b->run();
答案 15 :(得分:13)
当self
与::
运算符一起使用时,它引用当前类,可以在静态和非静态上下文中完成。 $this
指的是对象本身。此外,使用$this
调用静态方法(但不是引用字段)是完全合法的。
答案 16 :(得分:7)
$this
引用当前的类对象,self
引用当前类(Not object)。该类是对象的蓝图。所以你定义了一个类,但是你构造了对象。
换句话说,请使用self for static
和this for none-static members or methods
。
同样在子/父场景中self / parent
主要用于识别子类和父类成员和方法。
答案 17 :(得分:7)
另外,由于$this::
尚未讨论过。
仅供参考,从PHP 5.3开始处理实例化对象以获取当前范围值时,与使用static::
相反,可以选择使用$this::
。
class Foo
{
const NAME = 'Foo';
//Always Foo::NAME (Foo) due to self
protected static $staticName = self::NAME;
public function __construct()
{
echo $this::NAME;
}
public function getStaticName()
{
echo $this::$staticName;
}
}
class Bar extends Foo
{
const NAME = 'FooBar';
/**
* override getStaticName to output Bar::NAME
*/
public function getStaticName()
{
$this::$staticName = $this::NAME;
parent::getStaticName();
}
}
$foo = new Foo; //outputs Foo
$bar = new Bar; //outputs FooBar
$foo->getStaticName(); //outputs Foo
$bar->getStaticName(); //outputs FooBar
$foo->getStaticName(); //outputs FooBar
使用上面的代码不是常见的或推荐的做法,只是为了说明它的用法,并且更像是“你知道吗?”参考原始海报的问题。
它还代表$object::CONSTANT
的使用,例如echo $foo::NAME;
,而不是$this::NAME;
答案 18 :(得分:5)
如果要在不创建该类的对象/实例的情况下调用类的方法,请使用self
,从而保存RAM(有时为此目的使用self)。换句话说,它实际上是静态调用方法。使用this
作为对象透视图。
答案 19 :(得分:5)
我遇到了同样的问题,简单的答案是:
每当您使用静态方法或静态属性并希望在没有实例化类的对象的情况下调用它们时,您需要使用 self :: < / strong>要调用它们,因为 $ this 始终要求创建对象。
答案 20 :(得分:2)
案例1:使用self
可用于类常量
class classA { const FIXED_NUMBER = 4; self::POUNDS_TO_KILOGRAMS }
如果要在课外调用它,请使用classA::POUNDS_TO_KILOGRAMS
访问常量
案例2:对于静态属性
class classC { public function __construct() { self::$_counter++; $this->num = self::$_counter; } }
答案 21 :(得分:1)
根据php.net,在此上下文中有三个特殊关键字:self
,parent
和static
。它们用于从类定义中访问属性或方法。
$this
用于调用任何类的实例和方法,只要该类可访问。
答案 22 :(得分:-1)
self :: 关键字,用于当前类,基本上用于访问静态成员,方法和常量。但是如果使用 $ this ,则不能调用静态成员,方法和函数。
您可以在另一个类中使用 self :: 关键字,并访问静态成员,方法和常量。当它从父类扩展时,对于 $ this 关键字也是如此。从父类扩展后,您可以访问另一个类中的非静态成员,方法和函数。
下面提供的代码是 self :: 和 $ this 关键字的示例。只需将代码复制并粘贴到代码文件中,然后查看输出即可。
class cars{
var $doors=4;
static $car_wheel=4;
public function car_features(){
echo $this->doors." Doors <br>";
echo self::$car_wheel." Wheels <br>";
}
}
class spec extends cars{
function car_spec(){
print(self::$car_wheel." Doors <br>");
print($this->doors." Wheels <br>");
}
}
/********Parent class output*********/
$car = new cars;
print_r($car->car_features());
echo "------------------------<br>";
/********Extend class from another class output**********/
$car_spec_show=new spec;
print($car_spec_show->car_spec());