JS 中10个命名最糟糕但超实用的 API

23,264 阅读8分钟

你觉得JS里命名最糟糕的API有哪些?

大家好,我是不打篮球的LBJ,

前几天,贺老聊到,几个他认为 JS 里命名最糟糕的 API,中奖名单包括:

unshiftsplicesubstrcallee,以及备选的 stickystringifyreduce

(注意,以上排名先后)

另外,也有其他人补充,简单小结如下:

  1. 一直搞不清到底是 array.includes 还是 contains
  2. inhashas-own 那几个太混乱了
  3. for infor 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

很多人将 substrsubstring 搞混,因为他们确实长得太像了,而且作用和用法也类似,都是用于提取子字符串(子串)

作用

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

许多人可能将 calleecaller 搞混,虽然看上去就差了一个字母,但区别非常的大

作用

calleearguments 对象的属性,用于指向拥有这个arguments对象的函数。因此当函数被调用时,它的arguments.callee对象就会指向自身,也就是一个对自己的引用

callerfunction 的属性,用于指向调用当前函数的函数

语法

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. 如果是数字, 序列化时每一层级比上一层级多缩进对应数字值的空格,范围在1-10,即最小1个最大10个空格;
  2. 如果是字符串,序列化时该字符串会添加在每行前面,每一层级比上一层级多缩进该字符串,最多是个字符,超过则截取字符串;
  3. 如果是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 个参数:

  1. accumulator 累计器
  2. current Value 当前值
  3. index 当前索引
  4. 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. inhashas-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..offor..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 关注于迭代对象的值。内置对象MapSet已经实现了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"
}

小结

最后将所有内容小结为下图:

8-3.png

结语

以上就是本文的所有内容啦,如有问题,烦请指正 🌹