Khi nào nên sử dụng bản thân trên $ này?


Câu trả lời:


1728

Câu trả lời ngắn

Sử dụng $thisđể chỉ đối tượng hiện tại. Sử dụng selfđể tham khảo các lớp hiện tại. Nói cách khác, sử dụng $this->membercho các thành viên không tĩnh, sử dụng self::$membercho các thành viên tĩnh.

Trả lời đầy đủ

Dưới đây là một ví dụ về việc sử dụng đúng$thisselfcho các biến thành viên không tĩnh và tĩnh:

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

Dưới đây là một ví dụ về việc sử dụng không chính xác$thisselfcho các biến thành viên không tĩnh và tĩnh:

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

Dưới đây là một ví dụ về đa hình với $thiscác hàm thành viên:

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

Dưới đây là một ví dụ về việc triệt tiêu hành vi đa hình bằng cách sử dụng selfcho các hàm thành viên:

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

Ý tưởng là $this->foo()gọi foo()hàm thành viên của bất cứ thứ gì là loại chính xác của đối tượng hiện tại. Nếu đối tượng là của type X, do đó nó gọi X::foo(). Nếu đối tượng là của type Y, nó gọi Y::foo(). Nhưng với self :: foo (), X::foo()luôn được gọi.

Từ http://www.phpbuilder.com/board/showthread.php?t=10354361 :

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


330
Câu trả lời này là quá đơn giản. Như được chỉ ra trong các câu trả lời khác, selfđược sử dụng với toán tử phân giải phạm vi ::để chỉ lớp hiện tại; điều này có thể được thực hiện cả trong bối cảnh tĩnh và không tĩnh. Ngoài ra, nó hoàn toàn hợp pháp để sử dụng $thisđể gọi các phương thức tĩnh (nhưng không phải cho các trường tham chiếu).
Artefacto

50
Cũng xem xét sử dụng static :: thay vì :: self nếu bạn đang ở trên 5.3+. Nó có thể khiến bạn đau đầu không kể xiết, xem câu trả lời của tôi dưới đây để biết lý do.
Sqoo

25
-1. Câu trả lời này là sai lệch, đọc các câu trả lời khác để biết thêm.
Pacerier

6
Nó có thể được đơn giản hóa quá mức, nhưng nó đã trả lời câu hỏi cấp độ cơ bản của tôi mà không làm đầu tôi nổ tung. Tôi đã nhận được một số thông tin mà tôi thấy hữu ích hơn nữa, nhưng bây giờ tôi chỉ cố gắng tìm ra lý do tại sao tôi đánh các thuộc tính lớp của mình bằng $ this-> attrib và các hằng số lớp với self :: hằng. Điều này giúp tôi hiểu điều đó tốt hơn
MydKnight

Thế còn $this::?
James

742

Bản thân từ khóa KHÔNG chỉ đề cập đến 'lớp hiện tại', ít nhất là không theo cách giới hạn bạn với các thành viên tĩnh. Trong bối cảnh của một thành viên không tĩnh, selfcũng cung cấp một cách bỏ qua vtable ( xem wiki trên vtable ) cho đối tượng hiện tại. Giống như bạn có thể sử dụng parent::methodName()để gọi phiên bản cha của hàm, vì vậy bạn có thể gọi self::methodName()để gọi việc thực hiện các lớp hiện tại của một phương thức.

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

Điều này sẽ xuất ra:

Xin chào, tôi là Ludwig người đam mê
tạm biệt từ Ludwig người

sayHello()sử dụng $thiscon trỏ, vì vậy vtable được gọi để gọi Geek::getTitle(). sayGoodbye()sử dụng self::getTitle(), vì vậy vtable không được sử dụng và Person::getTitle()được gọi. Trong cả hai trường hợp, chúng ta đang xử lý phương thức của một đối tượng được khởi tạo và có quyền truy cập vào $thiscon trỏ trong các hàm được gọi.


3
Câu trả lời này sẽ còn tốt hơn nếu bạn bắt đầu với một quy tắc chung chứ không phải là một ngoại lệ. Đó là vấn đề về phong cách, không phải chuyên môn kỹ thuật. Đây là ví dụ tốt nhất mà tôi từng thấy về sự khác biệt giữa self :: và $ this->, nhưng thật xấu hổ khi che giấu điều đó bằng cách từ chối một khái niệm trước tiên.
adjwilli

3
@adjwilli: Tại sao phong cách xấu đó? Nó không nâng cao ý thức nếu kỳ vọng (luận điểm) của OP trước tiên không được chấp thuận (phản đề) và sau đó giải thích được đưa ra dưới dạng tổng hợp?
hakre

1
Tôi thấy "lớp hiện tại" thực sự có vấn đề. Vì sự kết hợp từ đó có thể được hiểu là cả "lớp selfđược đặt" / "định nghĩa lớp, nó là một phần nghĩa đen của" cũng như "lớp của đối tượng" (thực tế sẽ là static).
Jakumi

Thế còn $this::?
James

1
@James - không có lý do chính đáng để sử dụng $this::; tất cả các trường hợp có thể đã được bao phủ bởi các cú pháp được sử dụng phổ biến hơn. Tùy thuộc vào những gì bạn có nghĩa là, sử dụng $this->, self::hoặc static::.
ToolmakerSteve

461

KHÔNG SỬ DỤNG self::, sử dụngstatic::

Có một khía cạnh khác của bản thân :: đó là điều đáng nói. Khó chịu self::đề cập đến phạm vi tại điểm định nghĩa không phải tại điểm thực hiện . Hãy xem xét lớp đơn giản này với hai phương thức:

class Person
{

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

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

}

Nếu chúng ta gọi, Person::status()chúng ta sẽ thấy "Người còn sống". Bây giờ hãy xem xét những gì xảy ra khi chúng ta tạo một lớp kế thừa từ điều này:

class Deceased extends Person
{

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

}

Gọi Deceased::status()chúng ta sẽ thấy "Người đã chết" tuy nhiên những gì chúng ta thấy là "Người còn sống" vì phạm vi chứa định nghĩa phương thức ban đầu khi cuộc gọi đến self::getStatus()được xác định.

PHP 5.3 có một giải pháp. các static::dụng cụ điều hành giải quyết "cuối tĩnh ràng buộc", đó là một cách ưa thích của nói rằng đó là ràng buộc để phạm vi của lớp được gọi là. Thay đổi dòng trong status()để static::getStatus() và kết quả là những gì bạn mong đợi. Trong các phiên bản cũ hơn của PHP, bạn sẽ phải tìm một loại bùn để làm điều này.

Xem Tài liệu PHP

Vì vậy, để trả lời câu hỏi không như hỏi ...

$this->đề cập đến đối tượng hiện tại (một thể hiện của một lớp), trong khi static::đề cập đến một lớp


6
Còn về hằng số lớp thì sao?
Kevin Bond

53
"Gọi cho người đã chết :: status () chúng tôi sẽ thấy" Người đã chết "". Không. Đây là một lệnh gọi hàm tĩnh nên không có đa hình liên quan.
cquezel

2
Trong tất cả các sai sót của PHP, tôi không nghĩ rằng điều này là điên rồ cả. Làm thế nào khác họ sẽ cho phép các lập trình viên chỉ định các phương thức trên lớp hiện tại (trái ngược với việc tìm kiếm chúng trong vtable)? Nếu họ đã đặt tên khác cho nó (có lẽ với dấu gạch dưới hàng đầu) thì những người muốn tính năng này sẽ chỉ trích nó là xấu xí. Khác, bất cứ tên lành mạnh nào họ có thể sử dụng, dường như sẽ luôn có những người dễ nhầm lẫn, những người sẽ chỉ trích nó là hành vi "điên rồ", có khả năng không biết cách thức hoạt động của phương thức.
tne

2
Ví dụ này có vẻ khó hiểu với tôi: Tôi thấy getStatusphương thức như một phương thức mà tôi sẽ gọi cho một thể hiện của lớp, không phải cho một lớp.
Jāni Elmeris

1
@Sqoo - nói rằng "KHÔNG SỬ DỤNG self ::, sử dụng static ::" là một điểm lạ để thực hiện - đó là những hoạt động không cố ý giống nhau. Tôi nghĩ rằng điểm bạn thực sự đưa ra là "rõ ràng hơn nếu bạn sử dụng tên lớp thực tế 'MyClass ::', thay vì 'self ::' . Đó là, nếu bạn muốn hành vi của self::, bạn có thể hiểu điều đó, ít hơn gây nhầm lẫn, bằng cách sử dụng tên lớp cụ thể, ví dụ MyClass::.
ToolmakerSteve

248

Để thực sự hiểu những gì chúng ta đang nói về khi chúng ta nói về selfso với $this, chúng ta cần thực sự tìm hiểu những gì đang diễn ra ở mức độ khái niệm và thực tế. Tôi thực sự không cảm thấy bất kỳ câu trả lời nào làm điều này một cách thích hợp, vì vậy đây là nỗ lực của tôi.

Hãy bắt đầu bằng cách nói về một lớp và một đối tượng là gì.

Các lớp học và đối tượng, về mặt khái niệm

Vì vậy, những gì một lớp ? Rất nhiều người định nghĩa nó là một bản thiết kế hoặc một mẫu cho một đối tượng. Trong thực tế, bạn có thể đọc thêm về các lớp học trong PHP ở đây . Và ở một mức độ nào đó đó là những gì nó thực sự là. Chúng ta hãy nhìn vào một lớp học:

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

Như bạn có thể nói, có một thuộc tính trên lớp đó được gọi $namevà một phương thức (hàm) được gọi sayHello().

Điều rất quan trọng cần lưu ý rằng lớp là một cấu trúc tĩnh. Điều đó có nghĩa là lớp Person, một khi được định nghĩa, luôn giống nhau ở mọi nơi bạn nhìn vào nó.

Mặt khác, một đối tượng là một thể hiện của Class. Điều đó có nghĩa là chúng tôi lấy "bản thiết kế" của lớp và sử dụng nó để tạo một bản sao động. Bản sao này hiện được liên kết cụ thể với biến mà nó được lưu trữ. Do đó, mọi thay đổi đối với một thể hiện là cục bộ đối với thể hiện đó.

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

Chúng tôi tạo các thể hiện mới của một lớp bằng newtoán tử.

Do đó, chúng tôi nói rằng Class là cấu trúc toàn cầu và Object là cấu trúc cục bộ. Đừng lo lắng về ->cú pháp buồn cười đó , chúng ta sẽ đi sâu vào vấn đề đó một chút.

Một điều khác mà chúng ta nên nói đến, đó là chúng ta có thể kiểm tra xem một cá thể có phải là một instanceoflớp cụ thể hay không: $bob instanceof Personnó trả về một boolean nếu $bobcá thể đó được tạo bằng cách sử dụng Personlớp hoặc một con của Person.

Xác định nhà nước

Vì vậy, hãy đào sâu một chút vào những gì một lớp thực sự chứa. Có 5 loại "thứ" mà một lớp chứa:

  1. Thuộc tính - Hãy nghĩ về những điều này như các biến mà mỗi phiên bản sẽ chứa.

    class Foo {
        public $bar = 1;
    }
  2. Thuộc tính tĩnh - Hãy nghĩ về những điều này như các biến được chia sẻ ở cấp độ lớp. Có nghĩa là chúng không bao giờ được sao chép bởi mỗi trường hợp.

    class Foo {
        public static $bar = 1;
    }
  3. Phương thức - Đây là các hàm mà mỗi phiên bản sẽ chứa (và hoạt động trên các thể hiện).

    class Foo {
        public function bar() {}
    }
  4. Phương thức tĩnh - Đây là các hàm được chia sẻ trên toàn bộ lớp. Chúng không hoạt động trên các trường hợp, mà thay vào đó chỉ trên các thuộc tính tĩnh.

    class Foo {
        public static function bar() {}
    }
  5. Hằng số - Hằng số phân giải. Không đi sâu hơn ở đây, nhưng thêm vào cho đầy đủ:

    class Foo {
        const BAR = 1;
    }

Về cơ bản, chúng tôi đang lưu trữ thông tin trên lớp và bộ chứa đối tượng bằng cách sử dụng "gợi ý" về tĩnh để xác định xem thông tin có được chia sẻ (và do đó tĩnh) hay không (và do đó là động).

Nhà nước và phương pháp

Bên trong một phương thức, một thể hiện của một đối tượng được biểu diễn bằng $thisbiến. Trạng thái hiện tại của đối tượng đó là ở đó và việc thay đổi (thay đổi) bất kỳ thuộc tính nào sẽ dẫn đến thay đổi đối với trường hợp đó (nhưng không phải là đối tượng khác).

Nếu một phương thức được gọi là tĩnh, $thisbiến không được xác định . Điều này là do không có trường hợp liên quan đến một cuộc gọi tĩnh.

Điều thú vị ở đây là cách các cuộc gọi tĩnh được thực hiện. Vì vậy, hãy nói về cách chúng ta truy cập vào tiểu bang:

Nhà nước tiếp cận

Vì vậy, bây giờ chúng tôi đã lưu trữ trạng thái đó, chúng tôi cần truy cập nó. Điều này có thể nhận được một chút khó khăn (hoặc cách hơn một chút), vì vậy chúng ta hãy chia thành hai quan điểm: từ bên ngoài của một thể hiện / lớp (nói từ một cuộc gọi chức năng bình thường, hoặc từ phạm vi toàn cầu), và bên trong một thể hiện / class (từ bên trong một phương thức trên đối tượng).

Từ bên ngoài của một trường hợp / lớp

Từ bên ngoài của một thể hiện / lớp, các quy tắc của chúng tôi khá đơn giản và có thể dự đoán được. Chúng tôi có hai toán tử và mỗi toán tử cho chúng tôi biết ngay lập tức nếu chúng ta xử lý một thể hiện hoặc một lớp tĩnh:

  • ->- toán tử đối tượng - Điều này luôn được sử dụng khi chúng ta truy cập một thể hiện.

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

    Điều quan trọng cần lưu ý là việc gọi Person->fookhông có ý nghĩa (vì Personlà một lớp, không phải là một thể hiện). Do đó, đó là một lỗi phân tích cú pháp.

  • ::- Toán tử phân giải phạm vi - Điều này luôn được sử dụng để truy cập vào một thuộc tính hoặc phương thức tĩnh Class.

    echo Foo::bar()

    Ngoài ra, chúng ta có thể gọi một phương thức tĩnh trên một đối tượng theo cùng một cách:

    echo $foo::bar()

    Điều cực kỳ quan trọng cần lưu ý là khi chúng ta thực hiện điều này từ bên ngoài , đối tượng của đối tượng bị ẩn khỏi bar()phương thức. Có nghĩa là nó giống hệt như đang chạy:

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

Do đó, $thiskhông được xác định trong cuộc gọi tĩnh.

Từ bên trong của một trường hợp / lớp

Mọi thứ thay đổi một chút ở đây. Các toán tử tương tự được sử dụng, nhưng ý nghĩa của chúng trở nên mờ đi đáng kể.

Các đối tượng khai thác -> vẫn được sử dụng để thực hiện cuộc gọi đến trạng thái dụ của đối tượng.

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

Gọi bar()phương thức trên $foo(một thể hiện Foo) bằng cách sử dụng toán tử đối tượng: $foo->bar()sẽ dẫn đến phiên bản của thể hiện của $a.

Vì vậy, đó là cách chúng ta mong đợi.

Ý nghĩa của ::toán tử mặc dù thay đổi. Nó phụ thuộc vào ngữ cảnh của lệnh gọi đến hàm hiện tại:

  • Trong một bối cảnh tĩnh

    Trong ngữ cảnh tĩnh, mọi cuộc gọi được thực hiện ::cũng sẽ là tĩnh. Hãy xem xét một ví dụ:

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

    Gọi Foo::bar()sẽ gọi baz()phương thức tĩnh và do đó $thissẽ không được thực hiện. Điều đáng chú ý là trong các phiên bản gần đây của PHP (5.3+), điều này sẽ gây ra E_STRICTlỗi, bởi vì chúng tôi đang gọi các phương thức không tĩnh.

  • Trong một bối cảnh ví dụ

    Mặt khác, trong bối cảnh cá thể, các cuộc gọi được thực hiện sử dụng ::phụ thuộc vào người nhận cuộc gọi (phương thức chúng tôi đang gọi). Nếu phương thức được định nghĩa là static, thì nó sẽ sử dụng một cuộc gọi tĩnh. Nếu không, nó sẽ chuyển tiếp thông tin cá thể.

    Vì vậy, nhìn vào đoạn mã trên, cuộc gọi $foo->bar()sẽ trở lại true, vì cuộc gọi "tĩnh" xảy ra bên trong bối cảnh thể hiện.

Có lý? Không nghĩ vậy. Thật khó hiểu.

Từ khóa ngắn

Vì việc buộc mọi thứ lại với nhau bằng cách sử dụng tên lớp khá bẩn, PHP cung cấp 3 từ khóa "phím tắt" cơ bản để giúp giải quyết phạm vi dễ dàng hơn.

  • self- Điều này đề cập đến tên lớp hiện tại. Vì vậy, self::baz()giống như Foo::baz()trong Foolớp (bất kỳ phương thức nào trên đó).

  • parent - Điều này đề cập đến cha mẹ của lớp hiện tại.

  • static- Điều này đề cập đến lớp được gọi. Nhờ kế thừa, các lớp con có thể ghi đè các phương thức và thuộc tính tĩnh. Vì vậy, gọi chúng bằng cách sử dụng staticthay vì tên lớp cho phép chúng tôi giải quyết cuộc gọi đến từ đâu, thay vì mức hiện tại.

Ví dụ

Cách dễ nhất để hiểu điều này là bắt đầu xem xét một số ví dụ. Hãy chọn một lớp học:

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

Bây giờ, chúng tôi cũng đang xem xét thừa kế ở đây. Bỏ qua một chút rằng đây là một mô hình đối tượng xấu, nhưng hãy xem điều gì xảy ra khi chúng ta chơi với điều này:

$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

Vì vậy, bộ đếm ID được chia sẻ trên cả hai trường hợp và trẻ em (vì chúng tôi đang sử dụng selfđể truy cập nó. Nếu chúng tôi sử dụng static, chúng tôi có thể ghi đè lên nó trong một lớp con).

var_dump($bob->getName()); // Bob
var_dump($adam->getName()); // Adam
var_dump($billy->getName()); // child: Billy

Lưu ý rằng chúng tôi đang thực hiện phương thức Person::getName() cá thể mỗi lần. Nhưng chúng tôi đang sử dụng parent::getName()để làm điều đó trong một trong các trường hợp (trường hợp con). Đây là những gì làm cho cách tiếp cận này mạnh mẽ.

Lời cảnh báo số 1

Lưu ý rằng bối cảnh gọi là những gì xác định nếu một thể hiện được sử dụng. Vì thế:

class Foo {
    public function isFoo() {
        return $this instanceof Foo;
    }
}

Không phải lúc nào cũng đúng.

class Bar {
    public function doSomething() {
        return Foo::isFoo();
    }
}
$b = new Bar;
var_dump($b->doSomething()); // bool(false)

Bây giờ nó thực sự kỳ lạ ở đây. Chúng ta đang gọi một lớp khác, nhưng lớp $thisđược truyền cho Foo::isFoo()phương thức này là ví dụ của $bar.

Điều này có thể gây ra tất cả các loại lỗi và WTF-ery khái niệm. Vì vậy, tôi rất muốn đề nghị tránh các ::nhà điều hành từ bên trong phương pháp dụ về bất cứ điều gì ngoại trừ những ba ảo "short-cut" từ khóa ( static, selfparent).

Lời cảnh báo # 2

Lưu ý rằng các phương thức và thuộc tính tĩnh được chia sẻ bởi mọi người. Điều đó làm cho chúng về cơ bản biến toàn cầu. Với tất cả các vấn đề tương tự đi kèm với toàn cầu. Vì vậy, tôi sẽ rất do dự khi lưu trữ thông tin trong các phương thức / thuộc tính tĩnh trừ khi bạn cảm thấy thoải mái với nó là thực sự toàn cầu.

Lời cảnh báo số 3

Nói chung, bạn sẽ muốn sử dụng cái được gọi là Liên kết tĩnh tĩnh bằng cách sử dụng staticthay vì self. Nhưng lưu ý rằng chúng không giống nhau, vì vậy nói rằng "luôn luôn sử dụng staticthay vì selfthực sự thiển cận. Thay vào đó, hãy dừng lại và suy nghĩ về cuộc gọi bạn muốn thực hiện và suy nghĩ nếu bạn muốn các lớp con có thể ghi đè lên phần tĩnh đó được giải quyết gọi.

TL / DR

Quá tệ, quay lại và đọc nó. Nó có thể quá dài, nhưng nó dài vì đây là một chủ đề phức tạp

TL / DR # 2

Được rồi Nói tóm lại, selfđược sử dụng để tham khảo tên lớp hiện tại trong một lớp học, nơi như $thisđề cập đến đối tượng hiện dụ . Lưu ý rằng đó selflà một bản sao / dán cắt ngắn. Bạn có thể thay thế nó một cách an toàn bằng tên lớp của bạn và nó sẽ hoạt động tốt. Nhưng $thislà một biến động không thể được xác định trước thời hạn (và thậm chí có thể không phải là lớp của bạn).

TL / DR # 3

Nếu toán tử đối tượng được sử dụng ( ->), thì bạn luôn biết bạn đang xử lý một thể hiện. Nếu toán tử phạm vi phân giải phạm vi được sử dụng ( ::), bạn cần thêm thông tin về bối cảnh (chúng ta đã ở trong bối cảnh đối tượng chưa? Chúng ta có ở bên ngoài một đối tượng không? V.v.).


1
Lời cảnh báo # 1: $ điều này sẽ không được xác định khi gọi một phương thức tĩnh: 3v4l.org/9kr0e
Mark Achée

Chà ... $thissẽ không được xác định nếu bạn tuân theo "Tiêu chuẩn nghiêm ngặt" và không gọi các phương thức tĩnh mà không được định nghĩa là tĩnh. Tôi thấy kết quả mà bạn đã giải thích ở đây: 3v4l.org/weHVM Đồng ý, thực sự kỳ lạ.
Đánh dấu Achée

2
Sau khi đọc mô tả dài hoàn toàn, tôi cảm thấy lười biếng cuộn lên trên một lần nữa để nâng cấp nó. Đùa thôi, tôi đã upvote nó: D. Cảm ơn điều này rất hữu ích.
Mr_Green

3
sẽ rất tuyệt nếu thêm một lời giải thích rõ ràng về sự khác biệt giữa self :: $ property và self :: property; Tôi nghĩ điều đó cũng khá khó hiểu
Tommaso Barbugli

1
WoC # 1 hành xử khác với PHP 7. Như Foo::isFoo()được gọi là tĩnh, $thissẽ không được xác định. Đó là hành vi trực quan hơn theo ý kiến ​​của tôi. - Một kết quả khác được đưa ra nếu Barđược gia hạn từ Foo. Sau đó, cuộc gọi Foo::isFoo()sẽ thực sự nằm trong ngữ cảnh cá thể (không dành riêng cho PHP7).
Kontrollfreak

117

self(không $ tự) đề cập đến các loại của lớp, nơi như $thisđề cập đến hiện tại dụ của lớp. selfđược sử dụng trong các hàm thành viên tĩnh để cho phép bạn truy cập các biến thành viên tĩnh. $thisđược sử dụng trong các hàm thành viên không tĩnh và là một tham chiếu đến thể hiện của lớp mà hàm thành viên được gọi.

Bởi vì thislà một đối tượng, bạn sử dụng nó như:$this->member

Bởi vì selfkhông phải là một đối tượng, về cơ bản nó là một loại tự động đề cập đến lớp hiện tại, bạn sử dụng nó như sau:self::member


97

$this-> được sử dụng để chỉ một thể hiện cụ thể của các biến của lớp (biến thành viên) hoặc phương thức.

Example: 
$derek = new Person();

$ derek hiện là một ví dụ cụ thể của Person. Mọi người đều có First_name và last_name, nhưng $ derek có một First_name và last_name cụ thể (Derek Martin). Trong ví dụ $ derek, chúng ta có thể gọi những cái đó là $ this-> first_name và $ this-> last_name

ClassName :: được sử dụng để chỉ loại lớp đó và các biến tĩnh của nó, các phương thức tĩnh. Nếu nó hữu ích, bạn có thể thay thế từ "tĩnh" bằng "chia sẻ". Bởi vì chúng được chia sẻ, chúng không thể đề cập đến $ this, trong đó đề cập đến một trường hợp cụ thể (không được chia sẻ). Các biến tĩnh (tức là $ db_connection tĩnh) có thể được chia sẻ giữa tất cả các phiên bản của một loại đối tượng. Ví dụ, tất cả các đối tượng cơ sở dữ liệu chia sẻ một kết nối (kết nối $ tĩnh).

Các biến tĩnh Ví dụ: Giả sử chúng ta có một lớp cơ sở dữ liệu với một biến thành viên duy nhất: static $ num_connections; Bây giờ, đặt điều này trong hàm tạo:

function __construct()
{
    if(!isset $num_connections || $num_connections==null)
    {
        $num_connections=0;
    }
    else
    {
        $num_connections++;
    }
}

Giống như các đối tượng có các hàm tạo, chúng cũng có các hàm hủy, được thực thi khi đối tượng chết hoặc không được đặt:

function __destruct()
{
    $num_connections--;
}

Mỗi khi chúng ta tạo một thể hiện mới, nó sẽ tăng một bộ đếm kết nối của chúng ta. Mỗi khi chúng ta hủy hoặc ngừng sử dụng một thể hiện, nó sẽ làm giảm bộ đếm kết nối. Theo cách này, chúng ta có thể theo dõi số lượng phiên bản của đối tượng cơ sở dữ liệu mà chúng ta đang sử dụng với:

echo DB::num_connections;

Vì $ num_connections là tĩnh (được chia sẻ), nên nó sẽ phản ánh tổng số đối tượng cơ sở dữ liệu đang hoạt động. Bạn có thể đã thấy kỹ thuật này được sử dụng để chia sẻ các kết nối cơ sở dữ liệu giữa tất cả các phiên bản của lớp cơ sở dữ liệu. Điều này được thực hiện bởi vì việc tạo kết nối cơ sở dữ liệu mất nhiều thời gian, vì vậy tốt nhất là chỉ tạo một và chia sẻ nó (đây được gọi là Mô hình Singleton).

Phương thức tĩnh (tức là Chế độ xem tĩnh công khai :: format_phone_number ($ chữ số)) có thể được sử dụng mà KHÔNG khởi tạo một trong những đối tượng đó (tức là chúng không đề cập đến nội bộ $ this).

Ví dụ về phương thức tĩnh:

public static function prettyName($first_name, $last_name)
{
    echo ucfirst($first_name).' '.ucfirst($last_name);
}

echo Person::prettyName($derek->first_name, $derek->last_name);

Như bạn có thể thấy, hàm tĩnh công khai PrettyName không biết gì về đối tượng. Nó chỉ hoạt động với các tham số bạn truyền vào, giống như một hàm bình thường không phải là một phần của đối tượng. Tại sao phải bận tâm, nếu chúng ta có thể có nó không phải là một phần của đối tượng?

  1. Đầu tiên, gắn các chức năng vào các đối tượng giúp bạn giữ mọi thứ ngăn nắp, để bạn biết nơi tìm chúng.
  2. Thứ hai, nó ngăn chặn xung đột đặt tên. Trong một dự án lớn, bạn có thể có hai nhà phát triển tạo các hàm getName (). Nếu một cái tạo ClassName1 :: getName () và cái kia tạo ClassName2 :: getName (), thì không có vấn đề gì cả. Không có xung đột. Yay phương pháp tĩnh!

TỰF :: Nếu bạn đang mã hóa bên ngoài đối tượng có phương thức tĩnh mà bạn muốn tham chiếu, bạn phải gọi nó bằng tên của đối tượng View :: format_phone_number ($ phone_number); Nếu bạn đang mã hóa bên trong đối tượng có phương thức tĩnh mà bạn muốn tham chiếu, bạn có thể sử dụng tên của đối tượng View :: format_phone_number ($ pn), HOẶC bạn có thể sử dụng phím tắt self :: format_phone_number ($ pn)

Điều tương tự cũng xảy ra với các biến tĩnh: Ví dụ: View :: samples_path so với self :: samples_path

Bên trong lớp DB, nếu chúng ta đề cập đến một phương thức tĩnh của một số đối tượng khác, chúng ta sẽ sử dụng tên của đối tượng: Ví dụ: Session :: getUsersOnline ();

Nhưng nếu lớp DB muốn tham chiếu đến biến tĩnh của chính nó, nó sẽ chỉ tự nói: Ví dụ: self :: Connection;

Hy vọng rằng sẽ giúp mọi thứ rõ ràng :)


Câu trả lời chính xác. Tôi chỉ muốn chỉ ra, khi đề cập đến một thuộc tính tĩnh, bạn cần sử dụng một $dấu hiệu. Ví dụself::$templates_path
henrywright

30

Từ bài đăng trên blog này :

  • self đề cập đến lớp hiện tại
  • self có thể được sử dụng để gọi các hàm tĩnh và tham chiếu các biến thành viên tĩnh
  • self có thể được sử dụng bên trong các hàm tĩnh
  • self cũng có thể tắt hành vi đa hình bằng cách bỏ qua vtable
  • $this đề cập đến đối tượng hiện tại
  • $this có thể được sử dụng để gọi các hàm tĩnh
  • $thiskhông nên được sử dụng để gọi các biến thành viên tĩnh. Sử dụng selfthay thế.
  • $this không thể được sử dụng bên trong các hàm tĩnh

26

Trong PHP, bạn sử dụng từ khóa tự để truy cập các thuộc tính và phương thức tĩnh.

Vấn đề là bạn có thể thay thế $this->method()bằng self::method()bất cứ nơi nào, bất kể method()được tuyên bố là tĩnh hay không. Vậy bạn nên dùng cái nào?

Xem xét mã này:

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

Trong ví dụ này, self::who()sẽ luôn xuất ra 'cha mẹ', trong khi $this->who()sẽ phụ thuộc vào lớp mà đối tượng có.

Bây giờ chúng ta có thể thấy rằng bản thân đề cập đến lớp mà nó được gọi, trong khi $thisđề cập đến lớp của đối tượng hiện tại .

Vì vậy, bạn chỉ nên sử dụng bản thân khi $thiskhông có sẵn hoặc khi bạn không muốn cho phép các lớp con cháu ghi đè lên phương thức hiện tại.


22

Bên trong một định nghĩa lớp, $thisđề cập đến đối tượng hiện tại, trong khi selfđề cập đến lớp hiện tại.

Cần phải tham chiếu đến một phần tử lớp bằng cách sử dụng selfvà tham chiếu đến một phần tử đối tượng bằng cách sử dụng $this.

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

21

Dưới đây là một ví dụ về cách sử dụng chính xác $ this và self cho các biến thành viên không tĩnh và tĩnh:

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

21

Theo http://www.php.net/manual/en/lingu.oop5.static.php thì không có $self. Chỉ có $this, để tham khảo thể hiện hiện tại của lớp (đối tượng) và self, có thể được sử dụng để chỉ các thành viên tĩnh của một lớp. Sự khác biệt giữa một thể hiện đối tượng và một lớp xuất hiện ở đây.


9
Gợi ý: Đọc câu trả lời này khi vấp axit.
a20

16

Tôi tin rằng câu hỏi không phải là liệu bạn có thể gọi thành viên tĩnh của lớp hay không bằng cách gọi ClassName::staticMember. Câu hỏi là sự khác biệt giữa việc sử dụng self::classmember$this->classmember.

Ví dụ: cả hai ví dụ sau đều hoạt động mà không có bất kỳ lỗi nào, cho dù bạn sử dụng self::hay$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;
    }
}

Điều đặc biệt buồn cười là bạn bắt đầu câu trả lời của mình bằng "Tôi tin rằng câu hỏi không phải là liệu bạn có thể gọi thành viên tĩnh của lớp hay không bằng cách gọi ClassName :: staticMember. Câu hỏi là sự khác biệt giữa việc sử dụng self :: classmember và $ this-> classmember" và sau đó bạn tiến hành cho thấy không có sự khác biệt nào cả. Trong thực tế, bạn hiển thị một ví dụ về nơi hai tùy chọn hoạt động giống hệt nhau. -1
Butussy Butkus

Tuy nhiên hữu ích. Phạm vi là về độ phân giải và phần này không rõ ràng trong hướng dẫn sử dụng php. Tôi vẫn thấy nó hữu ích
renoirb 23/03 '

2
Fatal error: Access to undeclared static property: Person::$name in D:\LAMP\www\test.php on line 16
K-Gun

16

self đề cập đến lớp hiện tại (trong đó nó được gọi là),

$thisđề cập đến đối tượng hiện tại. Bạn có thể sử dụng tĩnh thay vì tự. Xem ví dụ:

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

Đầu ra: cha mẹ


16
  • Con trỏ đối tượng $thisđể chỉ đối tượng hiện tại.
  • Giá trị lớp staticđề cập đến đối tượng hiện tại.
  • Giá trị lớp selfđề cập đến lớp chính xác mà nó được định nghĩa trong.
  • Giá trị lớp parentđề cập đến cha mẹ của lớp chính xác mà nó được định nghĩa trong.

Xem ví dụ sau đây cho thấy quá tải.

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

Hầu hết thời gian bạn muốn tham khảo lớp hiện tại đó là lý do tại sao bạn sử dụng statichoặc $this. Tuy nhiên, có những lúc bạn cần self bởi vì bạn muốn lớp gốc bất kể cái gì mở rộng nó. (Rất, rất hiếm khi)


14

Vì không ai ở đây nói về màn trình diễn, đây là một điểm chuẩn nhỏ tôi đã làm (5.6):

 Name     | Time    | Percent  
----------|---------|---------  
 $this->  | 0.99163 | 106.23%  
 self::   | 0.96912 | 103.82%  
 static:: | 0.93348 | 100%

Đó là kết quả cho 2 000 000 lượt chạy và đây là mã tôi đã sử dụng:

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

1
Gọi hàm no-op 2 000 000 lần kéo dài 1 giây. Phải yêu PHP.
rr-

PHP cũ tốt. :) Nhưng một cuộc gọi = 0,001ms. Nó có tệ không?
tleb

Tôi tin rằng điều này (và những thứ tương tự) là lý do tại sao những thứ như ORM cảm thấy chậm trừ khi bạn lưu trữ nội dung và bộ tạo trang tĩnh là một thứ.
rr-

2
Về mặt lý thuyết, nó sẽ mất 1 chu kỳ xung nhịp của bộ xử lý, diễn ra trong khoảng 1 / 2e9 s = 0.5 nsnhững ngày này
Buddy

Chỉ cần đọc lại câu trả lời của tôi. Hãy cẩn thận: nó cũng tạo ra lớp . Tôi không biết tại sao tôi không sử dụng usetừ khóa tbh, nhưng tôi không có PHP nữa để làm lại điểm chuẩn và tôi thực sự không muốn cài đặt lại từ khóa.
tleb

13

Khi selfđược sử dụng với ::toán tử, nó đề cập đến lớp hiện tại, có thể được thực hiện cả trong bối cảnh tĩnh và không tĩnh. $thisđề cập đến chính đối tượng. Ngoài ra, việc sử dụng $thisđể gọi các phương thức tĩnh là hoàn toàn hợp pháp (nhưng không đề cập đến các trường).


8

Tôi gặp phải câu hỏi tương tự và câu trả lời đơn giản là:

  • $this yêu cầu một thể hiện của lớp
  • self:: không

Bất cứ khi nào bạn đang sử dụng các phương thức tĩnh hoặc thuộc tính tĩnh và muốn gọi chúng mà không có đối tượng của lớp được khởi tạo, bạn cần sử dụng self:để gọi chúng, bởi vì $thisluôn luôn yêu cầu đối tượng được tạo.


7

$thisđề cập đến đối tượng lớp hiện tại, selfđề cập đến lớp hiện tại (Không phải đối tượng). Lớp học là bản thiết kế của đối tượng. Vì vậy, bạn định nghĩa một lớp, nhưng bạn xây dựng các đối tượng.

Nói cách khác, sử dụng self for staticthis for none-static members or methods .

cũng trong kịch bản con / cha mẹ self / parentchủ yếu được sử dụng để xác định các thành viên và phương thức của lớp cha và con.


7

Ngoài ra kể từ khi $this::chưa được thảo luận.

Chỉ dành cho mục đích thông tin, kể từ PHP 5.3 khi xử lý các đối tượng được khởi tạo để có được giá trị phạm vi hiện tại, trái với việc sử dụng static::, người ta có thể sử dụng thay thế$this:: như vậy.

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

Sử dụng mã ở trên không phải là thông lệ hoặc được khuyến nghị, nhưng chỉ đơn giản là để minh họa việc sử dụng nó và là hoạt động như một "Bạn có biết?" liên quan đến câu hỏi của người đăng ban đầu.

Nó cũng đại diện cho việc sử dụng $object::CONSTANTví dụ echo $foo::NAME;như trái ngược với$this::NAME;


5

Sử dụng selfnếu bạn muốn gọi một phương thức của một lớp mà không tạo một đối tượng / thể hiện của lớp đó, do đó tiết kiệm RAM (đôi khi sử dụng self cho mục đích đó). Nói cách khác, nó thực sự đang gọi một phương thức tĩnh. Sử dụng thischo phối cảnh đối tượng.


2

Trường hợp 1: Sử dụng self có thể được sử dụng cho các hằng lớp

 lớp classA { 
     const FIXED_NUMBER = 4; 
     tự :: POUNDS_TO_KILOGRAM
}

Nếu bạn muốn gọi nó bên ngoài lớp, hãy sử dụng classA::POUNDS_TO_KILOGRAMSđể truy cập các hằng số

Trường hợp 2: Đối với thuộc tính tĩnh

lớp classC {
     hàm công khai __construct () { 
     tự :: $ _ bộ đếm ++; $ this-> num = self :: $ _ counter;
   }
}

1

Theo php.net có ba từ khóa đặc biệt trong bối cảnh này: self, parentstatic. Chúng được sử dụng để truy cập các thuộc tính hoặc phương thức từ bên trong định nghĩa lớp.

$thismặt khác, được sử dụng để gọi một thể hiện và phương thức của bất kỳ lớp nào miễn là lớp đó có thể truy cập được.


-1

self ::  keyword được sử dụng cho lớp hiện tại và về cơ bản nó được sử dụng để truy cập các thành viên, phương thức và hằng số tĩnh. Nhưng trong trường hợp $ này, bạn không thể gọi thành viên tĩnh, phương thức và hàm.

Bạn có thể sử dụng từ khóa self :: trong một lớp khác và truy cập các thành viên, phương thức và hằng số tĩnh. Khi nó sẽ được mở rộng từ lớp cha và tương tự trong trường hợp $ từ khóa này . Bạn có thể truy cập các thành viên không tĩnh, phương thức và hàm trong một lớp khác khi nó sẽ được mở rộng từ lớp cha.

Mã được đưa ra dưới đây là một ví dụ về self ::$ từ khóa này . Chỉ cần sao chép và dán mã trong tệp mã của bạn và xem đầu ra.

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());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.