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

When should I use 'self' over '$this'?

提问人:Casey Watson 提问时间:9/30/2008 最后编辑:Peter MortensenCasey Watson 更新时间:6/11/2023 访问量:825068

问:

在 PHP 5 中,using 和 和有什么不一样?self$this

什么时候合适?

PHP OOP 作用域

评论

0赞 Orangepill 10/25/2013
自我与新静态的可能重复

答:

20赞 cruizer 9/30/2008 #1

根据 Static Keyword,没有任何 .只有 ,用于引用类(对象)的当前实例,而 ,可用于引用类的静态成员。对象实例和类之间的区别在这里起作用。$self$thisself

评论

13赞 a20 11/21/2015
建议:被酸绊倒时阅读此答案。
1924赞 John Millikin 9/30/2008 #2

简答

用于指代当前 对象。用于指代 当前类。换言之,用于非静态成员, 用于静态成员。$thisself$this->memberself::$member

完整答案

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

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

下面是非静态和静态成员变量的错误用法示例:$thisself

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

下面是通过 for 成员函数来抑制多态行为的示例: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();
?>

这个想法是调用当前对象的确切类型的成员函数。如果对象是 ,则调用 。如果对象为 ,则调用 。但是使用 self::foo(),总是被调用。$this->foo()foo()type XX::foo()type YY::foo()X::foo()

http://www.phpbuilder.com/board/showthread.php?t=10354489

http://board.phpbuilder.com/member.php?145249-laserlight 提供

评论

350赞 Artefacto 8/25/2010
这个答案过于简单化了。正如其他答案中所指出的,与范围解析运算符一起使用以引用当前类;这可以在静态和非静态上下文中完成。此外,用于调用静态方法(但不能用于引用字段)是完全合法的。self::$this
54赞 Sqoo 10/6/2011
如果您使用的是 5.3+,还可以考虑使用 static:: 而不是 ::self。否则,它可能会给您带来难以言喻的头痛,请参阅下面的答案以了解原因。
25赞 Pacerier 7/13/2013
-1.这个答案具有误导性,请阅读其他答案以获取更多信息。
8赞 MydKnight 7/12/2015
它可能过于简单化,但它回答了我的基本问题,而不会让我头晕目眩。我确实得到了一些我发现有用的更多信息,但现在我只是想弄清楚为什么我用 $this->attrib 和 self::constant 来攻击我的类常量。这帮助我更好地理解了这一点
0赞 James 12/21/2017
怎么样?$this::
123赞 Zebra North 9/30/2008 #3

self(not $self) 是指类的类型,而是指类的当前实例。 用于静态成员函数,以允许您访问静态成员变量。 用于非静态成员函数,并且是对调用成员函数的类的实例的引用。$thisself$this

因为是一个对象,所以你可以像这样使用它:this$this->member

因为 is 不是对象,所以它基本上是自动引用当前类的类型。您可以像这样使用它:selfself::member

评论

0赞 JinWu 2/3/2022
类类型中的类型是什么意思?
1赞 Zebra North 2/3/2022
@JinWu 这里的“类型”是指描述类的数据类型。当你写的时候,你正在创建一个新的数据类型,以及内置的数据类型,如、、等。当你写 时,成为 类型的对象。class Test { ... } intstringDateTime$x = new Test$xTest
102赞 lo_fye 10/23/2008 #4

$this->用于引用类的变量(成员变量)或方法的特定实例。

Example: 
$derek = new Person();

$derek现在是 Person 的特定实例。 每个人都有first_name和last_name,但$derek都有特定的first_name和last_name(德里克·马丁)。在$derek实例中,我们可以将它们称为 $this->first_name 和 $this->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--;
}

每次我们创建一个新实例时,它都会将我们的连接计数器增加 1。每次我们销毁或停止使用实例时,它都会将连接计数器减少一个。通过这种方式,我们可以监控我们正在使用的数据库对象的实例数:

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

正如你所看到的,公共静态函数 prettyName 对对象一无所知。它只是使用您传入的参数,就像不属于对象的普通函数一样。那么,如果我们能把它作为对象的一部分,为什么还要麻烦呢?

  1. 首先,将函数附加到对象有助于保持事物井井有条,以便您知道在哪里可以找到它们。
  2. 其次,它可以防止命名冲突。在一个大项目中,你可能会有两个开发人员创建 getName() 函数。如果一个创建 ClassName1::getName(),另一个创建 ClassName2::getName(),则完全没有问题。没有冲突。是的静态方法!

自我::如果要在具有要引用的静态方法的对象外部进行编码,则必须使用对象的名称 View::format_phone_number($phone_number); 如果要在具有要引用的静态方法的对象内进行编码,则可以使用对象的名称 View::format_phone_number($pn),也可以使用 self::format_phone_number($pn) 快捷方式

静态变量也是如此:示例:View:templates_path 与 self::templates_path

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

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

评论

0赞 henrywright 3/4/2015
很好的答案。我只想指出,在引用静态属性时,需要使用符号。例如$self::$templates_path
17赞 dr evil 5/19/2009 #5

我相信问题不在于你是否可以通过调用来调用类的静态成员。问题是使用 和 之间有什么区别。ClassName::staticMemberself::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;
    }
}

评论

1赞 Buttle Butkus 12/23/2011
特别有趣的是,你的答案以“我相信问题不在于你是否可以通过调用 ClassName::staticMember 来调用类的静态成员。问题是使用 self::classmember 和 $this->classmember“ 有什么区别,然后你继续显示根本没有区别。实际上,您显示了两个选项工作相同的实例。-1
0赞 renoirb 3/23/2012
尽管如此,还是有用的。范围是关于分辨率的,这部分在php手册中并不清楚。我仍然觉得它很有用
2赞 Kerem 2/22/2013
Fatal error: Access to undeclared static property: Person::$name in D:\LAMP\www\test.php on line 16
765赞 nbeagle 7/28/2009 #6

关键字 self 仅仅指“当前类”,至少不会将您限制为静态成员。在非静态成员的上下文中,还提供了一种绕过当前对象的 vtable(参见 vtable 上的 wiki)的方法。就像你可以用来调用一个函数的父版本一样,你也可以调用一个方法的当前类的实现。selfparent::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()使用指针,因此调用 vtable 来调用 . 使用 ,因此不使用 vtable,而是调用 vtable。在这两种情况下,我们都在处理实例化对象的方法,并且可以访问被调用函数中的指针。$thisGeek::getTitle()sayGoodbye()self::getTitle()Person::getTitle()$this

评论

3赞 adjwilli 9/26/2014
如果你从一般规则而不是例外开始,这个答案会更好。这是一个风格问题,而不是技术专长问题。这是我见过的关于self::和$this->之间区别的最好例子,但是通过首先反驳一个概念来掩盖这一点是一种耻辱。
3赞 hakre 10/9/2014
@adjwilli:为什么这种风格很糟糕?如果OP的期望(论文)首先被否定(对立),然后解释作为综合给出,这不是提高意识吗?
1赞 Jakumi 9/12/2017
我发现“当前班级”确实有问题。由于该词组合可以理解为“所在的类”/“它是字面部分的类定义”以及“对象的类”(实际上是)。selfstatic
0赞 James 12/21/2017
怎么样?$this::
1赞 ToolmakerSteve 10/7/2019
@James - 没有充分的理由使用;更常用的语法已经涵盖了所有可能的情况。根据您的意思,使用 、 或 。$this::$this->self::static::
489赞 Sqoo 7/24/2011 #7

不要使用 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";
    }

}

打电话时,我们希望看到“人已死亡”。但是,我们看到“Person is alive”,因为定义调用时,作用域包含原始方法定义。Deceased::status()self::getStatus()

PHP 5.3 有一个解决方案。static:: resolution 运算符实现了“后期静态绑定”,这是一种奇特的说法,即它绑定到所调用类的作用域。将行更改为,结果就是您所期望的。在旧版本的 PHP 中,您必须找到一个 kludge 来执行此操作。status()static::getStatus()

参见 PHP 文档

所以要回答这个问题,而不是像问的那样......

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

评论

6赞 Kevin Bond 4/11/2012
对于类常量呢?
61赞 cquezel 2/6/2013
“调用 Deceased::status() 我们希望看到 'Person is dead'”。不。这是一个静态函数调用,因此不涉及多态性。
2赞 tne 5/16/2015
在PHP的所有缺陷中,我不认为这太疯狂了。否则,它们将如何允许编码人员在当前类上指定方法(而不是在 vtable 中查找它们)?如果他们以不同的方式命名它(也许带有前导下划线),那么想要这个功能的人会批评它丑陋。否则,无论他们使用什么理智的名字,似乎总会有人容易混淆,他们会批评它是“疯狂”的行为,可能忘记了方法调度是如何工作的。
2赞 Jānis Elmeris 7/17/2015
这个例子对我来说似乎令人困惑:我认为方法是一种我会为类实例调用的方法,而不是为类调用的方法。getStatus
2赞 ToolmakerSteve 10/7/2019
@Sqoo - 说“不要使用self::,使用static::”是一个奇怪的观点 - 这些是故意不相同的操作。我认为你真正要说的是“如果你使用实际的类名'MyClass::',而不是'self::',那就更清楚了。也就是说,如果你想要 的行为,你可以通过使用特定的类名来获得它,不那么容易混淆,例如 .self::MyClass::
21赞 Mohit Bumb 12/6/2011 #8

以下是正确使用 $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();
?> 
13赞 mrDjouk 3/22/2013 #9

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

20赞 Xeoncross 3/22/2013 #10
  • 对象指针是指当前对象。$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()); // 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"

大多数时候,您希望引用当前类,这就是您使用 or 的原因。但是,有时您需要,因为无论扩展什么,您都需要原始类。(非常非常罕见)static$thisself

6赞 MinhajulAnwar 4/10/2013 #11

如果要调用某个类的方法而不创建该类的对象/实例,从而节省 RAM(有时为此目的使用 self),请使用该函数。换句话说,它实际上是静态调用方法。用于对象透视。selfthis

22赞 Tarun Singhal 5/8/2013 #12

在类定义中,引用当前对象,而引用当前类。$thisself

必须使用 来引用类元素,并使用 来引用对象元素。self$this

self::STAT // refer to a constant value
self::$stat // static variable
$this->stat // refer to an object variable  
31赞 okconfused 5/10/2013 #13

这篇博文

  • self指当前类
  • self可用于调用静态函数和引用静态成员变量
  • self可以在静态函数中使用
  • self还可以通过绕过 vtable 来关闭多态行为
  • $this指当前对象
  • $this可用于调用静态函数
  • $this不应用于调用静态成员变量。请改用。self
  • $this不能在静态函数内部使用
268赞 ircmaxell 6/10/2013 #14

为了真正理解我们在谈论什么,我们需要在概念和实践层面上真正深入研究正在发生的事情。我真的不觉得任何答案都恰当地做到了这一点,所以这是我的尝试。self$this

让我们先来谈谈什么是类对象

类和对象,从概念上讲

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

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

正如你所知道的,该类上有一个属性叫做,方法(函数)叫做。$namesayHello()

需要注意的是该类是一个静态结构。这意味着类一旦定义,在你看到的任何地方都是相同的。Person

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

$bob = new Person;
$adam = new Person;
$bob->name = 'Bob';
echo $adam->name; // "my name"

我们使用运算符创建一个类的新实例new

因此,我们说类是全局结构,对象是局部结构。不要担心这个有趣的语法,我们稍后会介绍它。->

我们应该讨论的另一件事是,我们可以检查一个实例是否是一个特定的类:如果实例是使用该类创建的,则返回一个布尔值,或者是 的子类。instanceof$bob instanceof Person$bobPersonPerson

定义状态

因此,让我们深入研究一下类实际包含的内容。一个类包含 5 种类型的“事物”:

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

    class Foo {
        public $bar = 1;
    }
    
  2. 静态属性 - 将这些属性视为在类级别共享的变量。这意味着它们永远不会被每个实例复制。

    class Foo {
        public static $bar = 1;
    }
    
  3. 方法 - 这些是每个实例将包含(并在实例上运行)的函数。

    class Foo {
        public function bar() {}
    }
    
  4. 静态方法 - 这些是在整个类中共享的函数。它们对实例进行操作,而只对静态属性进行操作。

    class Foo {
        public static function bar() {}
    }
    
  5. 常量 - 类解析的常量。这里不再深入讨论,但为了完整起见,请补充:

    class Foo {
        const BAR = 1;
    }
    

因此,基本上,我们使用有关静态的“提示”将信息存储在类和对象容器上,这些“提示”标识信息是共享的(因此是静态的)还是非共享的(因此是动态的)。

状态和方法

在方法内部,对象的实例由变量表示。该对象的当前状态就在那里,改变(更改)任何属性都将导致对该实例(但不是其他实例)的更改。$this

如果静态调用方法,则不会定义变量。这是因为没有与静态调用关联的实例。$this

这里有趣的是静态调用是如何进行的。因此,让我们谈谈我们如何访问状态:

访问状态

因此,现在我们已经存储了该状态,我们需要访问它。这可能会变得有点棘手(或不止一点点),所以让我们把它分成两个观点:从实例/类外部(比如从普通函数调用,或从全局范围),和实例/类内部(从对象上的方法内部)。

从实例/类外部

从实例/类的外部来看,我们的规则非常简单且可预测。我们有两个运算符,每个运算符都会立即告诉我们,我们是在处理实例还是类静态:

  • -> - object-operator - 当我们访问实例时,始终使用此选项。

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

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

  • :: - scope-resolution-operator - 这始终用于访问 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;
    }
}

使用 object-operator: 调用 上的方法(实例 )将导致实例的 版本为 。bar()$fooFoo$foo->bar()$a

这就是我们所期望的。

但运算符的含义发生了变化。这取决于对当前函数的调用的上下文:::

  • 在静态上下文中

    在静态上下文中,使用进行的任何调用也将是静态的。让我们看一个例子:::

    class Foo {
        public function bar() {
            return Foo::baz();
        }
        public function baz() {
            return isset($this);
        }
    }
    

    调用将静态调用该方法,因此不会填充。值得注意的是,在最新版本的 PHP (5.3+) 中,这将触发一个错误,因为我们静态调用非静态方法。Foo::bar()baz()$thisE_STRICT

  • 在实例上下文中

    另一方面,在实例上下文中,使用的调用取决于调用的接收方(我们正在调用的方法)。如果该方法定义为 ,则它将使用静态调用。如果不是,它将转发实例信息。::static

    因此,查看上面的代码,调用将返回 ,因为“静态”调用发生在实例上下文中。$foo->bar()true

有意义?我不这么认为。这令人困惑。

快捷键关键字

因为使用类名将所有内容联系在一起是相当肮脏的,PHP 提供了 3 个基本的“快捷方式”关键字来使范围解析更容易。

  • 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 计数器在实例和子实例之间共享(因为我们使用它来访问它。如果我们使用 ,我们可以在子类中覆盖它)。selfstatic

var_dump($bob->getName()); // Bob
var_dump($adam->getName()); // Adam
var_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)

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

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

警告词 #2

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

警告词 #3

通常,您需要使用 而不是 来使用所谓的 Late-Static-Binding。但请注意,它们不是一回事,所以说“总是使用而不是”真的是短视的。相反,停下来想想你要进行的调用,并想想你是否希望子类能够覆盖该静态解析的调用。staticselfstaticself

TL/DR (英语)

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

TL/DR #2

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

TL/DR #3

如果使用了对象运算符 (),那么你总是知道你正在处理一个实例。如果使用 scope-resolution-operator (),则需要有关上下文的更多信息(我们是否已经处于对象上下文中?我们是否在物体之外?等)。->::

评论

1赞 Mark Achée 6/11/2013
警告 #1:调用静态方法时不会定义$this:3v4l.org/9kr0e
0赞 Mark Achée 6/11/2013
井。。。 如果遵循“严格标准”并且不静态调用未定义为静态的方法,则不会定义。我看到你在这里解释的结果:3v4l.org/WeHVM 同意,真的很奇怪。$this
2赞 Mr_Green 10/4/2013
在完整阅读了长篇描述后,我懒得再次滚动到上面为它投票。开个玩笑,我确实:D投了赞成票。谢谢,这非常有用。
3赞 Tommaso Barbugli 6/26/2014
最好对 self::$property 和 self::p roperty 之间的区别进行清晰的解释;我认为这也很令人困惑
1赞 Kontrollfreak 3/22/2016
自 PHP 7 以来,WoC#1 的行为有所不同。作为静态调用,不会被定义。在我看来,这是更直观的行为。-- 如果从 扩展,则给出另一个不同的结果。然后,调用实际上将在实例上下文中(不特定于 PHP7)。Foo::isFoo()$thisBarFooFoo::isFoo()
7赞 Rakesh Singh 9/15/2014 #15

$this引用当前类对象,并引用当前类(Not object)。类是对象的蓝图。因此,您定义了一个类,但是您构造了对象。self

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

此外,在子类/父类方案中,主要用于标识子类和父类成员和方法。self / parent

7赞 Will B. 9/30/2014 #16

此外,此后尚未讨论。$this::

仅供参考,从 PHP 5.3 开始,在处理实例化对象以获取当前范围值时,而不是使用 ,也可以这样使用。static::$this::

http://ideone.com/7etRHy

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::CONSTANTecho $foo::NAME;$this::NAME;

27赞 ramin rostami 12/29/2014 #17

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

在此示例中,将始终输出 'parent',而 将取决于对象具有的类。self::who()$this->who()

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

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

14赞 tleb 5/31/2015 #18

这是一个小的基准测试(repl.it 上的 7.2.24):

            Speed (in seconds)  Percentage
$this->     0.91760206222534    100
self::      1.0047659873962     109.49909865716
static::    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 = [];

// warmup
for ($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";

评论

1赞 rr- 9/6/2015
调用空操作函数 2 000 000 次持续 1 秒。一定要喜欢PHP。
0赞 tleb 9/9/2015
好老的PHP。:)但是呼叫 = 0.001 毫秒。有那么糟糕吗?
1赞 rr- 9/9/2015
我相信这(以及类似的事情)这就是为什么像 ORM 这样的东西感觉很慢,除非你缓存东西,而静态站点生成器是一回事。
2赞 Buddy 9/29/2015
从理论上讲,它应该需要 1 个处理器时钟周期,这大约是这些天1 / 2e9 s = 0.5 ns
0赞 tleb 4/29/2017
只需重新阅读我的答案。请注意:它也会创建类。我不知道为什么我没有使用关键字tbh,但是我不再有PHP来重做基准测试,而且我真的不想重新安装它。use
1赞 Fil 12/7/2015 #19

根据 php.net 在此上下文中有三个特殊关键字:和 。它们用于从类定义内部访问属性或方法。selfparentstatic

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

23赞 Kabir Hossain 1/1/2016 #20

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

$this指当前对象。 您可以使用 static 代替 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();

输出:

 parent
 child
2赞 li bing zhao 2/3/2016 #21

案例 1:Use 可用于类常量self

 class classA { 
     const FIXED_NUMBER = 4; 
     self::POUNDS_TO_KILOGRAMS
}

如果要在类外部调用它,请使用 to 访问常量classA::POUNDS_TO_KILOGRAMS

案例 2:对于静态属性

class classC {
     public function __construct() { 
     self::$_counter++; $this->num = self::$_counter;
   }
}
11赞 Mike 5/19/2018 #22

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

  • $this需要类的实例
  • self::

每当您使用静态方法静态属性并希望在不实例化类对象的情况下调用它们时,您都需要使用来调用它们,因为始终需要创建一个对象。self:$this

5赞 Deepak Syal 4/17/2020 #23

自我::用于当前类的关键字,基本上用于访问静态成员、方法和常量。但在$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());
1赞 Ajay Singh 6/11/2023 #24

虽然这是一个老问题,但作为一个新手,我发现以下比较很有用。特别是与 一起使用所需的规则和语法。self$this


// A. Definitions
   
// Three classes bar1 for parallel use, and bar2 and bar3 for linear use;

class bar1 {
    // Class bar1 to use the object method "$this",
    // to create multiple parallel instances;
    
    public $foo = 111; // Property "$foo" set without "static" keyword; 
    // because we will be using the object method "$this",
    // to create multiple (unique and fresh) instances;
    
    public function getfoo() {
        return $this->foo; // Function "getfoo" returns the value of property "foo";
        // While calling the property using "$this", no "$" symbol is used; 
        // By calling our class "bar1" using "$this" instead of "self", 
        // we can use on multiple instances;
    }
    public function setfoo($foo) {
        $this->foo = $foo; // Function "setfoo" sets the value of property "foo"; 
        // While calling the property using "$this", no "$" symbol is used; 
        // By calling our class "bar1" using "$this" instead of "self", 
        // we can use on multiple instances;
    }
}

class bar2 {
    // Class bar2 to use the static method "self" to use as single linear instance;
    // Note the use of "::" (scope resolution operator) instead of object call "->" 
    
    public static $foo = 111; // Property "$foo" set with "static" keyword; 
        // because we will be using the static method "self" to use as single instance;
    
    public static function getfoo() {
        return self::$foo; // Function "getfoo" returns the value of property "foo"; 
        // While calling the property using "self", "$" symbol is necessary; 
        // We are calling our class "bar2" using "self" instead of "$this", 
        // because we are using static method "self" to use as single instance;
    }
    public static function setfoo($foo) {
        self::$foo = $foo; // Function "setfoo" sets the value of property "foo"; 
        // While calling the property using "self", "$" symbol is necessary; 
        // We are calling our class "bar2" using "self" instead of $this, 
        // because we are using static method "self" to use as single instance;
    }
}

class bar3 {
    // Class bar3 is same as bar2 and uses the static method "self", 
    // except it sets the property "$foo" using "__construct" constructor method;
    // so it can be used as a single linear instance,
    // which can be optionally reset/reinitialized;
    
    public static $foo; // Property "$foo" initialized with "static" keyword;
        // No value is set here because we are setting it using "__construct" instead;
        
    public function __construct() { // "__construct" will set the property values
        self::$foo = 111;
    }
    
    public static function getfoo() {
        return self::$foo;
    }
    public static function setfoo($foo) {
        self::$foo = $foo;
    }
}

// B. Tests


// B.1 Object method; Parallel instances $x and $y;

$x = new bar1(); // Fresh instance $x
echo $x->getfoo().PHP_EOL; // Output: 111 
$x->setfoo(123); // Updated instance $x
echo $x->getfoo().PHP_EOL.PHP_EOL; // Output: 123

$y = new bar1(); // Fresh instance $y
echo $y->getfoo().PHP_EOL; // Output: 111 
$y->setfoo(143); // Updated instance $y
echo $y->getfoo().PHP_EOL.PHP_EOL; // Output: 143


// B.2 Static method; Single linear instance;

new bar2(); // Not a fresh instance; Can be omitted;
echo bar2::getfoo().PHP_EOL; // Output: 111 
bar2::setfoo(123); // Updated the same static instance
echo bar2::getfoo().PHP_EOL.PHP_EOL; // Output: 123 

new bar2(); // Not a fresh instance; Can be omitted;
echo bar2::getfoo().PHP_EOL; // Output: 123
bar2::setfoo(143); // Updated the same static instance
echo bar2::getfoo().PHP_EOL.PHP_EOL; // Output: 143


// B.3 Static method using __construct; Single linear yet re-settable instance;

new bar3(); // Resets into a fresh instance; Omitting this line will reuse old instance;
echo bar3::getfoo().PHP_EOL; // Output: 111 
bar3::setfoo(123); // Updated the same static instance
echo bar3::getfoo().PHP_EOL.PHP_EOL; // Output: 123 

new bar3(); // Resets into a fresh instance; Omitting this line will reuse old instance;
echo bar3::getfoo().PHP_EOL; // Output: 111
bar3::setfoo(143); // Updated the same static instance
echo bar3::getfoo().PHP_EOL.PHP_EOL; // Output: 143

评论

0赞 Desper 10/9/2023
一个很好的解释 Bahi Sahib,最谦虚的人