javascript基础问题整理(持续更新)

1.Number运算操作

Javascript浮点值采用得是IEEE-754双精度标准模型。因此是占用8个字节的内存空间,因为设计精度问题,所以不能测试特定的浮点值,比如:0.1+0.2 != 0.3。具体的计算流程和原因可以参考这篇Number类型详解博客 。js高程第四版有说存储整形值比浮点值的内存空间减少一半,但javascript数值只有Number类型,并且Number类型遵循IEEE-754双精度标准。按理说不该有内存上的占用区别,此疑问留待解决。

2.空位数组行为差异

ES6新增的方法和迭代器与早期ECMAScript版本中存在的方法行为不同,导致对空位数组的处理行为不一致。ES6的新增方法会将空位元素当作undefine,ES6之前的方法会忽略空位。因此实践中避免使用数组空位。如果确实需要空位,采用显示设置undefine值代替。

3.Object和Map的选择对比

对于多数Web开发任务来说,选择Object还是Map只是个人偏好问题,影响不大。不过,对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。

内存占用

Object和Map的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。不同浏览器的情况不同,但给定固定大小的内存,Map大约可以比Object多存储50%的键/值对。

插入性能

向Object和Map中插入新键/值对的消耗大致相当,不过插入Map在所有浏览器中一般会稍微快一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操作,那么显然Map的性能更佳。

查找速度

与插入不同,从大型Object和Map中查找键/值对的性能差异极小,但如果只包含少量键/值对,则Object有时候速度更快。在把Object当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。这对Map来说是不可能的。对这两个类型而言,查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选择Object更好一些。

删除性能

使用delete删除Object属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此,出现了一些伪删除对象属性的操作,包括把属性值设置为undefined或null。但很多时候,这都是一种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map的delete()操作都比插入和查找更快。如果代码涉及大量删除操作,那么毫无疑问应该选择Map。

4.WeakMap特性

weakMap只能使用对象作为"弱键",由于弱键的特性,当键值没有引用时,会被垃圾回收。"弱键"即便有引用,但引用在程序运行中不会被调用,也会被回收。

5.可计算属性

可计算属性,可以在对象字面量中完成动态属性赋值。中括号包围的对象属性键告诉运行时将其作为JavaScript表达式而不是字符串来求值:

const nameKey = 'name';
const ageKey = 'age';
const jobKey = 'job';

let person = {
  [nameKey]: 'Matt',
  [ageKey]: 27,
  [jobKey]: 'Software engineer'
};

6.new.target

抽象基类通过new.target可以实现。new.target保存通过new关键字调用的类或函数。通过在实例化时检测new.target是不是抽象基类,可以阻止对抽象基类的实例化:

// 抽象基类
class Vehicle {
  constructor() {
    console.log(new.target);
    if (new.target === Vehicle) {
      throw new Error('Vehicle cannot be directly instantiated');
    }
  }
}

// 派生类
class Bus extends Vehicle {}

new Bus();       // class Bus {}
new Vehicle();   // class Vehicle {}
// Error: Vehicle cannot be directly instantiated

7.作用域链相关

函数执行时,每个执行上下文中都会有一个包含其中变量的对象。全局上下文中的叫变量对象,它会在代码执行期间始终存在。而函数局部上下文中的叫活动对象,只在函数执行期间存在。定义函数时,就会为它创建作用域链,预装载全局变量对象,并保存在内部的[[Scope]]中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的[[Scope]]来创建其作用域链。接着会创建函数的活动对象(用作变量对象)并将其推入作用域链的前端。作用域链越长,则查找变量所需的时间也越多。因此应该注意尽量减短作用域链的长度。

8.dom元素事件阶段

DOM事件流分为3个阶段:事件捕获、到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡,最迟要在这个阶段响应事件。大多数支持DOM事件流的浏览器实现了一个小小的拓展。虽然DOM2 Events规范明确捕获阶段不命中事件目标,但现代浏览器都会在捕获阶段在事件目标上触发事件。最终结果是在事件目标上有两个机会来处理事件。addEventListener方法的第三个参数Boolean值是指定事件注册的阶段,默认为false,注册到冒泡阶段,true注册到捕获阶段,一般都添加到冒泡阶段,除非事件需要拦截。

9.typeof在"暂时性死区"中的异常表现

"暂时性死区"表示的是在一个代码块(一个作用域)中如果存在const和lei定义的变量,那么这个变量在定义前不能使用。使用var声明的变量会有变量提升,提升到函数作用域顶部。而let和const声明不会有变量提升。typeof操作符用于一个未定义的变量,返回的是undefined。而用于一个在下文中用let和const定义的变量,会报错。

{
	//一个未定义的变量
	typeof undeclared_variable // "undefined"
}
{
	//报错
	typeof x; // ReferenceError
	let x;
}

10.数组解构赋值

只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。

function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5

对象具有 Iterator 接口。解构赋值会依次从这个接口获取值。
上述代码转换为以下代码更为直观

function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

let a = fibs();
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
console.log(a.next());
//输出
{ value: 0, done: false }
{ value: 1, done: false }
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 5, done: false }
{ value: 8, done: false }

其实解构赋值也就是从a对象,它是一个生成器对象,实现了迭代器接口,因此可以使用next方法。解构赋值就是从这个next方法上取值。

11.Number.isInteger()

Number.isInteger()用来判断一个数值是否为整数。但由于数值存储为64位双精度格式。最高精度可达到53个二进制位。如果当数值的精度超过这个限度,第54位后面的位数会被丢弃。这会导致这个API误判。当一个数值的绝对值小于Number.MIN_VALUE,会被自动转为0,此时也会误判。

Number.isInteger(3.0000000000000002) // true
//Number.MIN_VALUE(5E-324)
Number.isInteger(5E-325) // true

12.数组扩展符…和Array.from

在计算包含4字节Unicode字符串的长度时,直接用length属性计算会出错,用数组扩展符转换成数组后计算正确。

"y🚀x".length // 4
[..."y🚀x"].length // 3

任何实现了Iterator接口的对象,都可以使用…扩展运算符转换为真正的数组。可以查看对象是否有[Symbol.iterator]属性以确定是否实现Iterator接口。 Array.from可以将任何拥有length属性的对象转换为数组。Array.from的第二个参数类似map方法,第三个参数绑定this对象。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值