gpt4 book ai didi

javascript - 我的对象不是通过引用更新的,逻辑有什么问题?

转载 作者:行者123 更新时间:2023-12-01 03:36:34 25 4
gpt4 key购买 nike

我已经使用此错误几天了,我认为我已经查明了问题所在,但是我不确定为什么它不起作用。我认为这可能与a problem with passing an object by reference有关,但是如果是这种情况,我不确定如何将该解决方案应用于我的情况。

基本上,我正在(作为学习经验)致力于自己的依赖关系注入(inject)的实现(尽管有人告诉我我的结构实际上称为AMD,但我将继续使用“DI”,直到我进一步了解其区别)。因此,我将简要解释我的代码,然后重点介绍有问题的部分。

语法:

这就是我的代码应该做的,这只是非常非常简单的DI。

我使用字符串路径创建了范围,并使用"/scopeName/subScopeName:componentName"选择范围,以便代码用户可以使用":"从范围中选择组件,从而在定义组件的同时选择范围。

没有接口(interface),因为在JS中键入检查非常简单。没有特殊的组件类型,例如工厂,值等,每个组件均被平等对待。

var JHTML = new Viziion('JHTML');
JHTML.addScope('/generate');

/* ... snip ... */

JHTML.addComponent('/generate:process', function(nodes) {
/* ... snip - the code inside isn't important here - snip ..*/
}).inject(['/generate:jsonInput']);
inject函数仅按预期的组件参数的顺序获取组件路径的数组。

钩子(Hook)是存储在 hooks属性中的组件,然后有一个函数 returnUserHandle,它将返回仅由钩子(Hook)组成的对象,因此所有函数都隐藏在闭包中,并且您可以向代码用户提供仅可用的方法。
JHTML.addHook('generate', function(jsonInput, process) {
var html = process(jsonInput);
return html;
}).inject(['/generate:jsonInput', '/generate:process']);

var handle = JHTML.returnUserHandle();

/* HTML Generator Syntax - Client */

console.log(handle.generate());

问题:

为了直观地将 inject指向正确的对象,主对象上有一个 focus属性,我认为我可以在 that.focusthis.focus等不同方法中使用 addComponent(这是对 inject的引用)将新功能链接到正确的位置在我的范围模型中,并在使用 focus创建它们或通过 addComponent方法调用之后,仍然在 focusComponent中引用它们,然后 inject可以找到依赖项,并通过执行以下操作“关联”它们:
that.focus = function() {
that.focus.apply(null, dependencies);
};

而且我认为这会将依赖关系(一个数组)打包为一个闭包,当代码用户调用该函数时,将应用正确的依赖关系,这就是局面。但是不。这些函数似乎没有通过引用从 that.focus传递到范围模型中。 that.focus更新,但范围模型不更新。

我的引用逻辑出了什么问题?

编码:

这是代码的简化版本。我认为我已经尽力解释了它的工作原理以及我要解决的引用问题的确切位置。

    /* Dependency Injection Framework - viziion.js */

function Viziion() {
var that = this;

//here's the focus property I mentioned
this.focus = null;

this.scope = {
'/': {
'subScopes': {},
'components': {}
}
};
this.hooks = {};

this.addScope = function(scopeName) {
/* the way this works inst relevant to the problem */
};

this.addComponent = function(componentName, func) {

var scopeArray = // snip
// snip - just code to read the component path

for (var i = 0; i <= scopeArray.length; i++) {
if (scopeArray[i] !== "") {
if (scope.subScopes[scopeArray[i]]) {
scope = scope.subScopes[scopeArray[i]];
} else if (i == scopeArray.length) {

// And here's where I add the component to the scope model
// and reference that component in the focus property
scope.components[scopeName] = func;
that.focus = scope.components[scopeName];

} else {
throw 'Scope path is invalid.';
}
}
}
} else {
throw 'Path does not include a component.';
}
return that;
};

this.returnComponent = function(componentName, callback) {
/* ... snip ... */
};

this.addHook = function(hookName, func) {
/* ... snip ... */
};

this.inject = function(dependencyArray) {
if (dependencyArray) {
var dependencies = [];
for (var i = 0; i < dependencyArray.length; i++) {
that.returnComponent(dependencyArray[i], function(dependency) {
dependencies.push(dependency);
});
}
that.focus = function() {
that.focus.apply(null, dependencies);
};
return that;
}
};

/* ... snip - focusComponent - snip ... */

/* ... snip - returnUserHandle - snip ... */


如上所示在“语法” header 下应用时,应生成带有一串HTML的控制台日志。

相反,我得到 TypeError: undefined is not a function,它对应于 var html = process(jsonInput);行。

如果您想一起测试完整的代码,则为:

/* Dependency Injection Framework - viziion.js */

function Viziion(appName) {
if (typeof appName == 'string') {
var that = this;
this.name = appName;
this.focus = null;
this.scope = {
'/': {
'subScopes': {},
'components': {}
}
};
this.hooks = {};

this.addScope = function(scopeName) {
if (typeof scopeName == 'string') {
var scopeArray = scopeName.split('/');
var scope = that.scope['/'];
for (var i = 0; i < scopeArray.length; i++) {
if (scopeArray[i] !== "") {
if (scope.subScopes[scopeArray[i]]) {
scope = scope.subScopes[scopeArray[i]];
} else {
scope.subScopes[scopeArray[i]] = {
'subScopes': {},
'components': {}
};
}
}
}
} else {
throw 'Scope path must be a string.';
}
return that;
};

this.addComponent = function(componentName, func) {
if (typeof componentName == 'string') {
var scopeArray = componentName.split(':');
if (scopeArray.length == 2) {
var scope = that.scope['/'];
var scopeName = scopeArray[1];
scopeArray = scopeArray[0].split('/');
for (var i = 0; i <= scopeArray.length; i++) {
if (scopeArray[i] !== "") {
if (scope.subScopes[scopeArray[i]]) {
scope = scope.subScopes[scopeArray[i]];
} else if (i == scopeArray.length) {
scope.components[scopeName] = func;
that.focus = scope.components[scopeName];
} else {
throw 'Scope path is invalid.';
}
}
}
} else {
throw 'Path does not include a component.';
}
} else {
throw 'Component path must be a string.';
}
return that;
};

this.returnComponent = function(componentName, callback) {
if (typeof componentName == 'string') {
var scopeArray = componentName.split(':');
if (scopeArray.length == 2) {
var scope = that.scope['/'];
var scopeName = scopeArray[1];
scopeArray = scopeArray[0].split('/');
for (var i = 0; i <= scopeArray.length; i++) {
if (scopeArray[i] !== "") {
if (i == scopeArray.length) {
callback(scope.components[scopeName]);
} else if (scope.subScopes[scopeArray[i]]) {
scope = scope.subScopes[scopeArray[i]];
} else {
throw 'Scope path is invalid.';
}
}
}
} else {
throw 'Path does not include a component.';
}
} else {
throw 'Component path must be a string.';
}
};

this.addHook = function(hookName, func) {
if (typeof hookName == 'string') {
that.hooks[hookName] = func;
that.focus = that.hooks[hookName];
} else {
throw 'Hook name must be a string.';
}
return that;
};

this.inject = function(dependencyArray) {
if (dependencyArray) {
var dependencies = [];
for (var i = 0; i < dependencyArray.length; i++) {
that.returnComponent(dependencyArray[i], function(dependency) {
dependencies.push(dependency);
});
}
console.log(that.focus);
that.focus = function() {
that.focus.apply(null, dependencies);
};
console.log(that.focus);
console.log(that.scope);
return that;
}
};

this.focusComponent = function(componentPath) {
that.focus = that.returnUserHandle(componentPath);
};

this.returnUserHandle = function() {
return that.hooks;
};

} else {
throw 'Viziion name must be a string.';
}
}

/* JSON HTML Generator - A Simple Library Using Viziion */

var JHTML = new Viziion('JHTML');

JHTML.addScope('/generate');

JHTML.addComponent('/generate:jsonInput', [{
tag: '!DOCTYPEHTML'
}, {
tag: 'html',
children: [{
tag: 'head',
children: []
}, {
tag: 'body',
children: []
}]
}]);

JHTML.addComponent('/generate:process', function(nodes) {
var html = [];
var loop = function() {
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].tag) {
html.push('<' + tag + '>');
if (nodes[i].children) {
loop();
}
html.push('</' + tag + '>');
return html;
} else {
throw '[JHTML] Bad syntax: Tag type is not defined on node.';
}
}
};
}).inject(['/generate:jsonInput']);



JHTML.addHook('generate', function(jsonInput, process) {
console.log('Process func arg:');
console.log(process);
var html = process(jsonInput);
return html;
}).inject(['/generate:jsonInput', '/generate:process']);

var handle = JHTML.returnUserHandle();

/* HTML Generator Syntax - Client */

console.log(handle.generate());

最佳答案

大问题,大答案。让我们开始吧。

严格的OOP,适用范围

首先,从您的代码来看,您似乎可能并不完全了解this的概念。

除非您事先更改对象方​​法的执行上下文,否则所述对象方法始终将其上下文this绑定(bind)到对象实例。

那是:

function A () {
var that = this;
this.prop = 1;
this.method = function () {
console.log(that.prop);
};
}

new A().method();

通常相当于:
function A () {
this.prop = 1;

this.method = function () {
console.log(this.prop);
};
}

new A().method();

除非在执行 method.bind.call之前对 .apply进行了调整。

为什么这么重要?好吧,如果我们正确使用 this上下文,那么我们可以利用对象原型(prototype)。原型(prototype)是一种基于实例定义对象的每种方法的更为优雅的解决方案。

在这里,我们创建两个实例,但只有一个 method
function A () {
this.prop = 1;
}

A.prototype.method = function () {
console.log(this.prop);
};

new A().method();
new A().method();

这对于提高清晰度很重要,以后在将上下文和参数绑定(bind)到函数 (!)时也很重要。

代码卫生

如果愿意,可以跳过本主题(直接转到“问题”),因为可能认为它不合适,但是请记住,它确实与代码中的部分问题有关。

您的代码很难阅读。

这是一些想法。

样机

使用它们。您不必担心用户会更改您的执行上下文,因为这可能是对程序的滥用。考虑到它们具有源代码,安全性不应该成为问题。

在这里没有太多要说的。

早点离开

如果您要进行完整性检查,请尝试尽早退出代码。如果由于类型不匹配而需要 throw,那么就在此处 throw-以后不需27行。
// Not great
if (typeof input === 'string') {
...
} else throw 'it away';


// Better
if (typeof input !== 'string') throw 'it away';
...

这也适用于循环-适当使用 continue关键字。这两件事都可以提高代码的清晰度,并减少嵌套和代码膨胀。

循环缓存

当您遍历数据结构时,并计划在块中多次使用当前元素时,应将该元素保存在变量中。访问元素和属性不一定是免费的。
// Not great
for (var i = 0; i < myArray.length; i++) {
if (myArray[i] > 5) callback(myArray[i]);

internalArray.push(myArray[i]);
}

// Better
var len = myArray.length, element;

for (var i = 0; i < len; i++) {
element = myArray[i];

if (element > 5) callback(element);

internalArray.push(element);
}

如果正确使用,则可以提高清晰度和性能。

问题

首先,我们在这里实际上在做什么?整个问题归结为功能绑定(bind)的应用过于复杂。也就是说,只需更改函数的执行上下文。

我还要直接声明该程序没有错误-只是有缺陷。

问题的主要症结在于这三行
that.focus = function() {
that.focus.apply(null, dependencies);
};

inject方法中找到。他们没有任何意义。这将导致无限递归,简单而简单。当您定义该函数时,它根本就不在乎 focusthat属性是正确的。这仅在执行时重要。

对我们来说幸运的是,由于 process组件未正确绑定(bind),我们实际上从未走得那么远。

问题的很大一部分是 focus属性。在您的程序中,您将其用作一种最新 Action 。关于刚刚发生的事情的单一历史。问题是,您试图以奇怪的方式热交换该值。

但是,由于 focus的反向应用,因此需要 inject属性(以及您稍后将看到的其他属性)。将组件/钩子(Hook)寄存器结构化为 inject模型的方式要求在方法调用之间保持状态。

作为本节的尾注, process组件函数定义将永远不会返回任何内容。即使您的模型正确,您的输入还是有缺陷的。 handle.generate()将始终返回 undefined

答案

那么我们该如何解决呢?好吧,老实说,第一个想法就是报废它。我认为反向注入(inject)模型很难看。从表面上看, inject方法涉及的间接级别非常困惑。

但是,那时我们什么都不会学,对吗?

真的,我们如何解决这个问题?好了,令功能程序员难以忍受的是,我们需要保持更多的状态。

就其本身而言,我们的 focus属性无法提供足够的信息来正确更改函数的执行上下文。

在我们的 focus之上(仅保留对我们最新组件值的引用),我们需要 field(组件/挂钩名称)和 fragment(组件对象,如果有挂钩则什么也没有)。

使用 inject中的这两个或三个值,我们可以获取 depedancies数组,将 bind设置为 focus,然后将结果函数设置回 field中。

下一部分的好处是,我们实际上可以通过使组件的上下文 this / hook为未绑定(bind)函数来删除闭包。

整个操作如下所示:
var focus = this.focus,
fragment = this.fragment,
field = this.field,
hook = function hook () {
return this.apply(null, arguments);
}, func;

dependencies.unshift(focus);
func = Function.prototype.bind.apply(hook, dependencies);

if (fragment) fragment[field] = func;
else this.hooks[field] = func;

其中大多数应该是很直接的,但是有一个可能会给人们带来一些问题。要记住的重要一点是,我们实际上是在这里依次创建两个函数,在某种意义上“丢弃”第一个。 (应该注意,这可以使用 hook.bind.apply进行另一种方式,但是它创建的代码甚至更加令人困惑。这几乎可以说是优雅的。)
dependencies.unshift(focus);
func = Function.prototype.bind.apply(hook, dependencies);

首先,我们将 focus(我们的原始函数)添加到依赖项列表的前面。此刻很重要。

然后,我们使用 Function.prototype.bind调用 Function.prototype.apply(请记住,函数原型(prototype)方法也共享函数原型(prototype)方法。从头到尾,很多乌龟)。

现在,我们将绑定(bind)上下文 hook和前缀依赖项传递给 apply
hook用作绑定(bind)的主机,其上下文 this由传递给 apply的参数数组的第一个元素更改。展开其余元素以塑造 bind的后续参数,从而创建结果函数的绑定(bind)参数。

这不是一个非常简单的概念,所以花点时间。

要注意的另一件事是我完全删除了 focusComponent。在上下文中,它的实现没有任何意义。您的模型依赖于最后一次输入注入(inject),因此您需要重新实现 focusComponent作为简单地调整 focusfieldfragment状态的方法。

一个小的子修补程序是 process组件函数。这里不再赘述。您可以与原始代码进行比较和对比,差异非常明显。

JHTML.addComponent('/generate:process', function (nodes) {
return (function build (struct, nodes) {
var length = nodes.length, node, tag;

for (var i = 0; i < length; i++) {
node = nodes[i];
tag = node.tag;

if (!tag) throw '[JHTML] Bad syntax: Tag type is not defined on node.';

struct.push('<' + tag + '>');

if (node.children) {
build(struct, node.children)
struct.push('</' + tag + '>');
}
}

return struct;
}([], nodes));
}).inject(['/generate:jsonInput']);


编码

以下是我认为您的代码的固定版本。它以我认为对清晰度和性能都有用的样式编写。

/* Dependency Injection Framework - viziion.js */

function Scope () {
this.subScopes = {};
this.components = {};
}

function Viziion (appName) {
if (typeof appName !== 'string') throw 'Viziion name must be a string.';

this.name = appName;
this.working = this.field = this.focus = null
this.scope = { '/': new Scope() };
this.hooks = {};
}

Viziion.prototype.addScope = function (scopeName) {
if (typeof scopeName !== 'string') throw 'Scope path must be a string.';

var scopeArray = scopeName.split('/'),
scope = this.scope['/'],
len = scopeArray.length,
element, sub;

for (var i = 0; i < len; i++) {
element = scopeArray[i];
if (element === '') continue;

sub = scope.subScopes[element]

if (sub) scope = sub;
else scope.subScopes[element] = new Scope();
}

return this;
};

Viziion.prototype.addComponent = function (componentName, func) {
if (typeof componentName !== 'string') throw 'Component path must be a string.';

var scopeArray = componentName.split(':'),
len, element, sub;
if (scopeArray.length != 2) throw 'Path does not include a component.';

var scope = this.scope['/'],
scopeName = scopeArray[1];

scopeArray = scopeArray[0].split('/');
len = scopeArray.length;

for (var i = 0; i <= len; i++) {
element = scopeArray[i];
if (element === '') continue;

sub = scope.subScopes[element];

if (sub) scope = sub;
else if (i === len) {
this.fragment = scope.components;
this.field = scopeName;
this.focus = scope.components[scopeName] = func;
}
else throw 'Scope path is invalid';
};

return this;
};

Viziion.prototype.returnComponent = function (componentName, callback) {
if (typeof componentName !== 'string') throw 'Component path must be a string.';

var scopeArray = componentName.split(':'),
len, element, sub;
if (scopeArray.length != 2) throw 'Path does not include a component.';

var scope = this.scope['/'],
scopeName = scopeArray[1];

scopeArray = scopeArray[0].split('/');
len = scopeArray.length;

for (var i = 0; i <= len; i++) {
element = scopeArray[i];
if (element === '') continue;
sub = scope.subScopes[element]

if (i === len) callback(scope.components[scopeName]);
else if (sub) scope = sub;
else throw 'Scope path is invalid';
}
};

Viziion.prototype.addHook = function (hook, func) {
if (typeof hook !== 'string') throw 'Hook name must be a string.';
this.fragment = null;
this.field = hook;
this.focus = this.hooks[hook] = func;
return this;
};

Viziion.prototype.inject = function (dependancyArray) {
if (!dependancyArray) return;

var dependencies = [],
len = dependancyArray.length,
element;

function push (dep) { dependencies.push(dep); }

for (var i = 0; i < len; i++) {
element = dependancyArray[i];
this.returnComponent(element, push);
}

var focus = this.focus,
fragment = this.fragment,
field = this.field,
hook = function hook () {
return this.apply(null, arguments);
}, func;

dependencies.unshift(focus);
func = Function.prototype.bind.apply(hook, dependencies);

if (fragment) fragment[field] = func;
else this.hooks[field] = func;

return this;
};

Viziion.prototype.returnUserHandle = function () { return this.hooks; };

/* JSON HTML Generator - A Simple Library Using Viziion */

var JHTML = new Viziion('JHTML');

JHTML.addScope('/generate');

JHTML.addComponent('/generate:jsonInput', [{
tag: '!DOCTYPE html'
}, {
tag: 'html',
children: [{
tag: 'head',
children: []
}, {
tag: 'body',
children: []
}]
}]);

JHTML.addComponent('/generate:process', function (nodes) {
return (function build (struct, nodes) {
var length = nodes.length, node, tag;

for (var i = 0; i < length; i++) {
node = nodes[i];
tag = node.tag;

if (!tag) throw '[JHTML] Bad syntax: Tag type is not defined on node.';

struct.push('<' + tag + '>');

if (node.children) {
build(struct, node.children)
struct.push('</' + tag + '>');
}
}

return struct;
}([], nodes));
}).inject(['/generate:jsonInput']);

JHTML.addHook('generate', function (jsonInput, process) {
return process(jsonInput);
}).inject(['/generate:jsonInput', '/generate:process']);


var handle = JHTML.returnUserHandle();

console.log(JHTML);

/* HTML Generator Syntax - Client */

console.log(handle.generate());

关于javascript - 我的对象不是通过引用更新的,逻辑有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34243499/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com