为逻辑非操作符,可以应用于ECMAScript中的任何值

  因为球是圆的,所以不论发生什么都有可能,对这点我是深信不疑的,但最近我总是在怀疑,JavaScript也是圆的!

  什么是“黑话”

  黑话,本指旧时江湖帮会人物的暗语、暗号,往往见于小说,后指流行于某一特殊行业中,非局外人所能了解的语言。而本文涉及到的“黑话”,其实是一些利用语言的特征使用的一些不常见的奇淫技巧,JavaScript的语法是十分简单灵活的,在项目中建议大家遵从ESLint规范编写可维护性的代码,各路神仙们也应该进行自我约束,毕竟“黑话”也并不全是什么好的东西,如果很多话可以直接讲,何必拐弯抹角的去说呢?

  “算术”

  算术中的位运算已被作者列为禁术,因此希望你在工程中使用位运算时,请确保你有充足的理由使用,并在需要时写好Hack注释。

  !与!!

  !为逻辑非操作符,可以应用于ECMAScript中的任何值,无论这个值是什么类型,它会被强制转化为一个布尔值变量,再对其值取反。

  !!只是单纯的将操作数执行两次逻辑非,它能将任意类型的值转化为相应的布尔值,它包含的步骤为:

  假设你需要通过一个布尔型变量表示是否有id值,以下写法推荐你使用最后一种方式来进行转化:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const enable1 = !!id;
  const enable2 = id ? true : false;
  const enable3 = Boolean(id);
</pre></p>
  ~ 与 ~~

  ~表示按位取反,~5的运行步骤为:

  至于原码、反码、补码原理请看原理篇。

  ~~它代表双非按位取反运算符,如果你想使用比Math.floor()更快的方法,那就是它了。需要注意,对于正数,它向下取整;对于负数,向上取整;非数字取值为0,它具体的表现形式为:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>~~null; // => 0
  ~~undefined; // => 0
  ~~Infinity; // => 0
  --NaN; // => 0
  ~~0; // => 0
  ~~{}; // => 0
  ~~[]; // => 0
  ~~(1/0); // => 0
  ~~false; // => 0
  ~~true; // => 1
  ~~1.9; // => 1
  ~~-1.9; // => -1
</pre></p>
  +

  在变量值前使用+的本意是将变量转换为数字,在一个函数接受数字类型的参数时特别有用:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>+'1' // 1
  +'-1' // '-1
  +[] // 0
  +{} // NaN
</pre></p>
  根据观察,+a与a * 1结果类似。除此之外,使用+也可以作为立即执行函数:+function() {}()js 截取字符串长度,等效于(function(){})()。

  字符串与数字相加时会将数值默认转为字符串,因此有了一下将数字转为字符串的快捷方法:'' + 1。

  & 与 &&

  如何你是从类C语言过来的话,请抛弃之前的刻板印象:&可以充当逻辑操作符号。在JavaScript中,&只能进行位运算。

  js截取最后一个字符_js截取字符串中的字符_js 截取字符串长度

  &,它表示按位与,此运算符需要两个数字并返回一个数字。如果它们不是数字,则会转换为数字。如果执行7 & 3, 则会经过以下步骤:

  它也可用于基偶数判断:const isOdd = num => !!(num & 1);

  &&,表示逻辑与,通常用于if条件判断,可跟你想象的不太一样,&&并不是单纯的返回true或者false,而是依据:

  在这里举几个例子:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>0 && false 0 (both are false-y, but 0 is the first)
  true && false false (second one is false-y)
  true && true true (both are true-y)
  true && 20 20 (both are true-y)
</pre></p>
  &&可以连接多个操作符,如:a && b && c && d,返回值的规则与上面一样。除此以外,它还经常被作为短路逻辑使用:若前面表达式不是truthy,则不会继续执行之后的表达式。如在取一个对象的属性,我们需要先判断是否为空才能进行取值,否则会抛出Uncaught TypeError,这种情况下一般我们也会通过逻辑或,给与表达式一个默认值:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const value = obj && obj.value || false
</pre></p>
  当JavaScript压缩工具遇到if判断时,也会使用&&短路逻辑从而节省内存空间:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>// before
  if (test) { alert('hello') }
  // after
  test && alert('hello')
</pre></p>
  | 与 ||

  它们与&和&&使用方法很相似,不同的是它们表示的是逻辑或,因此使用|会进行按位或运算,而||会返回第一个Truthy值。

  使用||进行默认值赋值在JavaScript中十分常见,这样可以省略很多不必要的if语句,比如:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>// before
  let res;
  if (a) {
   res = a;
  } else if (b) {
   res = b;
  } else if (c) {
   res = c;
  } else {
   res = 1;
  }
  
  // after
  const res = a || b || c || 1;
</pre></p>
  == 与 ===

  ==为相等运算符,操作符会先将左右两边的操作数强制转型,转换为相同的操作数,再进行相等性比较。

  ===为全等运算符,它除了在比较时不会将操作数强制转型,其余相等判断与==一致。

  简单而言, ==用于判断值是否相等, ===判断值与类型是否都相等,因此使用全等运算符判断操作数会更准确,新手也在学习JavaScript接收到的前几条Tips就是避免使用相等运算符,真的是这样吗?没错,这样能确保在你不彻底熟悉语言的情况下,尽可能的去避免犯错,但是我们也应该清楚在哪些情况下应该使用相等运算符,规则往往只针对于新手,而对聪明的你来说,最重要的是要清楚自己在做什么。

  相等操作符对于不同类型的值,进行的比较如下图所示:

  js截取最后一个字符_js 截取字符串长度_js截取字符串中的字符

  针对于undefined与null:undefined与null互等,与其余任意对象都不相等,因此在某些lib里,你可能会看到如下写法:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>if (VAR == undefined) {}
  if (VAR == null) {}
</pre></p>
  它等效于:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>if (VAR === undefined || VAR === null) {}
</pre></p>
  对于 '', false, 0而言,他们都属于Falsy类型,通过Boolean对象都会转换为假值,而通过==判断三者的关系,他们总是相等的,因为在比较值时它们会因为类型不同而都被转换为false值:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>console.log((false == 0) && (0 == '') && ('' == false)) // true
</pre></p>
  或者有时候我们希望利用强转特性比较字符串与数字:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>console.log(11 == '11') // true
  console.log(11 === '11') // false
</pre></p>
  ^

  按位异或运算符,对比每一个比特位,当比特位不相同时则返回1,否则返回0。很少人在Web开发中使用此运算符吧,除了传说中的一种场景:交换值。

  若要交换a与b的值,如果可以的话推荐你使用:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>[a, b] = [b, a];
</pre></p>
  或者新建一个c,用于存储临时变量,如果你遇到有人这样书写:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>// 异或运算,相同位取0,不同位取1,a ^ b ^ b = a, a ^ a ^ b = b
  a = a ^ b
  b = a ^ b
  a = a ^ b
</pre></p>
  这样通过异或运算进行交换两个数字型变量,请原谅他并忽视它,他只可能是一个醉心于魔法的初心者,并祝愿他早日发现,简洁易读的函数才是最佳实践。

  数值表示法3e9

  科学计数法是一种数学术语,将一个数表示为a乘以10的n次方,如光速30万公里每秒,在计算中通常将米做单位,则记为:300000000m/s,而在JavaScript中我们可使用科学计数法 3e9表示。

  在这里举几个科学计数法的示例:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>1e5; // 100000
  2e-4; // 0.0002
  -3e3; // -3000
</pre></p>
  Number对象有toExponential(fractionDigits)方法以科学计数法返回该数值的字符串表示形式,参数fractionDigits可选,用于用来指定小数点后有几位数字,例如:(179000).toExponential(); // "1.79e+5"。

  以下情况JavaScript会自动将数值转为科学计数法表示:

  .5px

  通常某些人习惯省略0.开头的数字,常见于数值计算、css属性中,比如0.5px可直接写为.5px,0.2 0.3可写为:.2 .3

  0x、0o和0b

  在十进制的世界里呆久了,请不要忘记还有其他进制的存在,在计算机中它们是同地位的。JavaScript提供了以下进制的表示方法:

  默认情况下,JavaScript 内部会自动将八进制、十六进制、二进制转为十进制再进行运算。从十进制转其他进制请查阅toString方法,从其他进制转十进制请查阅parseInt方法,从其他进制转其他进制请先转为十进制再转为其他方法。

  “话术”Array.prototype.sort

  Array.prototype.sort()默认根据字符串的Unicode编码进行排序,具体算法取决于实现的浏览器,在v8引擎中,若数组长度小于10则使用从插入排序,大于10使用的是快排。

  而sort支持传入一个compareFunction(a, b)的参数,其中a、b为数组中进行比较的两个非空对象(所有空对象将会排在数组的最后),具体比较规则为:

  因此利用sort即可写一个打乱数组的方法:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>[1,2,3,4].sort(() => .5 - Math.random())
</pre></p>
  但是以上的实现并不是完全随机的,究其原因,还是因为排序算法的不稳定性,导致一些元素没有机会进行比较,具体请参考问题,在抽奖程序中若要实现完全随机,请使用 Fisher–Yates shuffle 算法,以下是简单实现:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>function shuffle(arrs) {
   for (let i = arrs.length - 1; i > 0; i -= 1) {
   const random = Math.floor(Math.random() * (i + 1));
   [arrs[random], arrs[i]] = [arrs[i], arrs[random]];
   }
  }
</pre></p>
  Array.prototype.concat.apply

  apply接收数组类型的参数来调用函数,而concat接收字符串或数组的多个参数,因此可使用此技巧将二维数组直接展平:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>Array.prototype.concat.apply([], [1, [2,3], [4]])
</pre></p>
  而通过此方法也可以写一个深层次遍历的方法:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>function flattenDeep(arrs) {
   let result = Array.prototype.concat.apply([], arrs);
   while (result.some(item => item instanceof Array)) {
   result = Array.prototype.concat.apply([], result);
   }
   return result;
  }
</pre></p>
  经过测试,效率与lodash对比如下:

  js截取最后一个字符_js 截取字符串长度_js截取字符串中的字符

  对上述方法中的Array.prototype.concat.apply([], target)亦可以写成:[].concat(...target)。

  Array.prototype.push.apply

  在es5中,若想要对数组进行拼接操作,我们习惯于使用数组中的concat方法:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>let arrs = [1, 2, 3];
  arrs = arrs.concat([4,5,6]);
</pre></p>
  但还有酷的方法,利用apply方法的数组传参特性,可以更简洁的执行拼接操作:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const arrs = [1, 2, 3];
  arrs.push.apply(arrs, [4, 5, 6]);
</pre></p>
  Array.prototype.length

  js 截取字符串长度_js截取字符串中的字符_js截取最后一个字符

  它通常用于返回数组的长度,但是也是一个包含有复杂行为的属性,首先需要说明的是,它并不是用于统计数组中元素的数量,而是代表数组中最高索引的值:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const arrs = [];
  arrs[5] = 1;
  console.log(arrs.length); // 6
</pre></p>
  另外,length长度随着数组的变化而变化,但是这种变化仅限于:子元素最高索引值的变化,假如使用delete方法删除最高元素,length是不会变化的,因为最高索引值也没变:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const arrs = [1, 2, 3];
  delete arrs[2]; // 长度依然为3
</pre></p>
  length还有一个重要的特性,那就是允许你修改它的值,若修改值小于数组本身的最大索引,则会对数组进行部分截取:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const arrs = [1, 2, 3, 4];
  arrs.length = 2; // arrs = [1, 2]
  arrs.length = 0; // arrs = []
</pre></p>
  若赋予的值大于当前最大索引,则会得到一个稀疏数组:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const arrs = [1, 2];
  arrs.length = 5; // arrs = [1, 2,,,,]
</pre></p>
  若将值赋为0,则执行了清空数组的操作:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const arrs = [1, 2, 3, 4];
  arrs.length = 0; // arrs = []
</pre></p>
  使用此方法会将数组中的所有索引都删除掉,因此也会影响其他引用此数组的值,这点跟使用arrs = []有很大的区别:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>let a = [1,2,3];
  let b = [1,2,3];
  let a1 = a;
  let b1 = b;
  a = [];
  b.length = 0;
  console.log(a, b, a1, b1); // [], [], [1, 2, 3], []
</pre></p>
  在对length进行修改的时候,还需要注意:

  Object.prototype.toString.call

  每个对象都有一个toString(),用于将对象以字符串方式引用时自动调用,如果此方法未被覆盖,toString则会返回[object type],因此Object.prototype.toString.call只是为了调用原生对象上未被覆盖的方法,call将作用域指向需要判断的对象,这样一来就可以通过原生的toString方法打印对象的类型字符串:Object.prototype.toString.call([]) => "[object Array]" ,利用这个特性,可以较为精确的实现类型判断。

  在ES3中,获取到的type为内部属性[[Class]]属性,它可以用来判断一个原生属性属于哪一种内置的值;在ES5中新增了两条规则:若this值为null、undefined分别返回:[object Null]、[object Undefined];在ES6中不存在[[Class]]了,取而代之的是一种内部属性:[[NativeBrand]],它是一种标记值,用于区分原生对象的属性,具体的判断规则为:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>19.1.3.6Object.prototype.toString ( )
  When the toString method is called, the following steps are taken:
  
  If the this value is undefined, return "[object Undefined]".
  If the this value is null, return "[object Null]".
  Let O be ! ToObject(this value).
  Let isArray be ? IsArray(O).
  If isArray is true, let builtinTag be "Array".
  Else if O is a String exotic object, let builtinTag be "String".
  Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
  Else if O has a [[Call]] internal method, let builtinTag be "Function".
  Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
  Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
  Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
  Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
  Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
  Else, let builtinTag be "Object".
  Let tag be ? Get(O, @@toStringTag).
  If Type(tag) is not String, set tag to builtinTag.
  Return the string-concatenation of "[object ", tag, and "]".
  This function is the %ObjProto_toString% intrinsic object.
  
  NOTE
  Historically, this function was occasionally used to access the String value of the [[Class]] internal slot that was used in previous editions of this specification as a nominal type tag for various built-in objects. The above definition of toString preserves compatibility for legacy code that uses toString as a test for those specific kinds of built-in objects. It does not provide a reliable type testing mechanism for other kinds of built-in or program defined objects. In addition, programs can use @@toStringTag in ways that will invalidate the reliability of such legacy type tests.
</pre></p>
  Object.create(null)

  用于创建无“副作用”的对象,也就是说,它创建的是一个空对象,不包含原型链与其他属性。若使用const map = {}创建出来的对象相当于Object.create(Object.prototype)js 截取字符串长度,它继承了对象的原型链。

  JSON.parse(JSON.stringify(Obj))

  很常用的一种深拷贝对象的方式,将对象进行JSON字符串格式化再进行解析,即可获得一个新的对象,要注意它的性能不是特别好,而且无法处理闭环的引用,比如:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>const obj = {a: 1};
  obj.b = obj;
  JSON.parse(JSON.stringify(obj)) // Uncaught TypeError: Converting circular structure to JSON
</pre></p>
  js截取字符串中的字符_js截取最后一个字符_js 截取字符串长度

  这样通过JSON解析的方式其实性能并不高,若对象可通过浅拷贝复制请一定使用浅拷贝的方式,不管你使用{...obj}还是Object.assign({}, obj)的方式,而如果对性能有要求的情况下,请不要再造轮子了,直接使用npm:clone这个包或是别的吧。

  “理论”Truthy与Falsy

  对每一个类型的值来讲,它每一个对象都有一个布尔型的值,Falsy表示在Boolean对象中表现为false的值,在条件判断与循环中,JavaScript会将任意类型强制转化为Boolean对象。以下这些对象在遇到if语句时都表现为Falsy:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>if (false)
  if (null)
  if (undefined)
  if (0)
  if (NaN)
  if ('')
  if ("")
  if (document.all)
</pre></p>
  document.all属于历史遗留原因,所以为false,它违背了JavaScript的规范,可以不管它,而NaN这个变量,千万不要用全等或相等对其进行判断,因为它发起疯来连自己都打:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>console.log(NaN === 0) // false
  console.log(NaN === NaN) // false
  console.log(NaN == NaN) // false
</pre></p>
  但是我们可以使用Object.is方法进行判断值是否为NaN,它是ES6新加入的语法,用于比较两个值是否相同,它可以视为比全等判断符更为严格的判断方法,但是不可混为一谈:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>Object.is(NaN, NaN) // true
  Object.is(+0, -0) // false
</pre></p>
  而除了Falsy值,所有值都是Truthy值,在Boolean上下文中表现为true。

  原码, 反码, 补码

  在JavaScript进行位运算时,采用32位有符号整型,即数字5有以下表示方式:

  而数字-5的表示方式为:

  综上所述,有以下规律:

  那么它们到底有什么用呢?其实位运算就是用计算机底层电路所有运算的基础,为了让计算机的运算更加简单,而不用去辨别符号位,所有值都采用加法运算,因此,人们设计了原码,通过符号位来标识数字的正负:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>1 = 0000 0001
  -1 = 1000 0001
</pre></p>
  假如计算机要对两个数相加:1 + (-1),使用原码相加的运算结果为:10000010,很明显-2并不是我们想要的结果,因此出现了反码,若使用反码进行运算会有什么结果呢,让我们来看一下:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>1[反码] + (-1)[反码] = 0000 0001 + 1111 1110 = 11111111[反码] = 10000000[原码]
</pre></p>
  此时运算结果是正确的,可是这样还存在一个问题,有两个值可以表示0:1000 0000、0000 0000,对于计算机来说,0带符号是没有任何意义的,人们为了优化0的存在,设计出了补码:

  <pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box;padding-top: 8px;padding-bottom: 6px;background: rgb(241, 239, 238);border-radius: 0px;overflow-y: auto;color: rgb(80, 97, 109);text-align: start;font-size: 10px;line-height: 12px;font-family: consolas, menlo, courier, monospace, &#39;Microsoft Yahei&#39;!important;border-width: 1px !important;border-style: solid !important;border-color: rgb(226, 226, 226) !important;"><p>1[补码] + (-1)[补码] = 0000 0001 + 1111 1111 = 00000000[原码]
</pre></p>
  这样一来,-0的问题就可以解决了。

  参考资料

  https://dmitripavlutin.com/the-magic-behind-array-length-property/https://stackoverflow.com/questions/7310109/whats-the-difference-between-and-in-javascript

  <pre style="letter-spacing: 0.544px;"><p style="letter-spacing: 0.544px;white-space: normal;font-family: 微软雅黑;font-size: 16px;text-align: center;widows: 1;caret-color: rgb(51, 51, 51);color: rgb(63, 63, 63);">本文对你有帮助?请分享给更多人!
  关注「前端速报」,提升前端技能!
  js截取最后一个字符_js 截取字符串长度_js截取字符串中的字符

<p style="letter-spacing: 0.544px;word-spacing: 2px;text-align: right;">我知道你 “在看” [1]: /usr/uploads/images/2023/03/21/1679410865413_0.png [2]: /usr/uploads/images/2023/03/21/1679410865413_1.jpg [3]: /usr/uploads/images/2023/03/21/1679410865413_4.png [4]: /usr/uploads/images/2023/03/21/1679410865413_5.png [5]: /usr/uploads/images/2023/03/21/1679410865413_6.jpg [6]: /usr/uploads/images/2023/03/21/1679410865413_7.jpg 
文章由官网发布,如若转载,请注明出处:https://www.veimoz.com/1717
0 评论
361

发表评论