题 什么时候使用自我超过$?


在PHP 5中,使用之间有什么区别 self 和 $this

什么时候适合?


1777
2017-09-30 06:23


起源


可能重复 新自我与新静态 - Orangepill
我要问有什么区别:cont A; $ this-> A和self :: A - mboullouz


答案:


简答

使用 $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

通过 http://board.phpbuilder.com/member.php?145249-laserlight


1512
2017-09-30 06:29



这个答案过于简单化了。正如其他答案所指出的, self 与范围解析运算符一起使用 :: 引用当前的类;这可以在静态和非静态上下文中完成。此外,使用它是完全合法的 $this 调用静态方法(但不是引用字段)。 - Artefacto
如果您使用的是5.3+,还可以考虑使用static ::而不是:: self。否则可能会引起无法头痛的问题,请参阅下面的答案,了解原因。 - Sqoo
-1。这个答案有误导性,请阅读其他答案以获取更多信息。 - Pacerier
它可能过于简化了,但它解决了我的基本问题而没有让我的脑袋爆炸。我确实得到了一些我发现有用的信息,但是现在我只想弄清楚为什么我用$ this-> attrib命中我的类属性,用self :: constant命中类常量。这有助于我更好地理解 - MydKnight
关于什么 $this::? - James


关键字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 指针在被调用的函数中。


710
2017-07-27 18:00



如果你从一般规则开始而不是例外,这个答案会更好。这是一种风格问题,而不是技术专长。这是我见过的关于self ::和$ this->之间差异的最好例子,但是通过首先反驳一个概念来隐藏它是一种耻辱。 - adjwilli
@adjwilli:为什么那种糟糕的风格?如果OP的期望(论点)首先被拒绝(对立),那么它是否会提高意识,然后将解释作为综合给出? - hakre
我发现“当前阶级”确实存在问题。由于这个单词组合可以理解为“班级所在 self 位于“/”的类定义它是“以及”对象类的“字面部分”(实际上它将是 static)。 - Jakumi
关于什么 $this::? - James


不使用 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::是指一个班级


429
2017-07-24 15:08



类常量怎么样? - Kevin Bond
我猜也一样 - Sqoo
“呼唤已故:: status()我们希望看到”人死了“”。不。这是一个静态函数调用,因此不涉及多态。 - cquezel
@jasondavis谢谢,我试图使用英语并避免使用多态等术语。这个答案通常仅仅是计算机科学家所理解的,而PHP不是针对计算机科学家的语言! - Sqoo
这个例子让我感到困惑:我明白了 getStatus 方法作为一个我会调用一个类实例,而不是一个类。 - Jānis Elmeris


要真正理解我们在谈论时所谈论的内容 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种类型的“东西”:

  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 变量 没有定义。这是因为没有与静态调用相关联的实例。

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

进入国家

所以现在我们已经存储了那个状态,我们需要访问它。这可能会有点棘手(或 办法 不止一点,所以让我们将它分成两个视点:从实例/类的外部(比如来自普通函数调用,或来自全局范围),以及实例/类的内部(来自一个方法内部)目的)。

来自实例/类的外部

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

  • ->  - 对象操作  - 当我们访问实例时总是使用它。

    $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() 在其中一个案例(儿童案件)中这样做。这就是使这种方法变得强大的原因。

谨慎的话#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)

现在它是  这里很奇怪。我们打电话给另一个班级,但是 $this 那传递给了 Foo::isFoo() 方法是实例 $bar

这可能会导致各种错误和概念性的WTF-ery。所以我强烈建议避免 :: 从实例方法中的运算符除了那三个虚拟“快捷”关键字之外的任何东西(staticself,和 parent)。

谨慎的话#2

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

谨慎的话#3

一般来说,你会想要使用所谓的Late-Static-Binding static 代替 self。但请注意,它们不是一回事,所以说“总是使用 static 代替 self 真的是近视。相反,停下来思考你想要进行的调用,并思考你是否希望子类能够覆盖它 静态解决 呼叫。

TL / DR

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

TL / DR#2

好的。简而言之, self 用来参考 当前的班级名称在课堂上,在哪里 $this 指当前的对象 。注意 self 是一个复制/粘贴捷径。您可以安全地用您的班级名称替换它,它会正常工作。但 $this 是一个动态变量,无法提前确定(甚至可能不是您的班级)。

TL / DR#3

如果使用对象运算符(->), 然后你 总是 知道你正在处理一个实例。如果使用范围解析运算符(::),您需要有关上下文的更多信息(我们是否已经在对象上下文中?我们是否在对象之外?等等)。


228
2018-06-10 15:21



警告#1:$在调用静态方法时不会定义: 3v4l.org/9kr0e - Mark Achée
好... $this 如果您遵循“严格标准”并且不静态调用未定义为静态的方法,则不会定义。我看到你在这里解释的结果: 3v4l.org/WeHVM 同意,真的很奇怪。 - Mark Achée
在完整地阅读了长篇描述之后,我觉得懒得再次滚动以上来进行投票。只是开玩笑,我做了upvote它:D。谢谢这非常有用。 - Mr_Green
很高兴为self :: $ property和self :: property之间的区别添加一个明确的解释;我觉得那也很混乱 - Tommaso Barbugli
妇委会#1 表现不同 自PHP 7.以来 Foo::isFoo() 被称为静态的, $this 将不会被定义。在我看来,这是更直观的行为。 - 另一个 不同的结果 如果 Bar 从...延伸 Foo。然后是电话 Foo::isFoo() 实际上是在实例上下文中(不是特定于PHP7)。 - Kontrollfreak


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

因为 this 是一个对象,你使用它像: $this->member

因为 self 它不是一个对象,它基本上是一个自动引用当前类的类型,你可以像下面这样使用它: self::member


109
2017-09-30 07:26





$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对该对象一无所知。它只是处理你传入的参数,就像一个不属于对象的普通函数。那么,为什么我们可以把它作为对象的一部分呢?

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

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

静态变量也是如此: 例: 查看:: templates_path与self :: templates_path

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

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

希望有助于清理事情:)


93
2017-10-22 17:52



你的第二段文字中有$ ronny,但除非我错了,应该是$ derek。 - James Skemp
很好的答案。我只想指出,在引用静态属性时,你需要使用一个 $ 标志。例如 self::$templates_path - henrywright


这篇博文

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

27
2018-05-10 12:00