Skip to main content

《JavaScript 悟道》 笔记

JavaScript悟道 - 图灵社区

2 数值

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

Math.floorMath.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

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

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,则表示比较函数无法判断孰大孰小。

danger

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 的继承模式还有个问题,那就是意外继承。举例来说,你可能想将对象作为哈希表(字典)来用,但是它又继承了 toStringconstructor 这些属性,以及一些其他有可能继承的属性。这很可能与你自己想赋予的各种属性混淆。可以使用 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 访问.

startsWithendsWithcontains 三个方法是对 indexOflastIndexOf 这两个方法的包装。