對大多數 PHPer 來說,self 與 static 兩個 PHP 關鍵字不算陌生。我們學會透過self::xxxx
這種方式來呼叫目前類別的靜態屬性和方法。而 static 呢?想必很多人只知道它是用來定義一個靜態方法和類別屬性關鍵字。
這也是我之前的認知。
現在我們來回顧這兩個關鍵字的一些常見用法:
// self 用法 1 :调用静态成员属性 <?php class Person { protected static $maxAddressCount = 5; // 收获地址创建最大数量。 public function test() { echo self::$maxAddressCount; } } $person = new Person(); $person->test();
// self 用法 2 :调用静态方法 <?php class Person { protected static $maxAddressCount = 5; // 收获地址创建最大数量。 protected static function getMaxAddressCount() { return self::$maxAddressCount; } public function test() { echo self::getMaxAddressCount(); } } $person = new Person(); $person->test();
// self 用法 3 :创建一个当前对象 <?php // 单例示例 class Person { private static $instance = null; private function __construct() {} final public static function getInstance() { if (self::$instance == null) { self::$instance = new self; } return self::$instance; } public function test() { echo "hello world!"; } } $person = Person::getInstance(); $person->test();
關於 static 關鍵字的常見用法也在上面 3 個範例中得到綜合體現。
我深信上面的用法,任何一個入門的 PHPer 都是很熟悉的。現在我要講的是以下兩種方式:
new self() 与 new static() 的区别?
我相信很多人都知道new self()
創建一個當前類別的對象,不知道new static( )
也能建立一個目前類別的物件。
關於new static()
這種用法呢,在官方文件有說明。網址:https://www.php.net/manual/zh/language.oop5.late-static-bindings.php
PHP 官方稱這種方式為:後期靜態綁定。
官方範例 1:
<?php class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test();
因為 Class B 繼承了 Class A。 A 與 B 都有靜態方法who()
。此時經過B::test()
的時候,呼叫的實際上是 Class A 的who()
方法。
因為子類別 Class B 的靜態方法who()
屬於在 Class A 之後的子類別裡面才定義的。而 PHP 的預設特性只允許呼叫最先定義的。
就這引出了後期靜態綁定的概念。
官方範例2:
<?php class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); // 后期静态绑定从这里开始 } } class B extends A { public static function who() { echo __CLASS__; } } B::test();
我們把Class A 裡面的test()
方法體的self
改為static
之後,static 代表的永遠是指向呼叫類別。也就是說雖然在 Class A 父類別裡面定義的方法與子類別有同名衝突的情況。但是,當子類別呼叫的時候,那麼自動切換到子類別的靜態同名方法。取決於調用者。
大家可以透過運行以上兩個範例來理解。
之所以會有本小節內容。是因為我在實際運行當中要繼承單例方法導致了這個問題。所以,才牽扯出這個特性。