js知识点

数据类型

  • 转换规则是除了下面六个值被转为false,其他值都视为true。
  • undefined null false 0 NaN ""或''(空字符串)
  • 空数组([])和空对象({})对应的布尔值,都是true
  • NaN不等于任何值,包括它本身。NaN与任何数(包括它自己)的运算,得到的都是NaN。
  • Infinity === -Infinity // false, Infinity与NaN比较,总是返回false。
    0乘以Infinity,返回NaN;0除以Infinity,返回0;Infinity除以0,返回Infinity。Infinity减去或除以Infinity,得到NaN。
    Infinity与null计算时,null会转成0,等同于与0的计算。
    Infinity与undefined计算,返回的都是NaN。
  • 与数值相关的全局方法
    1、parseInt方法用于将字符串转为整数。如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。对于那些会自动转为科学计数法的数字,parseInt会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果。
    进制转换 parseInt方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,返回该值对应的十进制数,如果第二个参数是0、undefined和null,则直接忽略。如果字符串包含对于指定进制无意义的字符,则从最高位开始,只返回可以转换的数值。如果最高位无法转换,则直接返回NaN。
    2、parseFloat方法用于将一个字符串转为浮点数。parseFloat会将空字符串转为NaN。
    3、isNaN方法可以用来判断一个值是否为NaN,isNaN为true的值,有可能不是NaN,而是一个字符串。对于对象和数组,isNaN也返回true。
    4、isFinite方法返回一个布尔值,表示某个值是否为正常的数值。除了Infinity、-Infinity、NaN和undefined这几个值会返回false,isFinite对于其他的数值都会返回true。
  • 查看一个对象本身的所有属性,可以使用Object.keys方法。delete命令用于删除对象的属性,删除成功后返回true。需要注意的是,delete命令只能删除对象本身的属性,无法删除继承的属性
  • in运算符用于检查对象是否包含某个属性,可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性
  • with语句作用是操作同一个对象的多个属性,如果with区块内部有变量的赋值操作,必须是当前对象已经存在的属性,否则会创造一个当前作用域的全局变量。
  • arguments可以在函数体内部读取所有参数,正常模式下,arguments对象可以在运行时修改。严格模式下,arguments对象与函数参数不具有联动关系。也就是说,修改arguments对象不会影响到实际的函数参数。arguments对象带有一个callee属性,返回它所对应的原函数。可以通过arguments.callee,达到调用函数自身的目的。这个属性在严格模式里面是禁用的,因此不建议使用。
    将arguments转为真正的数组。下面是两种常用的转换方法
var args = Array.prototype.slice.call(arguments);

// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}
  • 闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以使得它诞生环境一直存在。
  • eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。凡是使用别名执行eval,eval内部一律是全局作用域。
  • Object.keys方法返回数组的所有键名。数组的键名就是整数0、1、2(索引下标)。数组的空位不影响length属性。数组最后一个成员后面有一个逗号,这不影响length属性的值,与没有这个逗号时效果一样。数组的空位是可以读取的,返回undefined。使用delete命令删除一个数组成员,会形成空位,并且不会影响length属性。空位就是数组没有这个元素,所以不会被遍历到,而undefined则表示数组有这个元素,值是undefined,所以遍历不会跳过。
  • 字符串也是类似数组的对象,所以也可以用Array.prototype.forEach.call遍历。这种方法比直接使用数组原生的forEach要慢,所以最好还是先将“类似数组的对象”转为真正的数组,然后再直接调用数组的forEach方法。
var arr = Array.prototype.slice.call('abc');
arr.forEach(function (chr) {
  console.log(chr);
});
// a
// b
// c

运算符

  • 余数运算符(%),运算结果的正负号由第一个运算子的正负号决定。
-1 % 2 // -1
1 % -2 // 1

所以,为了得到负数的正确余数值,可以先使用绝对值函数。

  • 指数运算符是右结合,而不是左结合。即多个指数运算符连用时,先进行最右边的计算。
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
  • 非数值经过数值运算符(+,-)以后,都变成了数值,减法运算符预期左右两侧的运算子应该是数值,如果不是,就会自动将它们转为数值。
  • 与NaN的比较。任何值(包括NaN本身)与NaN比较,返回的都是false。
  • undefined和null与自身严格相等,与其他类型的值比较时,结果都为false。
    undefined === undefined // true
    null === null // true
    由于变量声明后默认值是undefined,因此两个只声明未赋值的变量是相等的。
  • 严格相等运算符有一个对应的“严格不相等运算符”(!==),它的算法就是先求严格相等运算符的结果,然后返回相反值。
  • 且运算符(&&)的运算规则是:如果第一个运算子的布尔值为true,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false,则直接返回第一个运算子的值,且不再对第二个运算子求值。
  • 或运算符(||)的运算规则是:如果第一个运算子的布尔值为true,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false,则返回第二个运算子的值。
  • Number函数将字符串转为数值,要比parseInt函数严格很多。基本上,只要有一个字符无法转成数值,整个字符串就会被转为NaN。
parseInt('42 cats') // 42
Number('42 cats') // NaN

parseInt逐个解析字符,而Number函数整体转换字符串的类型。另外,parseInt和Number函数都会自动过滤一个字符串前导和后缀的空格。
umber方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。

Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
  • Number背后的转换规则比较复杂。
    第一步,调用对象自身的valueOf方法。如果返回原始类型的值,则直接对该值使用Number函数,不再进行后续步骤。
    第二步,如果valueOf方法返回的还是对象,则改为调用对象自身的toString方法。如果toString方法返回原始类型的值,则对该值使用Number函数,不再进行后续步骤。
    第三步,如果toString方法返回的是对象,就报错
  • String方法背后的转换规则,与Number方法基本相同,只是互换了valueOf方法和toString方法的执行顺序。
    一、先调用对象自身的toString方法。如果返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
    二、如果toString方法返回的是对象,再调用原对象的valueOf方法。如果valueOf方法返回原始类型的值,则对该值使用String函数,不再进行以下步骤。
    三、如果valueOf方法返回的是对象,就报错。
  • 所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true
  • null转为数值时为0,而undefined转为数值时为NaN
  • 一元运算符也会把运算子转成数值。
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0

语法

  • Error实例对象是最一般的错误类型,在它的基础上,JavaScript 还定义了其他6种错误对象。也就是说,存在Error的6个派生对象。
    1.SyntaxError对象是解析代码时发生的语法错误。
    2.ReferenceError对象是引用一个不存在的变量时发生的错误。
    3.RangeError对象是一个值超出有效范围时发生的错误。主要有几种情况,一是数组长度为负数,二是Number对象的方法参数超出范围,以及函数堆栈超过最大值。
    4.TypeError对象是变量或参数不是预期类型时发生的错误。比如,对字符串、布尔值、数值等原始类型的值使用new命令,就会抛出这种错误,因为new命令的参数应该是一个构造函数。
    5.URIError对象是 URI 相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数。
    6.eval函数没有被正确执行时,会抛出EvalError错误。该错误类型已经不再使用了,只是为了保证与以前代码兼容,才继续保留。

新增字符串方法

  • String.fromCodePoint()方法,用于从 Unicode 码点返回对应字符,弥补了String.fromCharCode()方法的不足。
  • String.raw()方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。(在所有斜杆前再加一个斜杠)
  • codePointAt()方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。
  • repeat(n)方法返回一个新字符串,表示将原字符串重复n次。参数如果是小数,会被取整。如果repeat的参数是负数或者Infinity,会报错。但是,如果参数是 0 到-1 之间的小数,则等同于 0
  • padStart(),padEnd()用以补全字符串,如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。一共接受两个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

另一个用途是提示字符串格式。

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
  • trimStart()和trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

新增数值方法

  • Number.isFinite()用来检查一个数值是否为有限的(finite),即不是Infinity。如果参数类型不是数值,Number.isFinite一律返回false。
  • Number.isNaN()用来检查一个值是否为NaN。如果参数类型不是NaN,Number.isNaN一律返回false。
  • 它们与传统的全局方法isFinite()和isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()对于非数值一律返回false, Number.isNaN()只有对于NaN才返回true,非NaN一律返回false。
  • Number.parseInt(), Number.parseFloat()
    ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
  • Number.isInteger()用来判断一个数值是否为整数。JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。
  • Number.EPSILON可以用来设置“能够接受的误差范围”。比如,误差范围设为 2 的-50 次方(即Number.EPSILON * Math.pow(2, 2)),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。如果参数不是数值,Number.isInteger返回false。
function withinErrorMargin (left, right) {
  return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2);
}
0.1 + 0.2 === 0.3 // false
withinErrorMargin(0.1 + 0.2, 0.3) // true
1.1 + 1.3 === 2.4 // false
withinErrorMargin(1.1 + 1.3, 2.4) // true
  • Math.trunc方法用于去除一个数的小数部分,返回整数部分。Math.ceil(x)法执行的是向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数。Math.floor() 方法执行的是向下取整计算,它返回的是小于或等于函数参数
  • Math.sign方法用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
  • Math.cbrt方法用于计算一个数的立方根。
  • Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。对于小数,Math.clz32方法只考虑整数部分。
  • Math.imul方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
  • Math.fround方法返回一个数的32位单精度浮点数形式。对于 -224 至 224 之间的整数(不含两个端点),返回结果与参数本身一致。
  • Math.hypot方法返回所有参数的平方和的平方根。
  • Math.expm1(x)返回 ex - 1,即Math.exp(x) - 1。
  • Math.log1p(x)方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。
  • Math.log10(x)返回以 10 为底的x的对数。如果x小于 0,则返回 NaN。
  • 指数运算符(**),这个运算符的一个特点是右结合
  • 新的数据类型 BigInt(大整数)用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。为了与 Number 类型区别,BigInt 类型的数据必须添加后缀n。BigInt 与普通整数是两种值,它们之间并不相等。typeof运算符对于 BigInt 类型的数据返回bigint。
  • JavaScript 原生提供BigInt对象,可以用作构造函数生成 BigInt 类型的数值。BigInt 对象继承了 Object 对象的两个实例方法。
    BigInt.prototype.toString()
    BigInt.prototype.valueOf()
    它还继承了 Number 对象的一个实例方法。
    BigInt.prototype.toLocaleString()
    此外,还提供了三个静态方法。
    BigInt.asUintN(width, BigInt): 给定的 BigInt 转为 0 到 2width - 1 之间对应的值。
    BigInt.asIntN(width, BigInt):给定的 BigInt 转为 -2width - 1 到 2width - 1 - 1 之间对应的值。
    BigInt.parseInt(string[, radix]):近似于Number.parseInt(),将一个字符串转换成指定进制的 BigInt。

箭头函数

  • 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。this对象的指向是可变的,但是在箭头函数中,它是固定的。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
  • 由于箭头函数使得this从“动态”变成“静态”,下面两个场合不应该使用箭头函数。
    第一个场合是定义对象的方法,且该方法内部包括this。
    第二个场合是需要动态this的时候,也不应使用箭头函数。

扩展运算符

  • 扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
  • 扩展运算符取代apply方法
// ES5 的写法
Math.max.apply(null, [14, 3, 77])

// ES6 的写法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);
  • 通过push函数,将一个数组添加到另一个数组的尾部。
// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
  • 复制数组
    数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
  • 合并数组
    扩展运算符提供了数组合并的新写法。
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

不过,这两种方法都是浅拷贝

  • 与解构赋值结合
    扩展运算符可以与解构赋值结合起来,用于生成数组。
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

const [first, ...rest] = [];
first // undefined
rest  // []

const [first, ...rest] = ["foo"];
first  // "foo"
rest   // []

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

  • 字符串
    扩展运算符还可以将字符串转为真正的数组。
  • 实现了 Iterator 接口的对象
    任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。
let nodeList = document.querySelectorAll('div');
let array = [...nodeList];

扩展运算符内部调用的是数据结构的 Iterator 接口,因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如Map 和 Set 结构,Generator 函数

  • Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。
  • Array.of方法用于将一组值,转换为数组。
  • 数组实例的 copyWithin()
    数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数。
target(必需):从该位置开始替换数据。如果为负值,表示倒数。
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

  • 数组实例的findIndex方法的用法与find方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
[1, 5, 10, 15].findIndex(function(value, index, arr) {
  return value > 9;
}) // 2

这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。

function f(v){
  return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person);    // 26
  • fill方法使用给定值,填充一个数组。fill方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
  • 数组实例的 entries(),keys() 和 values(),entries(),keys()和values()用于遍历数组,它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
  • Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。flat()默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()方法的参数写成一个整数,表示想要拉平的层数,默认为1。
  • 数组的空位指,数组的某一个位置没有任何值
    forEach(), filter(), reduce(), every() 和some()都会跳过空位。
    map()会跳过空位,但会保留这个值
    join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
    ES6 则是明确将空位转为undefined
  • 可枚举性
    对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
    目前,有四个操作会忽略enumerable为false的属性。
    for...in循环:只遍历对象自身的和继承的可枚举的属性。
    Object.keys():返回对象自身的所有可枚举的属性的键名。
    JSON.stringify():只串行化对象自身的可枚举的属性。
    Object.assign(): 忽略enumerable为false的属性,只拷贝对象自身的可枚举的属性。
  • 属性的遍历
    ES6 一共有 5 种方法可以遍历对象的属性。
    (1)for...in
    for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
    (2)Object.keys(obj)
    Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
    (3)Object.getOwnPropertyNames(obj)
    Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
    (4)Object.getOwnPropertySymbols(obj)
    Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。
    (5)Reflect.ownKeys(obj)
    Reflect.ownKeys返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
    以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。
    首先遍历所有数值键,按照数值升序排列。
    其次遍历所有字符串键,按照加入时间升序排列。
    最后遍历所有 Symbol 键,按照加入时间升序排列。
Reflect.ownKeys({ [Symbol()]:0, b:0, 10:0, 2:0, a:0 })
// ['2', '10', 'b', 'a', Symbol()]
  • super 关键字
    this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
const proto = {
  foo: 'hello'
};
const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};
Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
JavaScript 引擎内部,super.foo等同于Object.getPrototypeOf(this).foo(属性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

  • 扩展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3 // { b: 2 }
o3.a // undefined

上面代码中,对象o3复制了o2,但是只复制了o2自身的属性,没有复制它的原型对象o1的属性。

  • ES2020引入了一个新的 Null 判断运算符??。它的行为类似||,但是只有运算符左侧的值为nullundefined时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';

你可能感兴趣的:(js知识点)