Javascript 函数作用域和查找父对象的属性
Javascript function scope and finding properties of a parent object
我试图实现的是创建一些在JavaScript中对对象属性进行操作的函数,这是相当典型的做法。
问题是,为了灵活性和可扩展性,我不希望函数提前知道对象。据我所知,这正是Javascript应该相当不错的事情,但是我没有花很多时间在函数式语言上,我还没有掌握如何做到这一点。
所以我可能会遇到这样的情况:
function Owner()
{
var self=this;
self.size = 100;
self.writers = [];
}
function Writers()
{
var Alice= function()
{
var that=this;
that.name="alice";
that.operate = function()
{
console.log( "Alice says: "+self.size );
}
}
this.alice=new Alice();
}
var owner = new Owner();
var writers = new Writers();
owner.writers.push( writers.alice );
owner.writers[0].operate();
现在这返回Alice Says: undefined
一切都很好,但并不完全是我想要的 - 我显然想看到Alice Says: 100
.在经典语言中,我可能不得不将 Alice 函数交给所有者(在伪 C# 中(:
public class Owner()
{
private List<Writer> writers;
public Owner()
{
this.size=100;
this.writers= List<Writer>();
}
public int Size{ get; set; }
public void AddWriter( Writer writer )
{
writer.Owner = this;
this.writers.Add(writer);
}
}
public class Writer
{
private Owner owner;
private string name;
public void Writer( string name )
{
this.name=name;
}
public void operate()
{
Console.WriteLine( "{0} says: {1}", name, owner.Size );
}
}
据我所知,这不是很Javascripty-似乎应该有一种更好 - 或者至少更惯用 - 的方式来做到这一点。
就我正在使用的代码的实际目标而言(基本上是这样的,但更复杂(,目标是能够添加任意数量的"Writer"类,这些类可以在它们附加到的"所有者"实例上执行操作。这基本上是Decorator
模式的实现,我可以想到很多可以实现它的方法,但正如我所说,我正在寻找了解 Javascript 的功能/原型性质如何在这种情况下帮助我,以便我可以更好地处理语言。
我对如何通过实例传递函数也不完全满意 - 我觉得我应该能够像五彩纸屑一样将函数扔到各个地方,所以任何关于如何与可变范围交互的提示都会非常有帮助。
如果事实证明我试图做的是向后倒退,并且有更好的方法来实现这种类型的目标,那也没关系。我正在尝试在Javascript中获得好的语言,事实证明这很棘手,但我学到了很多东西。
编辑:我的目标是拥有一个基本类型,当我设计它时,我不必知道它可能必须执行的完整操作列表,并且我不必为每个不同的可用操作组合创建新的子类,确保我的操作与我的核心类型分离。
因此,如果我有一个Animal
类型,然后我的操作是Eat
、Run
、Grow
等等,理想情况下我会有一个myAnimal.can("Eat")
类型的调用(我意识到这有点像hasOwnProperty
但我假设这些操作是特定的基本类型(,它会通知我该操作是否可用,然后是myAnimal.actions.Eat("leaves")
是否可以访问。调用Eat
操作时,它将影响父对象myAnimal
对象的属性。在myAnimal
的生命周期中,它可能随时添加或删除能力,但只要检查它是否can
做一些足以满足我需求的事情。
实现它的一种方法是使用javascript闭包:
function Owner() {
var $self = {};
$self.size = 150;
$self.writers = [];
function Writers() {
var Alice = function () {
var that = this;
that.name = "alice";
that.operate = function () {
console.log("Alice says: " + $self.size);
console.log($self);
}
}
this.alice = new Alice();
}
var getO = function () {
var writers = new Writers();
$self.writers.push(writers.alice);
$self.writers[0].operate();
$self.writers[0].operate();
}
return getO
}
var o = Owner();
o();
这是小提琴。
闭包基本上创建了一个环境,其中"内部-外部"函数和变量可以从已返回的函数的"内部"访问:
这些是"内-外"函数和变量:
var $self = {};
$self.size = 150;
$self.writers = [];
function Writers() {
var Alice = function () {
var that = this;
that.name = "alice";
that.operate = function () {
console.log("Alice says: " + $self.size);
console.log($self);
}
}
this.alice = new Alice();
}
getO内部是"内部内部":
var getO = function () {
//Here are the functions you want to call:
var writers = new Writers();
$self.writers.push(writers.alice);
$self.writers[0].operate();
$self.writers[0].operate();
}
返回getO,我们已经创建了一个闭包!
从现在开始,当你调用realowner();
你会喜欢调用getO();
,getO()
能够访问"内部-外部"变量和函数(例如 $self
(。
在这一行中:
console.log( "Alice says: "+self.size );
self
不是你想要的,因为你对self
的定义不在范围内,因此你得到的是self
的全局定义,这是window
的,不会做你想要的。
设计中的问题是Writers
对象不知道Owner
对象的任何信息,因此不能有报告Owner
数组大小的方法。
你已经使你的代码比它需要的复杂得多,that
和self
不必要的定义,并且不清楚为什么一个Writers
对象应该知道它的容器。 如果您希望如此,则应将其容器传递给Writers
对象的构造函数,并且可以存储对其容器的引用。
如果你更好地描述你真正想要解决的问题,我们可能会想出一个非常简单、更恰当的面向对象的建议。
在 Javascript 中,您仍然需要记住将数据封装到对象中的概念,以及那些具有可以对可以访问的数据和属性进行操作的方法的对象。 如果要对对象中的数据进行操作(如在 .size
属性中(,则必须具有对包含该属性的对象的引用。
对对象(如Owner
对象(的引用可以来自多个位置:
- 它可以作为实例数据存储在某个其他对象中,因此可以从该其他对象方法根据需要使用(例如,在每个
Writers
对象中存储对所有者的引用。 - 它可以通过作用域使用(例如,在当前函数或嵌套的任何父函数中定义(。 在所有者构造函数的作用域内定义 Writers 对象。 这将使您能够访问 Owner 实例,但也会使 Writers 对象仅在 Owners 方法中可用(不公开可用(。
- 它可以作为参数传递给想要使用它的方法/函数。 可以将相应的所有者作为参数传递给
.operate(curOwner)
方法。 - 它可以在全球范围内存储,因此可以在任何地方使用。 如果一次只有一个所有者(例如,它是单例设计模式(,则可以全局存储所有者并全局访问。 这不太灵活(将来不能将多个所有者用于其他目的(,但可以更简单。
代码中缺少的是Writers
对象的.operate()
方法到达相应Owner
对象的任何方法。 它根本无法从.operate()
代码中获取。 如果您希望它在那里可用,则必须更改代码的结构以至少使用上述访问方法之一,或者发明一种访问 Owner 实例的新方法。
根据描述问题的最新编辑,下面是更多信息。 你可以利用javascript的松散类型的方法之一是你可以有一个对象数组,这些对象可以是你想要的任何类型。 只要它们都有一组通用的方法,如.eat()
、.grow()
、.run()
,即使它们是完全不同的对象类型,你也可以把对象都一视同仁(对它们调用这些方法(。 您不必创建一个公共基类,然后从中创建一个子类。 这不是很javascripty,也不是必需的。 只需创建三个不同的对象,为每个对象提供所需的方法集,将它们放入数组中,然后根据需要调用这些方法。 您甚至可以在调用对象之前测试对象是否具有特定方法,如果它们可能不都具有相同的方法(尽管这是一个不太完美的OO设计 - 尽管有时比其他设计更实用(。
例如,您可以拥有以下内容:
// define the cat object
function cat(name) {
this.name = name;
}
cat.prototype = {
eat: function(howMuch) {
// implementation of eat here
},
run: function(howFar) {
// implementation of run here
},
grow: function(years) {
// implementation of grow here
}
};
// define the dog object
function dog(name) {
this.name = name;
}
dog.prototype = {
eat: function(howMuch) {
// implementation of eat here
},
run: function(howFar) {
// implementation of run here
},
grow: function(years) {
// implementation of grow here
}
};
然后,您可以拥有一系列猫和狗:
var animals = [];
animals.push(new cat("fluffy"));
animals.push(new dog("bowser"));
animals.push(new cat("purr"));
然后,您可以拥有对 animals
数组的内容进行操作的代码,而不考虑它是哪种动物。 因此,要让每只动物跑 30 英尺,您可以这样做:
for (var i = 0; i < animals.length; i++) {
animals[i].run(30);
}
现在,你可以创建一个基类,它有一个默认的构造函数来保存名称,并定义不做任何抽象方法来吃、运行和增长(以C++或 C# 风格(,但这在 JS 中通常不是真正必要的,因为你不必知道任何对象的类型来使用它,只要它们都有一些你知道如何调用的通用方法集。
我把你的伪C#代码翻译成javascript。
在翻译时,我修改了一些命名约定以通用的js标准。(仅类的前导大写,私有成员的下划线(。我使用属性和闭包来更接近您的类描述。
我还修改了一些签名:
- 我猜名称和大小是只读的。
- 作家必须了解其所有者。
- 所有者可能希望让所有作家进行操作。(只是一个猜测(。
希望这将引导您实现目标。
小提琴在这里:http://jsfiddle.net/gamealchemist/zPu8C/3/
function Owner() {
var _writers = [];
Object.defineProperty(this, 'size', {
get: function () {
return _writers.length
},
enumerable: true
});
this.addWriter = function (writer) {
_writers.push(writer);
}
this.operateAll = function () {
_writers.forEach(function (w) {
w.operate();
});
};
}
function Writer(owner, name) {
var _owner = owner;
var _name = name;
Object.defineProperty(this, 'name', {
get: function () {
return _name;
},
enumerable: true
});
this.operate = function () {
console.log(this.name + " sees " + _owner.size + " people");
}
_owner.addWriter(this);
}
var wonderlandOwner = new Owner();
var alice = new Writer(wonderlandOwner, 'Alice');
var rabbit = new Writer(wonderlandOwner, 'Rabbit');
var madHatter = new Writer(wonderlandOwner, 'Mad Hatter');
wonderlandOwner.operateAll();
// outuput is :
// Alice sees 3 people
// Rabbit sees 3 people
// Mad Hatter sees 3 people
madHatter.operate();
// output is :
// Mad Hatter sees 3 people
- 如何从对象的原型方法访问JavaScript对象属性
- 如何将数组项添加到对象属性中
- 设置嵌套对象属性的更好方法
- JavaScript管理具有重复属性名称的对象属性
- 如何使用element.myobj.prop等具有对象属性的元素
- 如何使用(this)访问Angular 2 http rxjs catch函数中的对象属性
- Es6:能够在设置/更新/删除对象属性时调用自定义方法
- 如何在AngularJS工厂中正确声明对象属性
- 如何使用object.assign()从其他对象引用基本对象属性
- 使用XPath样式访问Javascript JSON对象属性
- 将javascript对象(属性+值)合并到一个对象中
- 数组:使对象属性成为数组键
- 无法从JavaScript中的函数调用对象属性
- Google Closure Advanced |无法识别对象属性|动态属性
- Javascript从匿名函数访问外部对象属性
- 从函数更改对象属性
- 如何从字符串变量访问对象属性
- 从嵌套对象属性中获取排除某个值的最高值
- 在Aurelia computeds中,当设置依赖关系时,如何声明对对象属性的依赖关系
- 传递数量不确定的可能嵌套的对象属性