後期靜態綁定也就是 static::
在類中的用法,首先須要明確一點,static::
後可跟 類常量 、靜態屬性(類變量)、靜態方法 和 普通方法(跟 類常量/靜態屬性/靜態方法 時 static
表明一個類,跟 普通方法時 static
表明一個對象),不能夠跟普通屬性(對象的成員變量),由於語義上沒法辨別是普通屬性仍是靜態屬性(語法都是 static::$xx)。this
引入後期靜態綁定的目的是爲了打破 self::
關鍵詞的限制,類中 self
的指向在編譯時期便肯定在了代碼上下文中 self
所在的類。而 static::
則是在運行時肯定其指向。在繼承關係中,即可看出 self
和 static
的區別。code
class A { public const FOO = 'a'; public function test() { echo self::FOO; // self 明確指向代碼上下文中所在類 A,即便被子類調用,self 也是指向 A } } class B extends A { public const FOO = 'b'; } (new B)->test(); // 輸出 a
class A { public const FOO = 'a'; public function test() { echo static::FOO; // static 在編譯階段不肯定指向,運行時才肯定指向,指向調用者,故稱爲 `後期綁定`。此處 static 後跟類常量,故 static 表明調用類,也就是 B 類。 } } class B extends A { public const FOO = 'b'; } (new B)->test(); // 輸出 b
class A { public static $foo = 'a'; public static function test() { echo self::$foo; } } class B extends A { public static $foo = 'b'; } B::test(); // 輸出 a
class A { public static $foo = 'a'; public static function test() { echo static::$foo; } } class B extends A { public static $foo = 'b'; } B::test(); // 輸出 b
class A { public static function who() { echo __CLASS__; // __CLASS__ 編譯時期綁定了 A 類名 } public static function test() { self::who(); // self 在編譯時期就指向了 A 類 } } class B extends A { public static function who() { echo __CLASS__; // __CLASS__ 編譯時期綁定了 B 類名 } } B::test(); // A
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(); // B
除了代替 self::
,static::
在一些場景中也可代替 $this->
。
有種場景是這樣的:當父類中的方法被子類調用時,方法中的 $this-> 會首先在其所在範圍(代碼上下文,即父類)裏尋找私有方法或屬性,若是尋找不到纔會調用其子類的方法或屬性。而 static 屬於後期綁定,可繞過這一步,直接指向方法的調用者。對象
class A { private function foo() { echo "a\n"; } public function test() { $this->foo(); // 所在範圍中存在私有方法 foo,因此並不會調用 B 中的 foo 方法 } } class B extends A { public function foo() { echo "b\n"; } } (new B)->test(); // 輸出 a
class A { private function foo() { echo "a\n"; } public function test() { static::foo(); // 繞過檢查,直接調用調用者的該方法 } } class B extends A { public function foo() { echo "b\n"; } } (new B)->test(); // 輸出 b