Perl的“保佑”究竟做了什么?
我知道在一个类的“new”方法中,在Perl中使用“bless”关键字:
sub new { my $self = bless { }; return $self; }
但究竟是什么“保佑”做这个哈希引用?
一般来说, bless
一个对象与一个类。
package MyClass; my $object = { }; bless $object, "MyClass";
现在,当你在$object
上调用一个方法时,Perl知道哪个包要search该方法。
如果省略第二个参数,就像在你的例子中一样,使用当前的包/类。
为了清楚起见,你的例子可能写成如下:
sub new { my $class = shift; my $self = { }; bless $self, $class; }
编辑:请参阅kixx的一个更详细的一个很好的答案 。
bless
一个包的参考。
引用是什么都没有关系,它可以是一个哈希(最常见的情况下),一个数组(不是很常见),一个标量(通常表示一个内向外的对象 ),一个正则expression式,子程序或TYPEGLOB(参见Damian Conway的“ 面向对象的Perl:概念和编程技术综合指南” ),甚至是对文件或目录句柄(最不常见的情况)的引用。
bless
的效果是,它允许你应用特殊的语法来祝福参考。
例如,如果一个幸运的引用被存储在$obj
(通过bless
与包“Class”相关联),那么$obj->foo(@args)
将调用一个子程序foo
,并将第一个参数作为引用$obj
,其余的参数( @args
)。 子程序应该在“Class”包中定义。 如果程序包“Class”中没有子程序foo
,则将search其他程序包(从包“Class”中的数组@ISA
中获取的)的列表,并find第一个find的子程序foo
。
简短版本:它将hash标记为附加到当前包命名空间(以便该包提供它的类实现)。
这个函数告诉REF引用的实体,它现在是CLASSNAME包中的一个对象,或者是CLASSNAME被省略的当前包。 build议使用保佑的双参数forms。
例如 :
bless REF, CLASSNAME bless REF
返回值
这个函数返回对保存到CLASSNAME中的对象的引用。
例如 :
以下是显示其基本用法的示例代码,对象引用是通过祝福对包的类的引用而创build的 –
#!/usr/bin/perl package Person; sub new { my $class = shift; my $self = { _firstName => shift, _lastName => shift, _ssn => shift, }; # Print all the values just for clarification. print "First Name is $self->{_firstName}\n"; print "Last Name is $self->{_lastName}\n"; print "SSN is $self->{_ssn}\n"; bless $self, $class; return $self; }
我遵循这个思想来指导开发面向对象的Perl。
保佑将任何数据结构引用与一个类相关联。 鉴于Perl如何创buildinheritance结构(在某种树中),很容易利用对象模型来创build用于合成的对象。
对于这个关联我们称之为对象,开发时总是要记住对象的内部状态和类行为是分开的。 你可以祝福/允许任何数据引用使用任何包/类行为。 由于包装可以理解对象的“情绪”状态。
我会在这里提供一个答案,因为这里的人没有完全点击我。
Perl的保佑function将任何对包内所有function的引用关联起来。
为什么我们需要这个?
我们首先在JavaScript中expression一个例子:
(() => { 'use strict'; class Animal { constructor(args) { this.name = args.name; this.sound = args.sound; } } /* [WRONG] (global scope corruption) * var animal = Animal({ * 'name': 'Jeff', * 'sound': 'bark' * }); * console.log(animal.name + ', ' + animal.sound); // seems good * console.log(window.name); // my window's name is Jeff? */ // new is important! var animal = new Animal( 'name': 'Jeff', 'sound': 'bark' ); console.log(animal.name + ', ' + animal.sound); // still fine. console.log(window.name); // undefined })();
现在,让我们剥离这个类的构造,并在没有它的情况下做:
(() => { 'use strict'; var Animal = function(args) { this.name = args.name; this.sound = args.sound; return this; // implicit context hashmap }; // the "new" causes the Animal to be unbound from global context, and // rebinds it to an empty hash map before being constructed. The state is // now bound to animal, not the global scope. var animal = new Animal({ 'name': 'Jeff', 'sound': 'bark' }); console.log(animal.sound); })();
该函数采用无序属性的哈希表(因为2016年在dynamic语言中必须以特定的顺序编写属性),并返回包含这些属性的哈希表,或者如果您忘记了新的关键字,将返回整个全局上下文(例如,浏览器中的窗口或nodejs中的全局)。
Perl没有“this”,“new”和“class”,但它仍然可以有一个类似的function。 我们不会有一个构造函数,也不会有一个原型,但是我们可以随意创build新的动物并修改它们的属性。
# self contained scope (sub { my $Animal = (sub { return { 'name' => $_[0]{'name'}, 'sound' => $_[0]{'sound'} }; }); my $animal = $Animal->({ 'name' => 'Jeff', 'sound' => 'bark' }); print $animal->{sound}; })->();
现在,我们有一个问题:如果我们希望动物自己发出声音,而不是打印出他们的声音怎么办? 也就是说,我们需要一个functionperformSound来打印动物自己的声音。
要做到这一点的一个方法是通过教导每个动物如何做到这一点。 这意味着每个Cat都有自己的重复函数来执行声音。
# self contained scope (sub { my $Animal = (sub { $name = $_[0]{'name'}; $sound = $_[0]{'sound'}; return { 'name' => $name, 'sound' => $sound, 'performSound' => sub { print $sound . "\n"; } }; }); my $animal = $Animal->({ 'name' => 'Jeff', 'sound' => 'bark' }); $animal->{'performSound'}(); })->();
这是不好的,因为每次构build动物时,performSound都被作为一个全新的函数对象。 10000只动物表示10000个表演。 我们想要一个单一的函数performSound,被所有的动物使用,查看他们自己的声音并打印它。
(() => { 'use strict'; /* a function that creates an Animal constructor which can be used to create animals */ var Animal = (() => { /* function is important, as fat arrow does not have "this" and will not be bound to Animal. */ var InnerAnimal = function(args) { this.name = args.name; this.sound = args.sound; }; /* defined once and all animals use the same single function call */ InnerAnimal.prototype.performSound = function() { console.log(this.name); }; return InnerAnimal; })(); /* we're gonna create an animal with arguments in different order because we want to be edgy. */ var animal = new Animal({ 'sound': 'bark', 'name': 'Jeff' }); animal.performSound(); // Jeff })();
这里是与Perl类似的地方停止的地方。
JavaScript的新操作符不是可选的,没有它,“this”内部的对象方法会破坏全局范围:
(() => { // 'use strict'; // uncommenting this prevents corruption and raises an error instead. var Person = function() { this.name = "Sam"; }; // var wrong = Person(); // oops! we have overwritten window.name or global.main. // console.log(window.name); // my window's name is Sam? var correct = new Person; // person's name is actually stored in the person now. })();
我们希望每个动物都有一个function来查看动物自己的声音,而不是在施工时对其进行硬编码。
祝福让我们使用一个包作为对象的原型。 通过这种方式,对象知道被“引用”的“包”,进而可以使包中的函数“进入”从该“包对象”的构造函数创build的特定实例中:
package Animal; sub new { my $packageRef = $_[0]; my $name = $_[1]->{'name'}; my $sound = $_[1]->{'sound'}; my $this = { 'name' => $name, 'sound' => $sound }; bless($this, $packageRef); return $this; } # all animals use the same performSound to look up their sound. sub performSound { my $this = shift; my $sound = $this->{'sound'}; print $sound . "\n"; } package main; my $animal = Animal->new({ 'name' => 'Cat', 'sound' => 'meow' }); $animal->performSound();
摘要/ TL; DR :
Perl没有“this”,“class”,也没有“new”。 祝福包的对象给这个对象一个包的引用,当它调用包中的函数时,它们的参数将被偏移1个槽,而第一个参数($ _ [0]或shift)相当于JavaScript的“这个”。 反过来,你可以在一定程度上模拟JavaScript的原型模型。
不幸的是,它使得在运行时创build“新类”是不可能的(因为你需要每个“类”有它自己的包),而在JavaScript中,你根本不需要包,作为“新”关键字构成一个匿名hashmap,供您在运行时用作包,您可以在其中添加新function并删除function。
有一些Perl库创build他们自己的方式来弥补expression的限制,如穆斯。
为什么混乱? :
因为包。 我们的直觉告诉我们将对象绑定到包含其原型的哈希映射。 这让我们可以像JavaScript一样在运行时创build“包”。 Perl没有这样的灵活性(至less不是内置的,你必须创造它或从其他模块中获得),反过来,你的运行时performance力受到阻碍。 称之为“祝福”并不会有多大好处。
我们想要做什么 :
像这样的东西,但绑定到原型地图recursion,并隐式绑定到原型,而不是明确地做到这一点。
这是一个天真的尝试:问题是“调用”不知道“是什么调用它”,所以它可能是一个普遍的Perl函数“objectInvokeMethod(object,method)”,它检查对象是否有方法,或者它的原型有它,或者它的原型有它,直到它到达最后并且find它(原型inheritance)。 Perl有很好的eval魔术,但是我会留下一些我可以稍后尝试的东西。
无论如何这里是这个想法:
(sub { my $Animal = (sub { my $AnimalPrototype = { 'performSound' => sub { return $_[0]->{'sound'}; } }; my $call = sub { my $this = $_[0]; my $proc = $_[1]; if (exists $this->{$proc}) { return $this->{$proc}->(); } else { return $this->{prototype}->{$proc}->($this, $proc); } }; return sub { my $name = $_[0]->{name}; my $sound = $_[0]->{sound}; my $this = { 'this' => $this, 'name' => $name, 'sound' => $sound, 'prototype' => $AnimalPrototype, 'call' => $call }; }; })->(); my $animal = $Animal->({ 'name' => 'Jeff', 'sound'=> 'bark' }); print($animal->{call}($animal, 'performSound')); })->();
无论如何,希望有人会发现这个职位有用。
例如,如果您可以确信任何Bug对象将是一个有福的散列,您可以(最后!)在Bug :: print_me方法中填写缺less的代码:
package Bug; sub print_me { my ($self) = @_; print "ID: $self->{id}\n"; print "$self->{descr}\n"; print "(Note: problem is fatal)\n" if $self->{type} eq "fatal"; }
现在,无论何时print_me方法通过引用已被祝福到Bug类中的任何散列来调用,$ selfvariables将提取作为第一个parameter passing的引用,然后打印语句访问散列散列的各种条目。