题目:
有一个对象数组,里面存储着通讯录。
函数 lookUp
有两个预定义参数:firstName
值和prop
属性 。
函数将会检查通讯录中是否存在一个与传入的 firstName
相同的联系人。如果存在,那么还需要检查对应的联系人中是否存在 prop
属性。
如果它们都存在,函数返回prop
属性对应的值。
如果firstName
值不存在,返回 "No such contact"
。
如果prop
属性不存在,返回 "No such property"
。
通讯录数组如下:
var contacts = [
{
"firstName": "Akira",
"lastName": "Laine",
"number": "0543236543",
"likes": ["Pizza", "Coding", "Brownie Points"]
},
{
"firstName": "Harry",
"lastName": "Potter",
"number": "0994372684",
"likes": ["Hogwarts", "Magic", "Hagrid"]
},
{
"firstName": "Sherlock",
"lastName": "Holmes",
"number": "0487345643",
"likes": ["Intriguing Cases", "Violin"]
},
{
"firstName": "Kristian",
"lastName": "Vos",
"number": "unknown",
"likes": ["Javascript", "Gaming", "Foxes"]
}
];
下面是解答过程:
1.拿到题目首先想到的是利用for循环,遍历数组,然后判断通讯录中是否存在与函数传进来的变量firstName相匹配的数组,如果有,进行第二步判断,如果没有,返回"No such contact"
function lookUp(firstName, prop){
for(var i=0; i<contacts.length; i++){
if(contacts[i].firstName==firstName){ //判断是否存在
//第二步判断
}else{
return "No such contact";
}
}
}
lookUp("Akira", "number");
2. 如果有,就继续第二步判断,判断对应的联系人中是否存在 prop
属性,存在就返回对应的属性值,不存在就返回 "No such property"
function lookUp(firstName, prop){
for(var i=0; i<contacts.length; i++){
if(contacts[i].firstName==firstName){
if(contacts[i].hasOwnProperty(prop)){ //第二步判断
return contacts[i][prop]; //第一个return语句
}else{
return "No such property"; //第二个return语句
}
}else{
return "No such contact"; //第三个return语句
}
}
}
lookUp("Akira", "number");
理论上来说,函数到此已经写完了,逻辑上看似没有问题,但是当提交代码以后,发现fcc并没有给我通过。在换了几组测试数据之后,发现了一个问题:只有用通讯录中的第一个数组数据进行测试时,返回的结果才是正确的。用通讯录中其他存在的数据去测试时,发现返回的都是“No such contact”。也就是说:不管你函数传进去的参数是通讯录中的第几个数据,实际上函数里面的for循环只触发了一次,也就是说无论什么情况下,通讯录的数据都无法全部被遍历,而是只遍历了第一条数据,即:contacts[0]这一条数据。
为什么for循环只执行了一次?不知大家有没有注意到我在代码中写的3条return语句。实际上,不管函数返回的是哪条return语句的内容,只要执行了return语句,循环便会退出,所以当第一次循环的时候,函数必定会执行3条return语句的其中一句,于是for循环就会退出,无法进行第二次循环。所以为了让循环进行下去,我们应该把return语句替换掉,所以我把return的三条语句的内容,用一个变量res去保存它们,然后函数的最后(for循环的外面)再去返回res。代码修改如下:
function lookUp(firstName, prop){
var res; //定义一个变量,用来保存结果
for(var i=0; i<contacts.length; i++){
if(contacts[i].firstName==firstName){
if(contacts[i].hasOwnProperty(prop)){
res = contacts[i][prop]; //之前的第一条return语句
}else{
res = "No such property"; //之前的第二条return语句
}
}else{
res = "No such contact"; //之前的第三条return语句
}
}
return res; //在for循环之外返回res
}
lookUp("Akira", "number");
再一次提交代码,发现fcc依旧没有让我通过。吸取了上一次的教训,再检查一下代码,发现这次刚好和上一次情况相反。上一次是无论如何for循环只执行了第一次,而这一次是无论如何for循环都执行到了最后一次,就算是中途找到了相匹配的数组数据,循环并没有结束,依然会继续下去,因此正确的返回结果会被后来的循环的结果覆盖掉。
如何解决?很简单,在第一个结果和第二个结果的下面,分别再加一条break语句,意思是假如执行到了这条结果语句,说明已经找到了正确的结果,那么break语句便会让这个循环终止,让函数返回当前的res。代码修改如下:(最终版本)
function lookUp(firstName, prop){
var res;
for(var i=0; i<contacts.length; i++){
if(contacts[i].firstName==firstName){
if(contacts[i].hasOwnProperty(prop)){
res = contacts[i][prop]; //第一条结果赋值语句
break;
}else{
res = "No such property"; //第二条结果赋值语句
break;
}
}else{
res = "No such contact"; //第三条结果赋值语句
}
}
return res;
}
lookUp("Akira", "number");
可能有些同学会问为什么第三条结果语句后面不需要加break语句。因为,试想一下,假如加了break语句,结果是和刚才的第一种情况一样的,for循环无论如何都只能执行一次。如果没有加break语句,比如通讯录第N个数据就匹配到了你传入的参数,它一定会执行第一条结果赋值语句或者是第二条结果赋值语句,所以一定会break,跳出循环。如果到最后一次循环还是没有执行第一条结果赋值语句或者第二条结果赋值语句,那么说明传进来的参数和通讯录中的数据是不匹配的,即不存在通讯录之中,那么就理所当然的将“No such contact”赋给res,此时最后一次循环结束,返回当前的res。因此第三条结果赋值语句后不能加break语句。