js基础
1.数字
//编写更大的数字
let num = 1e3;
console.log(num); //1000
let num2 = 1e-3;
console.log(num2); //0.001
console.log(0xff); // 十六进制形式的 255
console.log(0b11111111); // 二进制形式的 255
console.log(0o377); // 八进制形式的 255
//转换进制
let num3 = 255;
console.log(num3.toString(16)); //------ff-------
console.log(255..toString(16)); //直接对数字调用方法需要两个点
- 舍入:
(1) floor 向下
(2) ceil 向上
(3)round 四舍五入
请注意 toFixed 的结果是一个字符串,使用+ 运算将其转换为数字
let num = 12.34;
console.log( +num.toFixed(1) );
-
isNaN
isNaN(value) 将其参数转换为数字,然后测试它是否为 NaN:
使用加号 + 或 Number() 的数字转换是严格的。如果一个值不完全是一个数字,就会失败:
alert( +"100px" ); // NaN
这种情况我们就需要parseInt
console.log(parseInt("100px")); //------100-------
它们可以从字符串中“读取”数字,直到无法读取为止。如果发生 error,则返回收集到的数字。函数 parseInt 返回一个整数,而 parseFloat 返回一个浮点数
parseInt() 函数具有可选的第二个参数。它指定了数字系统的基数,因此 parseInt 还可以解析十六进制数字、二进制数字等的字符串:
alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255,没有 0x 仍然有效
alert( parseInt('2n9c', 36) ); // 123456
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);
}
console.log( randomInteger(1, 3) );
function randomInteger(min, max) {
// here rand is from min to (max+1)
let rand = min + Math.random() * (max - min + 1);
return Math.floor(rand);
}
console.log( randomInteger(1, 3) );
- 字符串
- 按位
对于 32-bit 整数,~n 等于 -(n+1)
alert( ~2 ); // -3,和 -(2+1) 相同
alert( ~1 ); // -2,和 -(1+1) 相同
alert( ~0 ); // -1,和 -(0+1) 相同
alert( ~-1 ); // 0,和 -(-1+1) 相同
检查字符串是否存在?
let str = "Widget";
if (~str.indexOf("Widget")) {
alert( 'Found it!' ); // 正常运行
}
-
获取子字符串
JavaScript 中有三种获取字符串的方法:substring、substr 和 slice。
(1)str.slice(start [, end])
返回字符串从 start 到(但不包括)end 的部分。
如果没有第二个参数,slice
会一直运行到字符串末尾:
let str = "stringify";
alert( str.slice(0, 5) ); // 'strin',从 0 到 5 的子字符串(不包括 5)
alert( str.slice(0, 1) ); // 's',从 0 到 1,但不包括 1,所以只有在 0 处的字符
反向截取:
let str = "stringify";
// 从右边的第四个位置开始,在右边的第一个位置结束
console.log( str.slice(-4) ); // 'gify'
(2)str.substring(start [, end])
返回字符串在 start 和 end 之间 的部分。
与slice的区别不支持负参数,允许start 大于 end
(3)str.substr(start [, length])
返回字符串从 start 开始的给定 length 的部分。
let str = "stringify";
alert( str.substr(2, 4) ); // 'ring',从位置 2 开始,获取 4 个字符
这个感觉是最不常用的截取多上我直接用 slice 的end- start 不就是多长了吗?
3.数组:
最大子数组:
function getMaxSubSum(arr) {
let maxSum = 0; //声明最大的和
let partialSum = 0; //声明一个数,加上数组的每一个值,并且每次循环中判断加上的值和maxSum进行比较取大值...
for (let item of arr) {
partialSum += item;
maxSum = Math.max(partialSum, maxSum);
if (partialSum<0) partialSum = 0; //取到加上为负值的时候把partialSum重置为0
}
return maxSum;
}
console.log(getMaxSubSum([-1, 2, 3, -9]));
arr.splice 方法可以说是处理数组的瑞士军刀。它可以做所有事情:添加,删除和插入元素。
arr.splice(start[, deleteCount, elem1, ..., elemN])
它从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回已被删除元素的数组。
let arr = ["I", "study", "JavaScript"];
arr.splice(1, 1); // 从索引 1 开始删除 1 个元素
alert( arr ); // ["I", "JavaScript"]
获取删除的元素:
let arr = ["I", "study", "JavaScript", "right", "now"];
// 删除前两个元素
let removed = arr.splice(0, 2);
alert( removed ); // "I", "study" <-- 被从数组中删除了的元素
arr.slice 方法比 arr.splice
简单得多。
arr.slice([start], [end])
它会返回一个新数组,将所有从索引 start 到 end(不包括 end)的数组项复制到一个新的数组。start 和 end 都可以是负数,在这种情况下,从末尾计算索引。
let arr = ["t", "e", "s", "t"];
alert( arr.slice(1, 3) ); // e,s(复制从位置 1 到位置 3 的元素)
alert( arr.slice(-2) ); // s,t(复制从位置 -2 到尾端的元素)
arr.split
将字符串以分隔符分隔为数组
let names = 'Bilbo, Gandalf, Nazgul';
let arr = names.split(', ');
console.log(arr); //[ 'Bilbo', 'Gandalf', 'Nazgul' ]
arr.join(glue)将数组转换为以分隔符分隔的字符串,。
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // 使用分号 ; 将数组粘合成字符串
alert( str ); // Bilbo;Gandalf;Nazgul
累加器:
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((acc, curr) => acc + curr, 0);
console.log(result);
thisArg:
let army = {
minAge: 18,
maxAge: 27,
canJoin(user) {
return user.age >= this.minAge && user.age <= this.maxAge;
}
};
let users = [
{age: 16},
{age: 20},
{age: 23},
{age: 30},
];
// let soldiers = users.filter(user => army.canJoin(user));
let soldiers = users.filter(army.canJoin, army);
console.log(soldiers);
随机排列数组
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
// let count = {
// '123': 0,
// '132': 0,
// '213': 0,
// '231': 0,
// '321': 0,
// '312': 0
// };
//
// for (let i = 0; i < 1000000; i++) {
// let array = [1, 2, 3];
// shuffle(array);
// count[array.join('')]++;
//
// }
//
// for (let key in count) {
// console.log(`${key}: ${count[key]}`);
// }
let array = [1, 2, 3];
shuffle(array);
console.log(array);
从数组创建键值对象:
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(arr) {
let obj = {};
return arr.reduce((acc, curr) => {
obj[curr.id] = curr;
return obj;
},{});
}
let usersById = groupById(users);
console.log(usersById);
// {
// john: { id: 'john', name: 'John Smith', age: 20 },
// ann: { id: 'ann', name: 'Ann Smith', age: 24 },
// pete: { id: 'pete', name: 'Pete Peterson', age: 31 }
// }
4.对象
实现对象的可迭代
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
this.current = this.from;
return this;
},
next() {
if (this.current <= this.to) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
for (let num of range) {
alert(num); // 1, 然后是 2, 3, 4, 5
}
Array.from
有一个全局方法 Array.from 可以接受一个可迭代或类数组的值,并从中获取一个“真正的”数组。然后我们就可以对其调用数组方法了。
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World(pop 方法有效)
总结:
可以应用 for..of
的对象
被称为 可迭代的
。
技术上来说,可迭代对象必须实现 Symbol.iterator
方法。
obj [Symbol.iterator]()
的结果被称为 迭代器(iterator)。由它处理进一步的迭代过程。
一个迭代器必须有next()
方法,它返回一个 {done: Boolean, value: any} 对象,这里 done:true 表明迭代结束,否则 value 就是下一个值。
Symbol.iterator
方法会被 for..of 自动调用,但我们也可以直接调用它。
内置的可迭代对象例如字符串和数组,都实现了 Symbol.iterator。
字符串迭代器能够识别代理对(surrogate pair)
。(译注:代理对也就是 UTF-16 扩展字符。)
有索引属性
和 length
属性的对象被称为 类数组对象。这种对象可能还具有其他属性和方法,但是没有数组的内建方法。
如果我们仔细研究一下规范 —— 就会发现大多数内建方法都假设它们需要处理的是可迭代对象或者类数组对象,而不是“真正的”数组,因为这样抽象度更高。
Array.from(obj[, mapFn, thisArg])
将可迭代对象或类数组对象 obj 转化为真正的数组 Array,然后我们就可以对它应用数组的方法。可选参数 mapFn 和 thisArg 允许我们将函数应用到每个元素。
对象运用:
(1)对象的属性值进行
将对象转换为可迭代对象,然后使用map进行函数运算,然后在转回对象
let prices = {
banana: 1,
orange: 2,
meat: 4
};
let doublePrice = Object.fromEntries(
Object.entries(prices).map(([key, value]) => [key, value * 2])
);
console.log(doublePrice); //{ banana: 2, orange: 4, meat: 8 }
console.log(prices); //{ banana: 1, orange: 2, meat: 4 }
(2)计算对象所有属性值的和
let salaries = {
"John": 100,
"Pete": 300,
"Mary": 250
};
/**
* @param {{[p: string]: T}} salaries
*/
function sumSalaries(salaries) {
return Object.values(salaries).reduce((acc, curr) => acc + curr, 0);
}
console.log(sumSalaries(salaries));
let order = {
banana: {
price: 10
},
apple: {
price: 20
}
};
Object.entries(order).forEach(([key, value]) => {
console.log(value);
});
//使用 数组解构
//for of 必须是一个可迭代的对象,这个一般有数组,set,map,或者字符串,对象必须使用key,value,entries来转换成可迭代的才可以使用for of
for (const [key,value] of Object.entries(order)) {
console.log(value);
}
// { price: 10 }
// { price: 20 }
(3)计算对象实体中值最大的值,返回值最大的实体的属性名:
let salaries = {
"John": 100,
"Pete": 300,
"Mary": 250
};
/**
*
* @param salaries{object}
*/
function topSalary(salaries) {
return Object.entries(salaries)
.reduce(
(acc, curr) => curr[1] > acc[1] ? curr : acc, [null, 0]
)[0];
}
console.log(topSalary(salaries));
5.Map and Set(映射和集合)
Map 是一个带键的数据项的集合,就像一个 Object
一样。 但是它们最大的差别是 Map
允许任何类型的键(key)。
map的键不会被转换为字符串,键可以是任何类型。
使用对象
作为键是 Map 最值得注意和重要的功能之一。对于字符串键,Object(普通对象)也能正常使用,但对于对象键则不行。
每一次 map.set 调用都会返回 map 本身,所以我们可以进行“链式”调用:
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
map迭代:
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// 遍历所有的键(vegetables)
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// 遍历所有的值(amounts)
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// 遍历所有的实体 [key, value]
for (let entry of recipeMap) { // 与 recipeMap.entries() 相同
alert(entry); // cucumber,500 (and so on)
}
从已有普通对象创建map
let obj = {
name: "John",
age: 20
};
let map = new Map(Object.entries(obj));
console.log(map);
从map创建对象:
Object.fromEntries 方法的作用是相反的:给定一个具有 [key, value] 键值对的数组,它会根据给定数组创建一个对象:
let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);
// 现在 prices = { banana: 1, orange: 2, meat: 4 }
alert(prices.orange); // 2
Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。
Set是可迭代的,我们可以使用 for..of 或 forEach 来遍历 Set:
let set = new Set(["oranges", "apples", "bananas"]);
for (let value of set) alert(value);
// 与 forEach 相同:
set.forEach((value, valueAgain, set) => {
alert(value);
});
使用set去重:
function unique(arr) {
return Array.from(new Set(arr));
}
let values = ["Hare", "Krishna", "Hare", "Krishna",
"Krishna", "Krishna", "Hare", "Hare", ":-O", 10
];
let result = unique(values);
console.log(result);
所有的集合对象(数组,Map,Set)和字符串都是可迭代对象
以let set = new Set([1,2,3]); 为例
转数组除了Array.from(set) 还可以用展开运算符[...set]
6.Symbol简述:
Symbol 是唯一标识符,7大基本类型之一。
Symbol 不会被自动转换为字符串
如果我们真的想显示一个 Symbol,我们需要在它上面调用 .toString(),如下所示:
let id = Symbol("id");
alert(id.toString()); // Symbol(id),现在它有效了
Symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
let id = Symbol("id");
let user = {
name: "John",
id: 1,
[id]: 123 // 而不是 "id":123
};
Symbol 属性不参与 for..in 循环。
Object.keys(user) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。
相反,Object.assign 会同时复制字符串和 symbol 属性:
7.对象原始值转换。
let user = {
name: "John",
money: 1000,
[Symbol.toPrimitive](hint) {
console.log(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.money;
}
};
// 转换演示:
console.log(String(user)); // hint: string -> {name: "John"}
console.log(+user); // hint: number -> 1000
console.log(user + 500); // hint: default -> 1500
如果没有 Symbol.toPrimitive,那么 JavaScript 将尝试找到它们,并且按照下面的顺序进行尝试:
对于 “string” hint,toString -> valueOf。
其他情况,valueOf -> toString。
let user = {
name: "John",
money: 1000,
// 对于 hint="string"
toString() {
return `{name: "${this.name}"}`;
},
// 对于 hint="number" 或 "default"
valueOf() {
return this.money;
}
};
console.log(String(user)); // toString -> {name: "John"}
console.log(+user); // valueOf -> 1000
console.log(user + 500); // valueOf -> 1500
转换算法:
调用 obj[Symbol.toPrimitive](hint)
如果这个方法存在,
否则,如果 hint 是 "string"
尝试 obj.toString() 和 obj.valueOf(),无论哪个存在。
否则,如果 hint 是 "number" 或者 "default"
尝试 obj.valueOf() 和 obj.toString(),无论哪个存在