Skip to main content

《JavaScript 悟道》 笔记

JavaScript悟道 - 图灵社区

2 数值​

  • Math.round 用于四舍五入小数, 返回一个整数. (单词round用作动词, 表示 绕过;拐过;使成圆形;把…四舍五入)
  • Math.ceil 返回一个比传入参数大的整数. (单词ceil, 作为动词, 表示 在…装天花板;在…装壁板;在(木船)上装船底隔板)
  • Math.floor 返回的一个恰比传入参数小的整数
  • Math.trunc 返回的则是恰比传入参数更接近0的整数
note

Math.floor 和 Math.trunc 都是将数转换为整数的函数。不同的是,Math.floor 返回的 是一个恰比传入参数小的整数,而 Math.trunc 返回的则是恰比传入参数更接近 0 的整数。

Math.round(3.3) // 3
Math.round(-3.3) // -3
Math.round(3.7) // 4
Math.round(-3.7) // -4

Math.ceil(3.4) // 4
Math.ceil(-3.4) // -3

Math.floor(2.5) // 2
Math.floor(-2.5) // -3

Math.trunc(2.5) // 2
Math.trunc(-2.5) // -2

6 布尔类型​

JavaScript 无法精确表示绝大多数小数,0.1 就是其中之一, 比如 0.1 + 0.2 返回是 0.30000000000000004, 所以除非你肯定值的大小在安全整数范围内,否则不要通过 === 的条件判断来结束循环。即使在安全整数范围内,也还是推荐使用 >= 。

任意数值与 NaN 进行任意比较都会返回 false 。

7 数组​

7.3 栈与队列​

  • pop 方法返回数组中的最后一个元素,并将其从数组中移除
  • push 方法则将传入的值附加到数组的末尾
  • shift 方法移除并返回的是数组中的第零个元素
  • unshift 方法将元素插入数组的开头
note

就性能来说, shift 和 unshift 比 pop 和 push 差很多, 这种差异在数组很大的时候尤为明显。当我们将 shift 和 push 一起使用的时候,数组就会像一个队列——先进先出。

7.6 遍历​

every用于测试数组中所有元素是否通过了测试(return truthy), 接受一个回调函数(签名如下).

  • 如果回调函数返回 falsy 值(未通过测试), 则停止遍历, 并且 every 函数返回 false.
  • 如果回调函数返回 truthy 值(通过测试), 则继续遍历, 如果全部遍历都返回 truthy, 则 every 函数返回 true.
Array.prototype.every = function(element, index, array) {
if(break) {
return false;
}
if(continue) {
return true;
}
}

some 方法与 every 类似。用于测试数组中至少有一个通过了测试(return truthy). 区别是 some 的回调函数返回 truthy 停止遍历(表示有一个通过了测试), 返回 falsy 继续遍历(表示未通过测试).

find 方法与 some 类似,只不过返回值不同, some(包括 every)返回值都是 true或者false. 而find的遍历函数返回truthy的时候,直接返回当前处理的元素。

findIndex 方法与 find 类似,只不过返回的不是一个元素,而是相应元素的序号。

7.7 排序​

sort 排序方法是原地生效的, 会修改原数组. 也就是说 sort 方法无法对冻结数组进行排序, 而且对共享型数组(shared array)进行排序操作也不安全。

sort的回调函数在比较大小的时候,如果认为第一个参数应当排在第二个参数前面,则自定义比较函数需要返回一个负数; 如果第二个参数应当排在前面,则需要返回正数;如果自定义比较函数返回0,则表示比较函数无法判断孰大孰小。

warning

sort 方法还有一个比较严重的问题,那就是缺乏稳定性。在比较两个相等值的时候(比较函数会返回 0),如果排序方法将这两个值排在它们原来的位置上,则可以说该排序方法是稳定的。然而 JavaScript 的 sort 方法不具备这种稳定性。尽管这种稳定性对于排序字符串或者数值数组的时候并不那么重要,但当我们需要对一个存储对象或者数组的数组进行排序的时候,就另当别论了。想象一下,你正在对姓名进行排序:先判断姓的顺序,若姓相同再判断名的顺序。如果 sort 方法稳定,我们就可以这么做:先对姓排一遍序,结束后再对名排一遍序。可惜的是 JavaScript 并不具备该稳定性,第二遍排序会把第一遍已经排好的姓打乱。

7.9 数组方法的纯度​

不会修改数组本身的方法

  • concat
  • every
  • filter
  • find
  • findIndex
  • forEach
  • indexOf
  • join
  • lastIndexOf
  • map
  • reduce
  • reduceRight
  • slice
  • some

会修改数组本身的方法

  • fill
  • pop
  • push
  • shift
  • splice
  • unshift
  • reverse
  • sort

8 对象​

8.3 继承​

Object.create(prototype) 可以将一个已有的对象继承到一个新对象中。该已有对象将作为新对象的原型。

如果我们访问在某一个对象上不存在的属性,JavaScript 会在返回 undefined 之前尝试在该对象的原型上寻找对应属性,那么该属性就会被返回,好像它就在我们正在访问的对象上一样。

当我们对一个对象的内容赋值的时候,被修改的只是原型链顶端的对象(包括delete),原型链中间的属性并不会被修改:

const parent = {age: 10};
const child = Object.create(parent);

child.age += 1;
console.log(child.age); // 11
delete child.age;
console.log(child.age); // 10

JavaScript 对象有自有属性(own property)和来自原型链的继承属性(inherited property). 大多数对象继承了 hasOwnProperty(string) 方法可以获取自有属性,然而这个方法其实有可靠性风险, 可以被用户代码覆盖.

JavaScript 的继承模式还有个问题,那就是意外继承。举例来说,你可能想将对象作为哈希表(字典)来用,但是它又继承了 toString 和constructor 这些属性,以及一些其他有可能继承的属性。这很可能与你自己想赋予的各种属性混淆。可以使用 Object.create(null) 创建一个“纯”对象,该对象没有任何继承属性。

8.4 键名​

Object.keys(object) 函数会将传入对象的所有自有属性(不包括继承属性)的键名作为字符串放入一个数组中并返回。键名在数组中的顺序是按属性的插入时间来排列的。

8.5 冻结​

Object.freeze(object) 可以将对象冻结,使其成为不可变(immutable)对象。

Object.freeze(object) 和 const 表达式做的是完全不同的事。 Object.freeze 作用于值,而 const 则作用于变量。如果将可变对象赋值给一个 const 变量(常量),你还是可以改变这个对象里的内容,但是无法为这个变量赋另一个值。如果将不可变对象赋值给一个普通的变量,那么你将不能改变变量里的内容,但是可以为变量赋另一个值。

8.6 莫使冻结共原型​

如果对象原型中的一个属性是不可变的,那么该对象就无法拥有同名的自有属性了。修改从冻结原型继承过来的属性会触发异常。此外,这种形式的继承会降低插入新属性的性能。每当我们插入新属性的时候,系统会在整条原型链中搜索是否存在该不可变属性。

经过测试发现, 修改冻结原型集成过来的属性不会触发异常, 只是修改不会成功, 比如以下代码:

const parent = Object.freeze({ age: 10 });
const child = Object.create(parent);

child.age += 1; // 这个地方并不会抛异常
console.log(child.age); // 10

9 字符串​

我们可以通过 String.fromCharCode([...]) 函数来创建字符串。字符串中的元素可以通过 charCodeAt 访问.

startsWith 、 endsWith 和 contains 三个方法是对 indexOf 和 lastIndexOf 这两个方法的包装。