什么时候应该使用'self'而不是'$this'?

在PHP 5中,使用self$this有什么区别?

什么时候合适?

800747 次浏览

根据静态关键字,没有任何$self。只有$this,用于引用类的当前实例(对象),和self,用于引用类的静态成员。对象实例和类之间的区别在这里发挥作用。

简短回答

使用$this来引用当前对象。使用self来引用换句话说,使用$this->member对于非静态成员,对静态成员使用self::$member

完整答案

下面是正确对非静态和静态成员变量使用$thisself的示例:

<?phpclass X {private $non_static_member = 1;private static $static_member = 2;
function __construct() {echo $this->non_static_member . ' '. self::$static_member;}}
new X();?>

下面是不正确对非静态和静态成员变量使用$thisself的示例:

<?phpclass X {private $non_static_member = 1;private static $static_member = 2;
function __construct() {echo self::$non_static_member . ' '. $this->static_member;}}
new X();?>

这是一个多态$this的成员函数示例:

<?phpclass 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作为成员函数的抑制多态行为示例:

<?phpclass 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

byhttp://board.phpbuilder.com/member.php?145249-laserlight

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,$this指的是特定实例(不共享)。静态变量(即静态db_connection)可以在一种类型对象的所有实例之间共享。例如,所有数据库对象共享一个连接(静态$连接)。

静态变量示例:假设我们有一个具有单个成员变量的数据库类:静态$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是静态的(共享的),所以它将反映活动数据库对象的总数。您可能已经看到这种技术用于在数据库类的所有实例之间共享数据库连接。这样做是因为创建数据库连接需要很长时间,所以最好只创建一个,并共享它(这称为单例模式)。

静态方法(即公共静态视图::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);

正如你所看到的,公共静态函数漂亮的名字对对象一无所知。它只是使用你传入的参数,就像一个不属于对象的普通函数。那么,如果我们可以让它不作为对象的一部分,为什么还要麻烦呢?

  1. 首先,将函数附加到对象可以帮助您保持事物的组织,因此您知道在哪里可以找到它们。
  2. 其次,它可以防止命名冲突。在一个大项目中,你很可能有两个开发人员创建getName()函数。如果一个创建ClassName1::getName(),另一个创建ClassName2::getName(),这根本没有问题。没有冲突。耶!静态方法!

销售F::如果你正在编码外面的对象,它有你想要引用的静态方法,你必须使用对象的名称View::format_phone_number($phone_number)调用它;如果你正在编码里面的对象有你想要引用的静态方法,你可以要么使用对象的名称View::format_phone_number($pn),或者你可以使用self::format_phone_number($pn)快捷方式

静态变量也是如此:示例:查看::templates_pathf::templates_path

在DB类内部,如果我们引用其他对象的静态方法,我们将使用对象的名称:示例:会话::getUsersOnline();

但是如果DB类想要引用它自己的静态变量,它只会说self:示例:Self::connection;

希望这有助于澄清问题:)

我相信问题不是你是否可以通过调用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;}}

关键字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指针。

不要使用#0.使用#1*

self:: 还有另一个值得一提的方面。

class Person{
public static function status(){self::getStatus();}
protected static function getStatus(){echo "Person is alive";}
}

如果我们调用Person::status(),我们将看到“Person是活着的”。现在考虑一下当我们创建一个继承自此的类时会发生什么:

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

调用Deceased::status()时,我们希望看到“Person已死”。然而,我们看到“Person是活着的”,因为范围包含定义调用self::getStatus()时的原始方法定义。

PHP 5.3有一个解决方案。#0解析运算符实现了“后期静态绑定”,这是一种奇特的说法,它绑定到被调用的类的范围。将status()中的行更改为static::getStatus(),结果就是您所期望的。在旧版本的PHP中,您必须找到一个拼凑来做到这一点。

php文档

所以要回答这个问题…

$this->指的是当前对象(类的实例),而static::指的是类。

这是一个正确使用$this和self的非静态示例和静态成员变量:

<?phpclass X {private $non_static_member = 1;private static $static_member = 2;
function __construct() {echo $this->non_static_member . ' '. self::$static_member;}}
new X();?>

self::运算符一起使用时,它引用当前类,这可以在静态和非静态上下文中完成。$this引用对象本身。此外,使用$this调用静态方法(但不能引用字段)是完全合法的。

  • 对象指针$this指向当前对象。
  • 类值static指的是当前对象。
  • 类值self指的是它定义的确切类。
  • 类值parent指的是定义它的确切类的父类。

请参阅以下显示重载的示例。

<?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()); // Bvar_dump($b::newSelfClass()); // A because self belongs to "A"var_dump($b->newThisClass()); // Bvar_dump($b->newParentClass()); // A

class C extends B{public static function newSelfClass(){return new self;}}

$c = new C;
var_dump($c::newStaticClass()); // Cvar_dump($c::newSelfClass()); // C because self now points to "C" classvar_dump($c->newThisClass()); // Cvar_dump($b->newParentClass()); // A because parent was defined *way back* in class "B"

大多数时候,你想引用当前类,这就是你使用static$this的原因。然而,有时你会需要self,因为你想要原始类,而不管它是什么扩展的。(非常,很少)

如果您想调用类的方法而不创建该类的对象/实例,请使用self,从而节省内存(有时为此目的使用self)。换句话说,它实际上是静态调用方法。使用this进行对象透视。

在类定义中,$this指的是当前对象,而self指的是当前类。

有必要使用self引用类元素,并使用$this引用对象元素。

self::STAT // refer to a constant valueself::$stat // static variable$this->stat // refer to an object variable

这篇博客文章

  • self表示当前类
  • self可用于调用静态函数和引用静态成员变量
  • self可以在静态函数中使用
  • self还可以通过绕过vtable来关闭多态行为
  • $this表示当前对象
  • $this可用于调用静态函数
  • $this不应该用于调用静态成员变量。改用self
  • $this不能在静态函数中使用

当我们谈论0和1时,要真正理解我们在谈论什么,我们需要在概念和实践层面上深入研究到底发生了什么。我真的觉得没有任何答案能恰当地做到这一点,所以这是我的尝试。

让我们从讨论对象开始。

类和对象,概念上

那么, a是什么?很多人将其定义为对象的蓝图模板。事实上,你可以阅读更多关于PHP中的类。在某种程度上,这就是它的真实含义。让我们看一个类:

class Person {public $name = 'my name';public function sayHello() {echo "Hello";}}

如您所见,该类上有一个名为$name的属性和一个名为sayHello()的方法(函数)。

非常重要的是要注意是一个静态结构。这意味着类Person一旦定义,无论你在哪里看它总是一样的。

另一方面,对象称为Class的实例。这意味着我们获取类的“蓝图”,并使用它来制作动态副本。该副本现在专门绑定到它存储的变量。因此,对实例的任何更改都是该实例的本地。

$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种类型的“事物”:

  1. 属性-将这些视为每个实例将包含的变量。

    class Foo {public $bar = 1;}
  2. Static Properties - Think of these as variables that are shared at the class level. Meaning that they are never copied by each instance.

    class Foo {public static $bar = 1;}
  3. Methods - These are functions which each instance will contain (and operate on instances).

    class Foo {public function bar() {}}
  4. Static Methods - These are functions which are shared across the entire class. They do not operate on instances, but instead on the static properties only.

    class Foo {public static function bar() {}}
  5. Constants - Class resolved constants. Not going any deeper here, but adding for completeness:

    class Foo {const BAR = 1;}

So basically, we're storing information on the class and object container using "hints" about static which identify whether the information is shared (and hence static) or not (and hence dynamic).

State and Methods

Inside of a method, an object's instance is represented by the $this variable. The current state of that object is there, and mutating (changing) any property will result in a change to that instance (but not others).

If a method is called statically, the $this variable is not defined. This is because there's no instance associated with a static call.

The interesting thing here is how static calls are made. So let's talk about how we access the state:

Accessing State

So now that we have stored that state, we need to access it. This can get a bit tricky (or way more than a bit), so let's split this into two viewpoints: from outside of an instance/class (say from a normal function call, or from the global scope), and inside of an instance/class (from within a method on the object).

From Outside Of An Instance/Class

From the outside of an instance/class, our rules are quite simple and predictable. We have two operators, and each tells us immediately if we're dealing with an instance or a class static:

  • -> - object-operator - This is always used when we're accessing an instance.

    $bob = new Person;echo $bob->name;

    需要注意的是,调用Person->foo没有意义(因为Person是一个类,而不是一个实例)。因此,这是一个解析错误。

  • ::-作用域-分辨率-运算符-这始终用于访问Class静态属性或方法。

    echo Foo::bar()

    此外,我们可以以相同的方式调用对象的静态方法:

    echo $foo::bar()

    重要的是要注意,当我们执行从外部时,对象的实例对bar()方法是隐藏的。这意味着它与运行完全相同:

    $class = get_class($foo);$class::bar();

Therefore, $this is not defined in the static call.

From Inside Of An Instance/Class

Things change a bit here. The same operators are used, but their meaning becomes significantly blurred.

The object-operator -> is still used to make calls to the object's instance state.

class Foo {public $a = 1;public function bar() {return $this->a;}}

使用对象运算符:$foo->bar()调用$fooFoo的实例)上的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类中的Foo::baz()相同(它上面的任何方法)。

  • 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()); // 1var_dump($adam->getId()); // 2var_dump($billy->getId()); // 3

因此,ID计数器在两个实例和子实例之间共享(因为我们使用self来访问它。如果我们使用static,我们可以在子类中覆盖它)。

var_dump($bob->getName()); // Bobvar_dump($adam->getName()); // Adamvar_dump($billy->getName()); // child: Billy

请注意,我们每次都执行Person::getName()实例方法。但我们在其中一个案例(子案例)中使用parent::getName()来执行它。这就是这种方法强大的原因。

注意事项#1

请注意,调用上下文决定了是否使用实例。因此:

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)

现在这里很奇怪。我们调用了一个不同的类,但传递给Foo::isFoo()方法的$this$bar的实例。

这可能会导致各种错误和概念性的WTF-ery。因此,我强烈建议在除了这三个虚拟“快捷”关键字(staticselfparent)之外的任何实例方法中避免使用::运算符。

注意事项#2

请注意,静态方法和属性是由每个人共享的。这使得它们基本上是全局变量。与全局带来的所有相同的问题。所以我真的很犹豫是否将信息存储在静态方法/属性中,除非你对它真正的全局感到满意。

注意事项#3

一般来说,你会想通过使用static而不是self来使用所谓的延迟静态绑定。但请注意,它们不是一回事,所以说“总是使用static而不是self真的是目光短浅的。相反,停下来想想你想做的调用,并想想你是否希望子类能够覆盖那个静态解析调用。

TL/DR

太糟糕了,回去读吧。它可能太长了,但它是那么长,因为这是一个复杂的话题

TL/DR#2

好的,没问题。简而言之,self用于引用类中的当前类名,其中$this引用当前对象实例。请注意,self是复制/粘贴的快捷方式。你可以安全地将其替换为你的类名,它会正常工作。但是$this是一个动态变量,不能提前确定(甚至可能不是你的类)。

TL/DR#3

如果使用了对象运算符(->),那么你总是知道你正在处理一个实例。如果使用了范围分辨率运算符(::),你需要更多关于上下文的信息(我们已经在对象上下文中了吗?我们在对象之外吗?等)。

$this指的是当前类对象,self指的是当前类(非对象)。类是对象的蓝图。所以你定义了一个类,但你构造了对象。

换句话说,使用self for staticthis for none-static members or methods

同样在子/父场景中,self / parent主要用于标识子/父类成员和方法。

此外,由于$this::尚未讨论。

仅供参考,从PHP 5.3开始,当处理实例化对象以获取当前范围值时,与使用static::相反,可以像这样使用$this::

http://ideone.com/7etRHy

class Foo{const NAME = 'Foo';
//Always Foo::NAME (Foo) due to selfprotected 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;

在PHP中,您使用self关键字来访问静态属性和方法。

问题是你可以在任何地方用self::method()替换$this->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()将始终输出“父”,而$this->who()将取决于对象的类。

现在我们可以看到self指的是调用它的类,而$this指的是当前对象的类

因此,您应该仅在$this不可用时使用self,或者当您不想允许后代类覆盖当前方法时使用self。

这是一个小基准测试(7.2.24 onrepl.it):

            Speed (in seconds)  Percentage$this->     0.91760206222534    100self::      1.0047659873962     109.49909865716static::    0.98066782951355    106.87288857386

4 000 000次运行的结果。结论:没关系。这是我使用的代码:

<?php
class Foo{public function calling_this() { $this->called(); }public function calling_self() { self::called(); }public function calling_static() { static::called(); }public static function called() {}}
$foo = new Foo();$n = 4000000;$times = [];
// warmupfor ($i = 0; $i < $n; $i++) { $foo->calling_this(); }for ($i = 0; $i < $n; $i++) { $foo->calling_self(); }for ($i = 0; $i < $n; $i++) { $foo->calling_static(); }
$start = microtime(true);for ($i = 0; $i < $n; $i++) { $foo->calling_this(); }$times["this"] = microtime(true)-$start;
$start = microtime(true);for ($i = 0; $i < $n; $i++) { $foo->calling_self(); }$times["self"] = microtime(true)-$start;
$start = microtime(true);for ($i = 0; $i < $n; $i++) { $foo->calling_static(); }$times["static"] = microtime(true)-$start;
$min = min($times);echo $times["this"] . "\t" . ($times["this"] / $min)*100 . "\n";echo $times["self"] . "\t" . ($times["self"] / $min)*100 . "\n";echo $times["static"] . "\t" . ($times["static"] / $min)*100 . "\n";

根据php.net在这种情况下有三个特殊的关键字:selfparentstatic。它们用于从类定义内部访问属性或方法。

另一方面,$this用于调用任何类的实例和方法,只要该类是可访问的。

self引用当前类(它在其中被调用),

$this表示当前对象。您可以使用静态而不是self。

请看示例:

class ParentClass {function test() {self::which();    // Outputs 'parent'$this->which();   // Outputs 'child'}
function which() {echo 'parent';}}
class ChildClass extends ParentClass {function which() {echo 'child';}}
$obj = new ChildClass();$obj->test();

输出:

 parentchild

案例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;}}

我遇到了同样的问题,简单的答案是:

  • $this需要一个类的实例
  • self::没有

每当您使用静态方法静态属性并且想要在没有实例化类对象的情况下调用它们时,您需要使用self:来调用它们,因为$this总是需要创建一个对象。

塞尔f::用于当前类的关键字,基本上它用于访问静态成员、方法和常量。但在$这个的情况下,您不能调用静态成员、方法和函数。

您可以在另一个类中使用塞尔f::关键字并访问静态成员、方法和常量。当它将从父类扩展时,$这个关键字也是如此。当它将从父类扩展时,您可以访问另一个类中的非静态成员、方法和函数。

下面给出的代码是塞尔f::$这个关键字的示例。只需将代码复制并粘贴到您的代码文件中并查看输出。

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());