ChatGPT解决这个技术问题 Extra ChatGPT

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

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

什么时候合适?

New self vs. new static 的可能重复项

a
allen

简答

使用 $this 来引用当前对象。使用 self 来引用当前类。换句话说,对非静态成员使用 $this->member,对静态成员使用 self::$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();
?>

下面是一个 polymorphism 示例,其中 $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() 成员函数,无论是当前对象的确切类型。如果对象是 X 类型,则它因此调用 X::foo()。如果对象是 Y 类型,它调用 Y::foo()。但是对于 self::foo(),X::foo() 总是被调用。

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

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


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

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


如果您从一般规则而不是例外开始,这个答案会更好。这是风格问题,而不是技术专长。这是我见过的关于 self:: 和 $this-> 之间区别的最好例子,但是通过首先反驳一个概念来隐藏它是一种耻辱。
@adjwilli:为什么这种风格不好?如果 OP 的期望(正题)首先被否决(反题),然后作为综合给出解释,它不会提高意识吗?
我发现“当前班级”确实有问题。由于该词组合可以理解为“self 所在的类”/“它是其文字部分的类定义”以及“对象的类”(实际上是 static)。
$this:: 呢?
@James - 没有充分的理由使用 $this::;更常用的语法已经涵盖了所有可能的情况。根据您的意思,使用 $this->self::static::
P
Peter Mortensen

请勿使用 self::。使用 static::*

self:: 还有一个方面值得一提。令人讨厌的是,self:: 指的是定义时的范围,而不是执行时的范围。考虑这个具有两种方法的简单类:

class Person
{

    public static function status()
    {
        self::getStatus();
    }

    protected static function getStatus()
    {
           echo "Person is alive";
    }

}

如果我们调用 Person::status(),我们将看到 "Person is alive" 。现在考虑当我们创建一个继承自这个的类时会发生什么:

class Deceased extends Person
{

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

}

调用 Deceased::status() 我们希望看到“Person is dead”。但是,我们看到“Person is alive”,因为作用域包含定义对 self::getStatus() 的调用时的原始方法定义。

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

请参阅PHP Documentation

因此,要回答未按要求回答的问题...

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


类常量呢?
“调用已故::status() 我们希望看到“人已故””。不,这是一个静态函数调用,因此不涉及多态性。
在 PHP 的所有缺陷中,我认为这一点都不疯狂。他们如何允许编码人员在当前类上指定方法(而不是在 vtable 中查找它们)?如果他们以不同的方式命名它(可能带有前导下划线),那么想要此功能的人会批评它丑陋。否则,无论他们使用什么健全的名称,似乎总会有容易混淆的人批评它是“疯狂”的行为,甚至可能不知道方法调度是如何工作的。
该示例让我感到困惑:我将 getStatus 方法视为我会为类实例而不是类调用的方法。
@Sqoo - 说 “不要使用 self::,使用 static::” 是一个奇怪的观点 - 这些是 故意 不同的操作。我认为你真正要表达的观点是“如果你使用实际的类名'MyClass::'而不是'self::'会更清楚。也就是说,如果你想要self::,您可以通过使用特定的类名(例如 MyClass::)来减少混淆。
C
Community

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

让我们从什么是类和对象开始。

概念上的类和对象

那么,什么是?很多人将其定义为对象的蓝图模板。事实上,您可以阅读更多内容About Classes In PHP Here。在某种程度上,这就是它的真实情况。我们来看一个类:

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

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

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

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

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

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

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

我们应该讨论的另一件事是,我们可以检查一个实例是否是一个特定类的 instanceof$bob instanceof Person 如果 $bob 实例是使用 { 4} 类, Person 的子级。

定义状态

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

属性 - 将这些视为每个实例将包含的变量。类 Foo { 公共 $bar = 1; } 静态属性 - 将这些视为在类级别共享的变量。这意味着它们永远不会被每个实例复制。类 Foo { 公共静态 $bar = 1;方法 - 这些是每个实例将包含的函数(并对实例进行操作)。 class Foo { public function bar() {} } 静态方法 - 这些是在整个类中共享的函数。它们不对实例进行操作,而是仅对静态属性进行操作。 class Foo { public static function bar() {} } 常量 - 类解析常量。这里不再深入,但为了完整性添加: class Foo { const BAR = 1; }

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

状态和方法

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

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

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

访问状态

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

从实例/类外部

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

-> - object-operator - 当我们访问实例时总是使用它。 $bob = 新人;回声$鲍勃->名称;重要的是要注意调用 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;
    }
}

使用对象运算符 $foo->bar()$fooFoo 的实例)上调用 bar() 方法将产生实例的 $a 版本。

这就是我们所期望的。

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

在静态上下文中 在静态上下文中,使用 :: 进行的任何调用也将是静态的。我们来看一个例子:class Foo { public function bar() { return Foo::baz(); } 公共函数 baz() { 返回 isset($this);调用 Foo::bar() 将静态调用 baz() 方法,因此不会填充 $this。值得注意的是,在最近的 PHP(5.3+)版本中,这将触发 E_STRICT 错误,因为我们正在静态调用非静态方法。

在实例上下文中 另一方面,在实例上下文中,使用 :: 进行的调用取决于调用的接收者(我们正在调用的方法)。如果方法被定义为静态,那么它将使用静态调用。如果不是,它将转发实例信息。因此,查看上面的代码,调用 $foo->bar() 将返回 true,因为“静态”调用发生在实例上下文中。

说得通?没想到。这很令人困惑。

快捷关键字

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

self - 这是指当前的类名。所以 self::baz() 与 Foo 类中的 Foo::baz() 相同(任何方法)。

parent - 这是指当前类的父级。

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() instance 方法。但是我们在其中一种情况(子情况)中使用 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

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


注意事项 #1:调用静态方法时不会定义 $this:3v4l.org/9kr0e
好吧...如果您遵循“严格标准”并且不静态调用未定义为静态的方法,则不会定义 $this。我看到你在这里解释的结果:3v4l.org/WeHVM同意,真的很奇怪。
完整阅读长描述后,我懒得再次滚动到上面来点赞。开个玩笑,我确实赞成:D。谢谢,这非常有用。
很高兴添加关于 self::$property 和 self::property 之间区别的清晰解释;我认为那也很混乱
WoC#1 behaves differently 自 PHP 7 起。由于 Foo::isFoo() 是静态调用的,因此不会定义 $this。在我看来,这是更直观的行为。 -- 如果 Bar 要从 Foo 扩展,则给出另一个 different result。然后调用 Foo::isFoo() 实际上会在实例上下文中(不是特定于 PHP7)。
P
Peter Mortensen

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

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

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


类型在类的类型中是什么意思?
@JinWu 这里的“类型”是指描述类的数据类型。当您编写 class Test { ... } 时,您正在创建一个新的数据类型,以及 intstringDateTime 等内置数据类型。当您编写 $x = new Test 时,$x 成为类型的对象Test
R
Rotimi

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

静态方法(即 public static 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);

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

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

SELF:: 如果您在具有要引用的静态方法的对象之外进行编码,则必须使用对象的名称调用它 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: Example: self::connection;

希望这有助于解决问题:)


很好的答案。我只想指出,当引用静态属性时,您需要使用 $ 符号。例如 self::$templates_path
A
Amal Murali

this blog post

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


A
Akib Bagwan

在 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。


Y
Yousha Aleayoub

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

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

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

M
Mohit Bumb

下面是对非静态和静态成员变量正确使用 $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();
?> 

P
Peter Mortensen

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

$this 指的是当前对象。您可以使用静态而不是自我。

请参阅示例:

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

Y
Yousha Aleayoub

对象指针 $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"

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


P
Peter Mortensen

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


建议:在酸绊倒时阅读此答案。
P
Peter Mortensen

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

特别有趣的是,您以“我相信问题不是您是否可以通过调用 ClassName::staticMember 来调用类的静态成员。问题是使用 self::classmember 和 $this->classmember 之间有什么区别”然后你继续表现出完全没有差异。事实上,您展示了一个实例,说明两个选项的工作方式相同。 -1
不过很有用。范围是关于分辨率的,这部分在 php 手册中并不清楚。我还是觉得有用
Fatal error: Access to undeclared static property: Person::$name in D:\LAMP\www\test.php on line 16
t
tleb

这是一个小基准(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";

调用无操作函数 2 000 000 次持续 1 秒。必须爱 PHP。
好旧的 PHP。 :) 但是一个电话 = 0.001 毫秒。有那么糟糕吗?
我相信这(和类似的事情)就是为什么诸如 ORM 之类的东西会感觉很慢,除非你缓存东西,而静态站点生成器就是一个东西。
理论上它应该需要 1 个处理器时钟周期,这几天大约是 1 / 2e9 s = 0.5 ns
只需重新阅读我的答案。注意:它也创建了类。我不知道为什么我没有使用 use 关键字 tbh,但我不再使用 PHP 来重做基准测试,而且我真的不想重新安装它。
H
Holger Just

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


P
Peter Mortensen

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

$this 需要一个类的实例

自我:: 没有

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


W
Will B.

此外,由于尚未讨论 $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::CONSTANT 的用法,例如 echo $foo::NAME; 而不是 $this::NAME;


P
Peter Mortensen

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

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

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


R
Rotimi

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


P
Peter Mortensen

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

p
perror

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

t
trincot

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

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