重复输入,直到输入的是一个数字
创建一个函数 readNumber
,它提示输入一个数字,直到访问者输入一个有效的数字为止。
结果值必须以数字形式返回。
访问者也可以通过输入空行或点击“取消”来停止该过程。在这种情况下,函数应该返回 null。
知识点
isNaN(value)
将其参数转换为数字,然后检测它是否为NaN
isFinite(value)
将其参数转换为数字,如果它是常规数字,则返回true
,而不是NaN/Infinity/-Infinity
实现:
function readNumber() {
let num;
do {
num = prompt("Enter a number please?", 0);
console.log(num, '--', typeof num, '----', isFinite(num))
} while ( !isFinite(num) );
if (num === null || num === '') return null;
return +num;
}
alert(`Read: ${readNumber()}`);
从 min 到 max 的随机数
内建函数 Math.random()
会创建一个在 0
到 1
之间(不包括 1
)的随机数。
编写一个 random(min, max
) 函数,用以生成一个在 min
到 max
之间的随机浮点数(不包括 max
))。
// 我们需要将区间 0…1 中的所有值“映射”为范围在 min 到 max 中的值。
// 1.如果我们将 0…1 的随机数乘以 max-min,则随机数的范围将从 0…1 增加到 0..max-min。
// 2.现在,如果我们将随机数与 min 相加,则随机数的范围将为 min 到 max。
function random(min, max) {
return min + Math.random() * (max - min);
}
alert( random(1, 5) );
alert( random(1, 5) );
alert( random(1, 5) );
从 min 到 max 的随机整数
创建一个函数 randomInteger(min, max)
,该函数会生成一个范围在 min
到 max
中的随机整数,包括 min
和 max
。
在 min..max
范围中的所有数字的出现概率必须相同。
方法一:
// 调整取值范围的边界
function randomInteger(min, max) {
// 现在范围是从 (min-0.5) 到 (max+0.5)
let rand = min - 0.5 + Math.random() * (max - min + 1);
return Math.round(rand);
}
alert( randomInteger(1, 3) );
方法二:使用 Math.floor
来取范围从 min
到 max+1
的随机数
function randomInteger(min, max) {
// here rand is from min to (max+1)
let rand = min + Math.random() * (max + 1 - min);
return Math.floor(rand);
}
alert( randomInteger(1, 3) );
字符串首字母大写
写一个函数 ucFirst(str)
,并返回首字母大写的字符串 str
function ucFirst(str) {
if (!str) return str;
return str[0].toUpperCase() + str.slice(1);
}
alert( ucFirst("john") ); // John
检查 spam
写一个函数 checkSpam(str)
,如果 str 包含 viagra 或 XXX 就返回 true,否则返回 false。
函数必须不区分大小写。
function checkSpam(str) {
let lowerStr = str.toLowerCase();
return lowerStr.includes('viagra') || lowerStr.includes('xxx');
}
alert( checkSpam('buy ViAgRA now') );
alert( checkSpam('free xxxxx') );
alert( checkSpam("innocent rabbit") );
截断文本
创建函数 truncate(str, maxlength)
来检查 str 的长度,如果超过 maxlength —— 应使用 “…” 来代替 str 的结尾部分,长度仍然等于 maxlength。
function truncate(str, maxlength) {
return (str.length > maxlength) ?
str.slice(0, maxlength - 1) + '…' : str;
}
提取货币
我们有以 “$120” 这样的格式表示的花销。意味着:先是美元符号,然后才是数值。
创建函数 extractCurrencyValue(str)
从字符串中提取数值并返回。
function extractCurrencyValue(str) {
return +str.slice(1);
}
数组操作
let styles = ["Jazz", "Blues"];
// 从数组末端添加"Rock-n-Roll"
styles.push("Rock-n-Roll"); // ["Jazz", "Blues", "Rock-n-Roll"]
// 用 "Classics" 替换掉数组最中间的元素。查找数组最中间的元素的代码应该适用于任何奇数长度的数组。
styles[Math.floor((styles.length - 1) / 2)] = "Classics"; // ["Jazz", "Classics", "Rock-n-Roll"]
// 移除数组第一个值
let shiftItem = styles.shift() // style = ["Classics", "Rock-n-Roll"]
console.log(shiftItem) // "Jazz"
//在数组前面添加 Rap 和 Reggae
styles.unshift("Rap", "Reggae"); // style = ["Rap", "Reggae", "Classics", "Rock-n-Roll"]
在数组上下文调用
let arr = ["a", "b"];
arr.push(function() {
alert( this ); // this指数组arr
});
arr[2](); // 执行方法,打印arr:"a,b,function(){alert(this)}"
输入数字求和(数组)
写出函数 sumInput(),要求如下:
- 使用 prompt 向用户索要值,并存在数组中。
- 当用户输入了非数字、空字符串或者点击“取消”按钮的时候,问询结束。
- 计算并返回数组所有项之和。
P.S. 0 是有效的数字,不要因为是 0 就停止问询。
function sumInput() {
let numbers = [];
while (true) {
let value = prompt("请输入数字?", 0)
if (value === "" || value === null || !isFinite(value)) break;
numbers.push(+value);
}
let sum = 0;
for (let item of numbers) {
sum += item;
}
return sum;
}
alert( sumInput() );
将 border-left-width 转换成 borderLeftWidth(数组)
function camelize(str) {
return str
.split('-') // splits 'border-left-width' into array ['border', 'left', 'width']
.map(
// converts ['border', 'left', 'width'] into ['border', 'Left', 'Width']
(word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)
)
.join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord'
}
console.log(camelize("border-left-width")) // borderLeftWidth
过滤范围(数组)
写一个函数 filterRange(arr, a, b)
,该函数获取一个数组 arr
,在其中查找数值大于或等于 a
,且小于或等于 b
的元素,并将结果以数组的形式返回。
该函数不应该修改原数组。它应该返回新的数组。
function filterRange(arr, a, b) {
// 在表达式周围添加了括号,以提高可读性
return arr.filter(item => (a <= item && item <= b));
}
let arr = [5, 3, 8, 1];
let filtered = filterRange(arr, 1, 4);
console.log( filtered ); // 3,1(匹配的值)
console.log( arr ); // 5,3,8,1(未经改动的数组中的值)
原位(in place)过滤范围(数组)
写一个函数 filterRangeInPlace(arr, a, b)
,该函数获取一个数组 arr,并删除其中介于 a 和 b 区间以外的所有值。检查:a ≤ arr[i] ≤ b
。
该函数应该只修改数组。它不应该返回任何东西。
function filterRangeInPlace(arr, a, b) {
for (let i = 0; i < arr.length; i++) {
let val = arr[i];
// 如果超出范围,则删除
if (val < a || val > b) {
arr.splice(i, 1);
i--;
}
}
}
let arr = [5, 3, 8, 1];
filterRangeInPlace(arr, 1, 4); // 删除 1 到 4 范围之外的值
console.log( arr ); // [3, 1]
降序/升序排序(数组)
let arr = [5, 2, 1, -10, 8];
// 升序
arr.sort((a, b) => a - b);
console.log(arr)
// 降序
arr.sort((a, b) => b - a);
console.log( arr );
复制和排序数组
我们有一个字符串数组 arr。我们希望有一个排序过的副本,但保持 arr 不变。
创建一个函数 copySorted(arr)
返回这样一个副本。
使用 str.localeCompare
方法正确地对字母进行排序。
function copySorted(arr) {
// slice()返回一个新的数组,再对新数组进行排序
return arr.slice().sort((a, b) => a.localeCompare(b));
}
let arr = ["HTML", "JavaScript", "CSS"];
let sorted = copySorted(arr);
console.log( sorted );
console.log( arr );
按字段排序一组对象
let users = [
{ name: "John", age: 20, surname: "Johnson" },
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" }
]
// 常规写法
// 通过 name (Ann, John, Pete)
users.sort((a, b) => a.name > b.name ? 1 : -1);
// 通过 age (Pete, Ann, John)
users.sort((a, b) => a.age > b.age ? 1 : -1);
编写函数 byField(fieldName)
,每次传入字段名,就可以实现排序。
function byField(fieldName){
return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;
// 上面代码是简写,具体实现如下
// function compareFn (a, b){
// return a[fieldName] > b[fieldName] ? 1 : -1
//}
// return compareFn;
}
// 使用
users.sort(byField('name'));
users.sort(byField('age'));
byField
函数接受一个参数 fieldName 。- 在函数内部定义了一个名为
compareFn
的函数,它接受两个参数a
和b
。通过比较a
和b
对象中指定的fieldName
属性的值来决定返回 1 (表示a
排在b
之后)还是 -1 (表示a
排在b
之前)。 - 最后,
byField
函数返回的是compareFn
这个函数本身,而不是调用compareFn
的结果。
知识点array.sort()
sort(compareFn)
,参数compareFn
是定义排序顺序的函数,该函数有a
和b
两个参数。返回值应该是一个数字,其符号表示两个元素的相对顺序:如果 a
小于 b
,返回值为负数,如果 a
大于 b
,返回值为正数,如果两个元素相等,返回值为 0。NaN 被视为 0。
a,第一个用于比较的元素。不会是 undefined。
b,第二个用于比较的元素。不会是 undefined。
创建一个可扩展的 calculator
创建一个构造函数 Calculator,以创建“可扩展”的 calculator 对象。
该任务由两部分组成:
- 首先,实现
calculate(str)
方法,该方法接受像 “1 + 2” 这样格式为“数字 运算符 数字”(以空格分隔)的字符串,并返回结果。该方法需要能够理解加号 + 和减号 -。 - 然后添加方法
addMethod(name, func)
,该方法教calculator
进行新操作。它需要运算符name
和实现它的双参数函数func(a,b)
。
function Calculator() {
// 存储支持的方法
this.methods = {
"-": (a, b) => a - b,
"+": (a, b) => a + b
};
// 计算
this.calculate = function(str) {
let split = str.split(' '),
a = +split[0],
op = split[1],
b = +split[2];
if (!this.methods[op] || isNaN(a) || isNaN(b)) {
return NaN;
}
return this.methods[op](a, b);
};
// 添加计算方法
this.addMethod = function(name, func) {
this.methods[name] = func;
};
}
let calc = new Calculator();
console.log( calc.calculate("3 + 7") ); // 10
console.log( calc.calculate("7 - 2") ); // 5
// 添加乘法、除法、求幂运算
calc.addMethod("*", (a, b) => a * b);
calc.addMethod("/", (a, b) => a / b);
calc.addMethod("**", (a, b) => a ** b);
console.log( calc.calculate("2 ** 3") ); // 8
数组映射到数组
你有一个 user
对象数组,每个对象都有 user.name
。编写将其转换为 names
数组的代码。
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let users = [ john, pete, mary ];
let names = users.map(item => item.name)
console.log( names ); // John, Pete, Mary
数组映射到对象
你有一个 user
对象数组,每个对象都有 name
,surname
和 id
。
编写代码以该数组为基础,创建另一个具有 id
和 fullName
的对象数组,其中 fullName
由 name
和 surname
生成。
let john = { name: "John", surname: "Smith", id: 1 };
let pete = { name: "Pete", surname: "Hunt", id: 2 };
let mary = { name: "Mary", surname: "Key", id: 3 };
let users = [ john, pete, mary ];
let usersMapped = users.map(item => {
return {
id: item.id,
fullname: `${item.name} ${item.subname}`
}
})
console.log(usersMapped)
按年龄对用户排序
编写函数 sortByAge(users)
获得对象数组的 age
属性,并根据 age
对这些对象数组进行排序。
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let arr1 = [ pete, john, mary ];
let sortByAge = (arr) => {
arr.sort((a, b) => a.age - b.age);
}
sortByAge(arr1);
获取平均数(数组)
function getAverage(arr) {
let total = arr.reduce((prev, current) => prev + current, 0)
return total / arr.length;
}
let arr1 = [5, 17, 10, 28, 59]
console.log( getAverage(arr1) );
数组去重
利用for...of
遍历去重
function unique(arr) {
let result = [];
for (let str of arr) {
if (!result.includes(str)) {
result.push(str);
}
}
return result;
}
let arr= ["Hare", "Krishna", "Hare", "Krishna",
"Krishna", "Krishna", "Hare", "Hare", ":-O"
];
console.log( unique(arr) ); // ['Hare', 'Krishna', ':-O']
使用 Set 来存储唯一值
let arr= ["Hare", "Krishna", "Hare", "Krishna",
"Krishna", "Krishna", "Hare", "Hare", ":-O"
];
let set = new Set(arr)
let uniqueArr = Array.from(set)
console.log(uniqueArr) // ['Hare', 'Krishna', ':-O']
// 简洁写法
function unique(arr) {
return Array.from(new Set(arr));
}
let uniqueArr = unique(arr)
从数组创建键(值)对象
假设我们收到了一个用户数组,形式为:{id:..., name:..., age:... }
。
创建一个函数 groupById(arr)
从该数组创建对象,以 id 为键(key),数组项为值。
let users = [
{id: 'john', name: "John Smith", age: 20},
{id: 'ann', name: "Ann Smith", age: 24},
{id: 'pete', name: "Pete Peterson", age: 31},
];
function groupById(array) {
return array.reduce((obj, value) => {
obj[value.id] = value;
return obj;
}, {})
}
let usersById = groupById(users);
过滤字谜(需要反复学习)
Anagrams 是具有相同数量相同字母但是顺序不同的单词。
nap - pan 相同字母anp,相同长度,顺序不同
ear - are - era 相同字母aer…
cheaters - hectares - teachers 相同字母aceehrst…
写一个函数 aclean(arr)
,它返回被清除了字谜(anagrams)的数组。
理解:
字母anp组,只留下一个单词; 字母 aer组只留下一个单词; 字母aceehrst组,只留下一个单词。
对于所有的字谜(anagram)组,都应该保留其中一个词,但保留的具体是哪一个并不重要。
方法1:使用Map
function aclean(arr) {
let map = new Map();
for (let word of arr) {
console.log("原始单词:", word)
// 将单词 split 成字母,对字母进行排序,之后再 join 回来
let sorted = word.toLowerCase().split('').sort().join('');
console.log('sorted:', sorted)
map.set(sorted, word);
}
return Array.from(map.values());
}
let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"];
console.log( aclean(arr) );
如果再次遇到相同字母排序形式的单词,那么它将会覆盖 map 中有相同键的前一个值。
方法2:使用对象Object
function aclean(arr) {
let obj = {};
for (let i = 0; i < arr.length; i++) {
let sorted = arr[i].toLowerCase().split("").sort().join("");
obj[sorted] = arr[i];
}
return Object.values(obj);
}
let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"];
alert( aclean(arr) );