bless将引用与包关联。 引用的对象并不重要,它可以是散列(最常见的情况)、数组(不太常见)、标量(通常表示由内而外的对象)、正则表达式、子例程或TYPEGLOB(参见Object Oriented Perl: A Comprehensive Guide to Concepts and Programming Techniques by Damian Conway一书中的有用示例),甚至是文件或目录句柄的引用(最不常见的情况)。 bless-ing的作用是,它允许您将特殊的语法应用于受祝福的引用。 例如,如果一个受祝福的引用存储在$obj中(通过bless与包“Class”关联),则$obj->foo(@args)将调用子例程foo,并传递引用$obj作为第一个自变量,后面是其余自变量(@args)。子例程应该在包“Class”中定义。如果包“Class”中没有子例程foo,将搜索其他包的列表(取自包“Class”中的数组@ISA),并且将调用找到的第一个子例程foo。
#!/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;
}
(() => {
//'use strict'; // uncomment to fix the bug mentioned below.
class Animal {
constructor(args) {
console.log(this);
this.name = args.name;
this.sound = args.sound;
}
}
/* This is left for historical reasons,
* modern JavaScript engines no longer allow you to
* call class constructors without using new.
*
* 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?
*/
// as of recently, Animal constructor cannot be called without using the new operator.
var animal = new Animal({
'name': 'Jeff',
'sound': 'bark'
});
console.log(animal.name + ', ' + animal.sound); // still fine.
console.log("window's name: " + window.name); // undefined
})();
现在让我们看看这个类构造的去糖版本:
(() => {
// 'use strict'; // uncomment to fix bug.
var Animal = function(args) {
this.name = args.name;
this.sound = args.sound;
return this; // implicit context hashmap
};
/**
* The bug left for historical reasons,
* should still work now in modern web developer consoles.
*
* var animal = Animal({
* 'name': 'Jeff',
* 'sound': 'Bark'
* });
* console.log(animal.name + ', ' + animal.sound); // seems good
* console.log("The window's name is: " + window.name); // my window's name is Jeff?
*/
// the new operator causes the "this" inside methods to refer to the animal
// rather than the global scope, so the bug mentioned above does not occur.
var animal = new Animal({
'name': 'Jeff',
'sound': 'bark'
});
console.log(animal.sound);
console.log(window.name); // the name has not been changed by the constructor.
})();
(() => {
'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;
})();
var animal = new Animal({
'sound': 'bark',
'name': 'Jeff'
});
animal.performSound(); // Jeff
})();
(() => {
// uncommenting this prevents unintentional
// contamination of the global scope, and throws a TypeError instead.
// 'use strict';
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.
})();
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'
});
print("The animal's ref is: " . ref($animal) . "\n");
$animal->performSound();
#!/usr/bin/perl
use warnings;
use strict;
print('Enter the name for the class(eg Greeter): $ ');
my $class_name = <>;
chomp $class_name;
print('Enter the name of the method(eg greet): $ ');
my $method_name = <>;
chomp $method_name;
no strict 'refs';
# The line below violates strict 'refs'.
*{$class_name . '::new'} = sub {
my $self = bless {}, $_[0];
return $self;
};
use strict 'refs';
no strict 'refs';
# The line below violates strict 'refs'
*{$class_name . '::' . $method_name} = sub {
print("Hello, World!\n");
};
use strict 'refs';
my $instance = ($class_name)->new();
$instance->$method_name();
# | Illustrates catching 3 distinct ways of calling a package's member.
package Test;
sub new {
return bless {}, __PACKAGE__;
}
sub runTest {
if (ref($_[0]) eq __PACKAGE__) {
# | $_[0] is the blessed reference.
# | Despite being called with "->", $_[1] is NOT "Test"(__PACKAGE__).
print("Test::runTest was called through a blessed reference call(\$instance->runTest().\n");
} elsif ($_[0] eq __PACKAGE__) {
# | $_[0] is "Test"(__PACKAGE__), but we can't determine for sure whether it was -> or ::.
print("Test::runTest was called through Test->runTest() or through Test::runTest() with 'Test' as the first argument.\n");
} else {
# | $_[0] is neither a blessed reference nor "Test"(__PACKAGE__), so it can't be an arrow call.
print "Test::runTest was called through Test::runTest()\n";
}
}
package main;
my $test = Test->new();
$test->runTest();
Test->runTest();
Test::runTest();
Test::runTest('Test'); # <- Same as "Test->runTest();"
Test::runTest($test); # <- Same as "$test->runTest();"
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";
}
8条答案
按热度按时间piah890a1#
通常,
bless
将对象与类关联。现在,当您在
$object
上调用一个方法时,Perl知道要在哪个包中搜索该方法。如果省略了第二个参数(如示例中所示),则使用当前包/类。
为了清楚起见,您的示例可能编写如下:
编辑:请参阅kixx的好答案,了解更多细节。
baubqpgj2#
bless
将引用与包关联。引用的对象并不重要,它可以是散列(最常见的情况)、数组(不太常见)、标量(通常表示由内而外的对象)、正则表达式、子例程或TYPEGLOB(参见Object Oriented Perl: A Comprehensive Guide to Concepts and Programming Techniques by Damian Conway一书中的有用示例),甚至是文件或目录句柄的引用(最不常见的情况)。
bless
-ing的作用是,它允许您将特殊的语法应用于受祝福的引用。例如,如果一个受祝福的引用存储在
$obj
中(通过bless
与包“Class”关联),则$obj->foo(@args)
将调用子例程foo
,并传递引用$obj
作为第一个自变量,后面是其余自变量(@args
)。子例程应该在包“Class”中定义。如果包“Class”中没有子例程foo
,将搜索其他包的列表(取自包“Class”中的数组@ISA
),并且将调用找到的第一个子例程foo
。f5emj3cl3#
简短版本:它将该哈希值标记为附加到当前包名称空间(以便该包提供其类实现)。
frebpwbc4#
这个函数告诉REF引用的实体,它现在是CLASSNAME包中的一个对象,如果省略了CLASSNAME,则是当前包中的一个对象。建议使用双参数形式的bless。
示例:
传回值
此函数返回对已分配到CLASSNAME中的对象的引用。
示例:
下面的示例代码显示了它的基本用法,对象引用是通过将一个引用赋给包的类来创建的−
pcww981p5#
在内部明确区分
bless
-艾德referent的是,用于引用的SV
(存储在标量中)获取一个额外的FLAGS
值(OBJECT
),以及一个携带包名的STASH
(还有一些其他区别)打印,抑制相同(与此无关)的部分
这样,解释器就知道
并且这告知其用途。
例如,当遇到对该变量的解引用(
$obj->name
)时,在包(或层次结构)中查找具有该名称的子对象,将该对象作为第一个参数传递,等等。vuv7lop36#
我将尝试在这里提供一个答案,因为在我最初写这篇文章的时候,这里的答案并不适合我(警告,这个答案的结构相当糟糕,请随意跳过对你不是特别有用的部分)。
Perl的bless函数将指定的引用与包名字符串相关联,并使blessed引用的箭头操作符在与该引用相关联的包中查找方法,如果没有找到,则使用
@ISA
数组(如果有)继续查找(这超出了本文的范围)。我们为什么需要这个?
让我们开始用JavaScript表达一个示例:
现在让我们看看这个类构造的去糖版本:
Animal的构造函数接受一个Object的属性,并返回一个具有这些属性的Animal,或者,如果您忘记输入new关键字,它将返回整个全局上下文(即浏览器开发者控制台中的
window
)。Perl没有“this”、“new”或“class”,但它仍然可以有一个行为类似的函数。我们不会有构造函数或原型,但我们将能够创建新的动物并修改它们的个体属性。
现在,我们有一个问题:如果我们希望动物自己发出声音,而不是直接打印它们的声音,也就是说,我们需要一个函数performSound来打印动物自己的声音。
一种方法是为Animal的每个示例提供其自己的performSound子例程引用。
这通常不是我们想要的,因为performSound是作为一个全新的子例程引用给每一个被构造的动物的。构造10000个动物可能会分配10000个performSound子例程。我们希望有一个子例程performSound供所有Animal示例使用,这些示例查找自己的声音并打印出来。
这里是与Perl相似的地方。
JavaScript的new运算符不是可选的,如果没有它,对象方法内部的“this”会污染全局作用域:
我们希望每个Animal都有一个函数,用于查找该动物自己的声音,而不是在构造时硬编码。
祝福让我们可以使用包的子例程,而不必为每个对象附加一个子例程ref,它还使
ref
引用更有意义的包名(如Animal
)作为对象的名称,而不是无聊的HASH
或任何其他你选择祝福的引用对象:摘要/TL; DR:
1.将对象祝福给封装,可让该对象指涉封装。
1.使用箭头操作符来调用一个受祝福的引用对象(
$blessedObject->method(...arguments)
)的方法通常与调用Package::method($blessedObject, ...arguments)
相同,但是如果没有找到方法,它将继续使用包的@ISA
来查找,这超出了本文的范围。1.实际上,您可以在运行时创建新类,只要您违反严格的'refs'或使用eval,下面是如何实现的演示:
为什么这么混乱?
bless之所以令人困惑的一个原因是,实际上有三种调用包的方法
1.通过
A::a()
,作为包子程序。1.通过
A->a()
,作为一个包子例程,但它会将__PACKAGE__
作为第一个参数在其他参数之前隐式传递。1.通过
$a->a()
与$a
的祝福变成A
。下面的代码对此进行了说明:
另一个原因是,与JavaScript不同,Python可以有多个具有不同名称但定义/方法/属性不同的类,Perl类有唯一的(
ref $obj
),因为在任何给定时间只能有一个具有特定名称的包,并且@伊萨需要一点时间来适应。我的最后一个原因是,包不如其他脚本语言中的类那样有形,在其他脚本语言中,您可以通过赋值运算符将对类本身的引用存储在变量中,而在Perl中,您不仅不能存储对类的引用(您只能通过名称字符串引用软件包),但是试图通过一个存储在变量中的名字(例如String[$method])来引用一个包,而不违反严格的'refs'或使用eval似乎是不可能的。
无论如何,希望有人会发现这个职位有用。
注:这是一个相当老的尝试在一个答案,我已经试图清理数量的天真和尴尬/无意义/分散注意力的陈述,并添加更多有用的例子,以帮助理解这个概念,但它仍然是远离我希望它是(它仍然是相当令人沮丧的重读)。我离开它,因为我仍然相信它可能是有用的人。
请把它与盐增益,我道歉的任何头痛所呼吁的布局不佳的结构的答案。
e37o9pze7#
我遵循这一思想来指导面向对象Perl的开发。
最好将任何数据结构引用与类相关联。考虑到Perl如何创建继承结构(在一种树中),很容易利用对象模型来创建用于组合的对象。
对于这个我们称为对象的关联,要开发时始终牢记对象的内部状态和类的行为是分离的.并且你可以祝福/允许任何数据引用使用任何包/类的行为.因为包可以理解对象的“情绪”状态.
tcomlyy68#
例如,如果你确信任何Bug对象都将是一个幸运的散列,你可以(终于!)在Bug::print_me方法中填充缺失的代码:
现在,每当print_me方法被调用时,通过引用任何一个被祝福到Bug类中的哈希值,$self变量提取作为第一个参数传递的引用,然后print语句访问被祝福的哈希值的各个条目。