目前我的in for循环打印firstName。如果我希望它只打印lastName怎么办? 我如何修改for in循环?
var friends = {};
friends.bill = {
firstName: "Bill",
lastName: "Gates",
number: "(206) 555-5555",
address: ['One Microsoft Way','Redmond','WA','98052']
};
friends.steve = {
firstName: "Steve",
lastName: "Jobs",
number: "(408) 555-5555",
address: ['1 Infinite Loop','Cupertino','CA','95014']
};
var list = function(obj) {
for(var prop in obj) {
console.log(prop);
}
};
list(friends);
答案 0 :(得分:5)
请阅读 MDN - Working with objects 文件,您可以从中得出以下结论:
objectName.propertyName
用于访问对象中的指定属性。在您的情况下,这会转换为:obj.bill.lastName
和obj.steve.lastName
。obj[prop]
将在obj
内提供每个对象的每个实例。从那里访问对象属性的方法如#1所述。<强>结果:强>
var list = function(obj) {
for(var prop in obj) {
console.log(obj[prop].lastName);
}
};
答案 1 :(得分:0)
您将对象friends
传递到list
函数中,但list
函数循环遍历friends
对象的属性,而不是里面每个朋友的属性。
输出每个朋友的lastName
属性:
var list = function (obj) {
for (var friendObj in obj) {
console.log(obj[friendObj].lastName);
}
};
或者,要输出每个朋友的每个属性,请将list
功能更改为:
var list = function (obj) {
for (var friendObj in obj) {
for (var prop in obj[friendObj]) {
console.log(obj[friendObj][prop]);
}
console.log("");
}
};
答案 2 :(得分:0)
来自answer的dz210非常棒:可读,简单,直截了当且易于操作。
我会建议一些更难做的事情。我建议你为自己构建一个可重用函数的小工具包,它可以帮助你更简单地解决这些简单问题。
首先,您的最终数据结构如下所示:
var friends = {
bill: {
firstName: "Bill",
lastName: "Gates",
number: "(206) 555-5555",
address: ['One Microsoft Way','Redmond','WA','98052']
},
steve: {
firstName: "Steve",
lastName: "Jobs",
number: "(408) 555-5555",
address: ['1 Infinite Loop','Cupertino','CA','95014']
}
}
您需要拉出存储在其中的两个值,表示为&#34; bill&#34;和&#34;史蒂夫&#34;。这是第一次传递一个函数来提取对象的值,如下所示:
var values = obj => {
var results = [];
Object.keys(obj).forEach(key => results.push(obj[key]));
return results;
}
请注意,这使用内置功能来提取对象的键。然后,对于每一个,您将需要采用lastName
属性。这是一个简单的功能:
var getLastName = obj => obj['lastName'];
(我知道我本来可以使用点符号。请耐心等一下。)
虽然这适用于此用途,但它显然非常具体。 firstName
的一个看起来像这样:
var getFirstName = obj => obj['firstName'];
等等。但是如果我们想让这个可重用,我们可以创建一个函数,给定一个属性名称,返回一个接受一个对象的函数并检索该对象的命名属性,如下所示:
var prop = name => obj => obj[name];
然后我们
var getLastName = prop('lastName');
现在,对于我们的情况,我们希望获得列表中每个元素的姓氏。通过将函数应用于每个数据元素将一个数据结构转换为具有相同形状的另一个数据结构称为mapping
,我们可以编写一个简单的map
函数:
var map = fn => list => list.map(fn);
(注意there are reasons以避免这种风格的实现,但它起初并不坏。)
这允许我们做,例如,
map(n => n * n)([1, 2, 3, 4, 5]); //=> [1, 4, 9, 16, 25]
或
map(str => str.toUpperCase())(['foo', 'bar', 'baz']); //=> ['FOO', 'BAR', "BAZ']
现在,为了收集列表中每位朋友的姓氏,我们可以将map(getLastName)
应用于values(friends)
的结果:
map(getLastName)(values(friends)); //=> ['Gates', 'Jobs']
当然我们可以在那里停留,但仍有一些有用的清理工作要做。姓氏没有什么特别之处。我们可能经常想从列表的每个元素中获取命名属性的值,并且给它一个有用的名称会很好。在许多地方,它被称为“拔出”,表示从每个元素中删除命名属性。这是一个基本的实现:
var pluck = name => map(prop(name));
允许
var getLastNames = people => pluck('lastName')(values(people))
getLastNames(friends); //=> ['Gates', 'Jobs']
但是,一旦我们map
,我们也可以简单地实施values
:
var values = obj => map(key => obj[key])(Object.keys(obj));
pluck
和getLastNames
之间也存在一些共性。在每种情况下,我们调用一个函数,并将结果传递给另一个函数。在数学中,这称为composition
,我们可以编写另一个函数来表示它。这个函数是我们在这里开发的样式代码的核心,将两个函数合并为一个可重用性:
var compose = (f, g) => x => f(g(x));
使用它,我们可以重写两个函数:
var pluck = compose(map, prop);
var getLastNames = compose(pluck('lastName'), values);
// library functions
var map = fn => list => list.map(fn);
var compose = (f, g) => x => f(g(x));
var prop = name => obj => obj[name];
var pluck = compose(map, prop);
var values = obj => map(key => obj[key])(Object.keys(obj));
// application code
var getLastNames = compose(pluck('lastName'), values);
这里有两点需要注意:
var log = obj => console.log(obj); var lastNames = getLastNames(friends); //=> ['Gates', 'Jobs'] lastNames.forEach(log); // logs 'Gates' and then 'Jobs' to the console
values
相当复杂。这是因为我们需要在调用的第一个函数中使用obj
参数,然后在使用该结果的函数中再次使用map
参数。当然有一些技术可以帮助我们做到这一点,但它们比这里所做的更复杂。为什么有人会编写这类代码来解决这么简单的问题?
他们可能不会。
但是小问题会成倍增加并变成更大的问题。有许多方法可以满足一个人的要求。我在这里建议的是一种帮助管理这种复杂性的技术。通过为许多这些要求构建一个简单可重用函数的工具包,可以在安全基础上构建更大更好的代码。
此外,所讨论的两个函数compose
和var getLastNames = compose(pluck('lastName'), values);
最终成为函数式编程中使用的两个最突出的函数。如果你继续使用这种技术,你会一次又一次地看到它们。其他都是可重用且合理的,除了专门为此要求编写的一个函数外。但是让我们再看看那个:
values
一旦我们习惯了构图的概念,这是一个非常优雅的阅读。我们可以理解getLastNames是两个函数pluck('lastName')
和var getLastNames = R.compose(R.pluck('lastName'), R.values);
的组合。前者很清楚,后者应该是直截了当的。我们不必阅读任何描述如何来获取名称的代码,或者如何编写这两个函数。这比我们希望程序做什么的声明更接近于关于如何执行它的明确说明。这就是重点。我们越能描述我们想要的而不是如何这样做,我们就越要求计算机理解我们而不是试图理解计算机。
当然可以。围绕其中一些原则设计了许多库。 Underscore旨在为Javascript带来一些功能,Lodash提供了非常相似的功能集。这是两个最流行的JS工具。但是它们都坚持一些原生方法的设计,而牺牲了可能更简洁的功能设计。其他库,例如fn.js,fnuc,FKit和我最喜欢的Ramda(披露:我是作者之一),更直接的功能他们的前景。
例如,使用Ramda,代码就是
compose
但是在这里使用图书馆的危险在于你依靠它来牺牲理解正在发生的事情。如果您对map
和prop
,pluck
和values
以及friends.bill = ...
做了什么有很好的理解,那么使用一个可能更好的库 - 测试,这些功能的战斗硬化版本很有用。但如果你不这样做,可能会妨碍你的学习。从自己尝试这些东西然后慢慢转移到图书馆开始,从来没有伤害过。
这种从公共数据结构上的简单函数构建所有东西,使用可以接受函数和/或返回新函数的函数的技术,是所谓的函数式编程的标志之一。还有许多其他相当常见的功能,我们只讨论其中的两个。其中最重要的是引用透明度,其中任何表达式都可以用其值替换,而不会改变程序的行为。我们通过主要使用纯函数来迈出了重要的一步。另一个是不可变数据结构。虽然Javascript不支持此功能,但我们可以拒绝修改传递给我们的数据并始终返回新结构。您可能会注意到这就是我们在这里所做的。 (尽管请注意原始问题中的friends.steve = ...
行和{{1}}行,但您并未使用不可变数据结构。)
网络上有许多用于函数式编程的优秀资源,最近,其中一些资源都是针对Javascript的。现在是使用这种材料的好时机。