你觉得JS
里命名最糟糕的API有哪些?
大家好,我是不打篮球的LBJ,
前几天,贺老聊到,几个他认为 JS
里命名最糟糕的 API
,中奖名单包括:
unshift
、splice
、substr
、callee
,以及备选的sticky
、stringify
、reduce
(注意,以上排名分先后)
另外,也有其他人补充,简单小结如下:
- 一直搞不清到底是
array.includes
还是contains
in
、has
、has-own
那几个太混乱了for in
和for of
对英文基础差的人来说简直没法区分
其实,说糟糕是因为命名不好理解,容易混淆。本文正是要解决这个问题!
本文将深入浅出讲清楚这些容易搞混乱的 API
,共10
个
Top1. unshift
作用
unshift
用于向数组的开头添加一个或多个元素,并返回新的长度
基本语法
array.unshift(item1,item2, ..., itemX)
举例
let fruits = ["Apple", "Banana"];
let len = fruits.unshift("Lemon", "Orange");
console.log(len);//4
console.log(fruits);//['Lemon', 'Orange', 'Apple', 'Banana']
注意
unshift
添加元素是在原数组添加的,因此unshift
会改变原数组
Top2. splice
作用
splice
用于数组中截取并添加元素。添加在切口处添加,添加的新数据在原数组上,返回被截取部分
基本语法
array.splice(从第几位开始, 截取的长度, 在切口处添加的数据1, 数据2, ...);
根据传参的不同,结果也会不同:
- 如果什么都不传,返回空数组[];
- 如果值传第一个,表示从该位置开始,一直截取到数组的最后一位;
- 如果传第一个和第二个参数,表示从该位置开始,一直截取指定位数;
- 如果还传递其他参数,那么从第三位开始的后面参数都会作为原始组的新成员,添加到切口处
举例
let numbersArr = ['a', 'b', 'c', 1, 2, 3]
let strArr = numbersArr.splice(0,3)//从0位开始截取3位,不添加新数据
console.log(numbersArr);//[1, 2, 3]
console.log(strArr);// ['a', 'b', 'c']
注意
- 此方法会改变原数组,除非什么也不传
- 在新的Chrome中,如果什么都不传,会返回空数组[];而老的浏览器,如果什么也不传,结果可能是第o为开始,截取到数组.length-1位,相当于复制数组
Top3. substr
很多人将 substr
和 substring
搞混,因为他们确实长得太像了,而且作用和用法也类似,都是用于提取子字符串(子串)
作用
substr
的作用是从起始索引号提取字符串中指定数目的字符
而 substring
的所用是,提取字符串中两个指定的索引号之间的字符
语法
string.substr(start,length)
- 第一个参数,表示提取子串的起始下标
- 第二个参数,表示提取的字串的长度
举例
let str = "Hello JueJin!"
let hello = str.substr(0,5)
console.log(hello) //Hello
console.log(str)//"Hello JueJin!"
注意
- 此方法仅提取字串,不改变原始字符串
- 如果第二个参数不传,表示提取到最后
Top4. callee
许多人可能将 callee
和 caller
搞混,虽然看上去就差了一个字母,但区别非常的大
作用
callee
是 arguments
对象的属性,用于指向拥有这个arguments对象的函数。因此当函数被调用时,它的arguments.callee对象就会指向自身,也就是一个对自己的引用
而 caller
是 function
的属性,用于指向调用当前函数的函数
语法
function foo(){
//xxx
arguments.callee()
//xxx
}
当函数执行时,调用 arguments.callee()
,相当于递归调用此函数
举例
计算n的阶层
function mul(n){
if(n==0||n==1){
return 1;
}
return n*arguments.callee(n-1);
}
注意
- 在严格模式下不支持使用arguments.callee
Top5. sticky
sticky
我都没用过,它是正则表达式对象的属性,且只读、不能枚举
作用
它的作用是用于反映搜索是否具有粘性。
sticky
的值是布尔值,它在 y
标志使用时为真; 否则为假
举例
const str1 = 'Hello Juejin!';
const regex1 = new RegExp('foo', 'y');
regex1.lastIndex = 6;//指定下一次匹配的起始索引
console.log(regex1.sticky);// true
console.log(regex1.test(str1));// true -- 本例中,仅当 lastIndex = 6 时匹配成功,这就是 sticky 的作用
console.log(regex1.test(str1));// false
更多参考
Top6. stringify
作用
JSON.stringify()
可以将一个JS
对象或值转换为 JSON
格式字符串
语法
JSON.stringify(value[, replacer [, space]])
参数:
-
value
:表示将要序列化成一个JSON
字符串的JS
对象或值 -
replacer
:可选,用于处理将要序列化的值
如果指定了一个 replacer 函数,则可以选择性地替换值,或者指定的 replacer 是数组,则可选择性地仅包含数组指定的属性
space
:可选,指定缩进用的空白字符串,用于美化输出
space
的值可以是以下三种情况:
- 如果是数字, 序列化时每一层级比上一层级多缩进对应数字值的空格,范围在1-10,即最小1个最大10个空格;
- 如果是字符串,序列化时该字符串会添加在每行前面,每一层级比上一层级多缩进该字符串,最多是个字符,超过则截取字符串;
- 如果是null、undefined或其他类型,则被忽略,不做处理
举例
JSON.stringify(333) // '333'
JSON.stringify(true) // 'true'
JSON.stringify(new String('333')) //'"333"'
JSON.stringify(Boolean(true)) // 'true'
JSON.stringify('json') === 'json' // false
JSON.stringify('json') === '"json"' // true
JSON.stringify(Symbol()) // undefined
JSON.stringify([Symbol(), Math.abs, undefined]) // '[null,null,null]'
JSON.stringify({ [Symbol()]: Math.abs, key: undefined }) // '{}'
JSON.stringify(null) // 'null'
JSON.stringify(NaN) // 'null'
const obj = {}
Object.defineProperties(obj, {
'json': { value: 'JSON', enumerable: true },
'stringify': { value: 'STRINGIFY', enumerable: false }
})
JSON.stringify(obj)// '{"json":"JSON"}'
JSON.stringify({[Symbol()]: 333}) // '{}'
const a = { '1': 911, 'r': 822, '11': 9922}
JSON.stringify(a)
// '{"1":911,"11":9922,"r":822}'
const a = { key: 'json' }
a.toJSON = () => 'JSON'
JSON.stringify(a)// '"JSON"'
JSON.stringify(/\d/) // "{}"
JSON.stringify(new Error()) // "{}"
const a = {}
a.key = a
JSON.stringify(a)// Uncaught TypeError: Converting circular structure to JSON
JSON.stringify(12n)// Uncaught TypeError: Do not know how to serialize a BigInt
const a = {'\u0066': 333}
JSON.stringify(a)// '{"f":333}'
JSON.stringify(new Date())//'"2022-04-14T05:18:12.659Z"'
Top7. reduce
为什么 reduce 也在这呢,这里引用贺老的话:
加 reduce 主要是有部分同志提出不喜欢这 API,所以拿来凑数。我个人其实还好,因为换个名字比如 fold 也没好到哪儿去。。
既然有人提到了,那就来说说它
作用
reduce
方法用于对数组中每个元素执行一个由你提供的函数,将其结果汇总为单个返回值
语法
array.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
reduce
接受两个参数:
callback
函数initialValue
初始值
callback
接受 4 个参数:
accumulator
累计器current Value
当前值index
当前索引array
源数组
举例
// 累加
[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => a + i);
// 输出:36
// 累加,默认一个初始值
[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => a + i, 5);
// 输出:41
// 累乘
[1, 2, 3, 4, 5, 6, 7, 8].reduce((a, i) => a * i);
// 输出:40320
Top8. array.includes
有人说一直搞不清是 array.includes
还是 array.contains
,因为两个词都很常见
事实上,数组没有contains
方法,只有 includes
方法
贺老的解释是:
本来叫contains,但是因为兼容性问题只好改名成includes
作用
includes
用于判断数组中是否包含某元素。包含返回true
,不包含返回false
,它与字符串的 includes
方法类似
语法
arr.includes(searchElement)
arr.includes(searchElement, fromIndex)
- 第一个参数表示要查找的元素
- 第二个参数表示搜索的起始位置
举例
let arr = [1,2,3]
let res = arr.includes(1)
console.log(res)//true
注意
- 如果第二个参数为负数,则表示从倒数第几位向后搜索
Top9. in
、has
和has-own
这三个完全是不同的东西:
- 首先来看 in
它常用于两种情况
首先是for in 循环。 for in 用于迭代任何对象,包括数组,但默认情况下迭代的是 key,例如:
let list = [1, 2, 3];
for (let i in list) {
console.log(i);
}
上述代码中,使用for...in
遍历出的是数组的下标"0", "1", "2"
另外一种情况是:变量 in 对象
作用是,用于检测前面的东西是不是后面这个对象的属性(原型上有也算)
- 接着来看 has
常见情况,比如Map.prototype.has()
它的作用是,用于表明 map 中是否存在指定元素,例如:
var myMap = new Map();
myMap.set("bar", "foo");
myMap.has("bar"); // true
myMap.has("baz"); //false
- 最后是
has-own
比如Object.prototype.hasOwnProperty()
,
用于检测某个属性是否属于某个对象,跟in 不同的是,hasOwnProperty
排除了原型上的
const object1 = {};
object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));// true
console.log(object1.hasOwnProperty('toString'));//false
console.log(object1.hasOwnProperty('hasOwnProperty'));// false
Top10. for in 和 for of
for..of
和for..in
均用于迭代,但是它们又有所不同
首先是迭代的值不同
for..in
迭代的是对象的键的列表,而for..of
则迭代对象的键对应的值
举例1
let list = [1, 2, 3];
for (let i in list) {
console.log(i);
}
for (let i of list) {
console.log(i);
}
上述代码中,使用for...in
遍历出的是数组的下标"0", "1", "2"
,而使用for..of
遍历数组得到的就是对应的值"1", "2", "3"
另外一个主要区别是 for in 可以操作任何对象;它提供了查看对象属性的一种方法。
但是 for of 关注于迭代对象的值。内置对象Map
和Set
已经实现了Symbol.iterator
方法,让我们可以访问它们保存的值。
举例2
let pets = new Set(["Cat", "Dog"]);
pets["species"] = "mammals";
for (let pet in pets) {
console.log(pet); // "species"
}
for (let pet of pets) {
console.log(pet); // "Cat", "Dog"
}
小结
最后将所有内容小结为下图:
结语
以上就是本文的所有内容啦,如有问题,烦请指正 🌹