$watch 内部循环的执行
execution of $watch inside loop
我正在读一本书,其中遇到了以下代码(有一个名为 products 的二维数组,它有 3 行)-
html
<div unordered-list="products" list-property="price | currency"></div>
和 JS
angular.module("exampleApp", [])
.directive("unorderedList", function () {
return function (scope, element, attrs) {
var data = scope[attrs["unorderedList"]];
var propertyExpression = attrs["listProperty"];
if (angular.isArray(data)) {
var listElem = angular.element("<ul>");
element.append(listElem);
for (var i = 0; i < data.length; i++) {
var itemElement = angular.element('<li>');
listElem.append(itemElement);
var watcherFn = function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
scope.$watch(watcherFn, function (newValue, oldValue) {
itemElement.text(newValue);
});
}
}
}
})
书上说,"AngularJS evaluates the three watcher functions, which refer to data[i] after the loop terminates"。 “到时间循环终止时,i 的值为 3,这意味着所有三个观察者函数都尝试访问一个
数据数组中的对象不存在,这就是该指令不起作用的原因。
watcher 函数在循环内部并从作用域调用。$watch 为什么会这样?
这是由于 javascript 的变量作用域的工作原理。
您可以在以下位置阅读完整的答案:JavaScript closure inside loops – simple practical example
Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.
同样适用于javascript个watchers,应用于不同的tick
。 tick 表示下一个事件循环执行。
要了解更多信息,请阅读有关 angular 的摘要周期的更多信息。
所以在函数执行之前变量的值已经改变了。
简单解决方案 1 - 特定于 angular
针对 angular 的一个简单解决方案是为 $watch
构造一个字符串表达式。
所以 $scope.$watch('products[1].price | currency', ..)
应该可以。
根据您的代码段将其转换为变量
$scope.$watch(`${attrs['unorderdList']}[${i}].${attrs['listProperty']}`, function(newValue){ ... })
或者类似的东西应该可以。我将需要更多信息来提供 运行 示例。
简单解决方案 2 - noneangular具体
另一种解决方案(并非特定于 angular)是将其绑定到不同的范围。所以如果循环的第一行是
for (let i = 0; i < data.length; i++) {
只需将 var
替换为 let
- 就可以了。
该解决方案利用了新的 JavaScript let
语法,它具有块作用域而不是函数作用域。
for (let i = 0; i < data.length; i++) {
var itemElement = angular.element('<li>');
listElem.append(itemElement);
var watcherFn = function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
scope.$watch(watcherFn, function (newValue, oldValue) {
itemElement.text(newValue);
});
}
发生这种情况是因为这些函数将与表示执行时外部 JS 函数作用域状态的闭包一起工作。这就是为什么在循环停止后变量 i
将引用最后分配的值:3.
要使变量 i
在迭代时等于值,您需要创建中间作用域并通过附加函数包装它:
var watcherFn = (function(i)
return function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
}(i));
您可以在这个答案中阅读更多关于 JS 中的闭包的信息:
How do JavaScript closures work?
我正在读一本书,其中遇到了以下代码(有一个名为 products 的二维数组,它有 3 行)-
html
<div unordered-list="products" list-property="price | currency"></div>
和 JS
angular.module("exampleApp", [])
.directive("unorderedList", function () {
return function (scope, element, attrs) {
var data = scope[attrs["unorderedList"]];
var propertyExpression = attrs["listProperty"];
if (angular.isArray(data)) {
var listElem = angular.element("<ul>");
element.append(listElem);
for (var i = 0; i < data.length; i++) {
var itemElement = angular.element('<li>');
listElem.append(itemElement);
var watcherFn = function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
scope.$watch(watcherFn, function (newValue, oldValue) {
itemElement.text(newValue);
});
}
}
}
})
书上说,"AngularJS evaluates the three watcher functions, which refer to data[i] after the loop terminates"。 “到时间循环终止时,i 的值为 3,这意味着所有三个观察者函数都尝试访问一个 数据数组中的对象不存在,这就是该指令不起作用的原因。
watcher 函数在循环内部并从作用域调用。$watch 为什么会这样?
这是由于 javascript 的变量作用域的工作原理。
您可以在以下位置阅读完整的答案:JavaScript closure inside loops – simple practical example
Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.
同样适用于javascript个watchers,应用于不同的tick
。 tick 表示下一个事件循环执行。
要了解更多信息,请阅读有关 angular 的摘要周期的更多信息。
所以在函数执行之前变量的值已经改变了。
简单解决方案 1 - 特定于 angular
针对 angular 的一个简单解决方案是为 $watch
构造一个字符串表达式。
所以 $scope.$watch('products[1].price | currency', ..)
应该可以。
根据您的代码段将其转换为变量
$scope.$watch(`${attrs['unorderdList']}[${i}].${attrs['listProperty']}`, function(newValue){ ... })
或者类似的东西应该可以。我将需要更多信息来提供 运行 示例。
简单解决方案 2 - noneangular具体
另一种解决方案(并非特定于 angular)是将其绑定到不同的范围。所以如果循环的第一行是
for (let i = 0; i < data.length; i++) {
只需将 var
替换为 let
- 就可以了。
该解决方案利用了新的 JavaScript let
语法,它具有块作用域而不是函数作用域。
for (let i = 0; i < data.length; i++) {
var itemElement = angular.element('<li>');
listElem.append(itemElement);
var watcherFn = function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
scope.$watch(watcherFn, function (newValue, oldValue) {
itemElement.text(newValue);
});
}
发生这种情况是因为这些函数将与表示执行时外部 JS 函数作用域状态的闭包一起工作。这就是为什么在循环停止后变量 i
将引用最后分配的值:3.
要使变量 i
在迭代时等于值,您需要创建中间作用域并通过附加函数包装它:
var watcherFn = (function(i)
return function (watchScope) {
return watchScope.$eval(propertyExpression, data[i]);
}
}(i));
您可以在这个答案中阅读更多关于 JS 中的闭包的信息: How do JavaScript closures work?