6. const、let与var的区别?

  1. 块级作用域
  2. 不具备在预编译时提升的特性
  3. 暂时性死区
  4. 变量不允许重复声明

其中,const在声明变量时必须初始化

ES6规定,_let/const_ 命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。 总之,在代码块内,使用 _let_ 命令声明变量之前,该变量都是不可用的。 这在语法上,称为 “暂时性死区”( temporal dead zone,简称 TDZ)。

7. 箭头函数和普通函数的区别?

  1. (写法不同)省略了function函数声明关键字, 返回值return关键字可省
  2. 不会创建自己的this,函数内的this指向上级
  3. 是匿名函数,不能被当作构造函数, 不能使用new
  4. 不绑定arguments,取而代之用rest参数…解决
  5. 没有原型属性
  6. 不能用作 Generator 函数,不可以使用yield命令

**arguments** 是一个对应于传递给函数的参数的类数组对象。

事件传播的三个阶段是什么??

A: 目标 > 捕获 > 冒泡
B: 冒泡 > 目标 > 捕获
C: 目标 > 冒泡 > 捕获
D: 捕获 > 目标 > 冒泡

答案:D 原因:在捕获阶段,事件通过父元素向下传递到目标元素。 然后它到达目标元素,冒泡开始。

1. 严格模式

“use strict”启动。
目的:
- 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
 - 消除代码运行的一些不安全之处,保证代码运行的安全;
 - 提高编译器效率,增加运行速度;
 - 为未来新版本的Javascript做好铺垫。
使用:1. 脚本首行(建议脚本放在立即执行的匿名函数) 2. 函数内部首行
限制(严格模式改变语法及运行是时的行为):

  1. 全局变量必须显式声明。(通常缺少声明的变量赋值默认为全局变量)
  2. 静态绑定。禁止使用with语句。创设eval作用域。
  3. 增强的安全措施。禁止this关键字指向全局对象(必须通过new 产生局部this)。禁止子在函数内部遍历调用栈(fn.caller\arguments)。
  4. 禁止删除变量(只有configurable设置为true的对象属性)。
  5. 显示报错。(对象的只读属性被被赋值/getter方法读取的属性被赋值/禁止扩展的对象添加新属性/删除不可删除的属性)

作用域和闭包

1.理解词法作用域和动态作用域

2.理解JavaScript的作用域和作用域链

3.理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题

4.this的原理以及几种不同使用场景的取值

5.闭包的实现原理和作用,可以列举几个开发中闭包的实际应用

6.理解堆栈溢出和内存泄漏的原理,如何防止

7.如何处理循环的异步操作

8.理解模块化解决的实际问题,可列举几个模块化方案并理解其中原理

执行机制

1.为何try里面放return,finally还会执行,理解其内部机制

2.JavaScript如何实现异步编程,可以详细描述EventLoop机制

4.可以快速分析一个复杂的异步嵌套逻辑,并掌握分析方法

5.使用Promise实现串行

6.Node与浏览器EventLoop的差异

7.如何在保证页面运行流畅的情况下处理海量数据

JS

1. js有哪些数据类型?

值类型(基本类型):字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
引用数据类型:对象(Object)、数组(Array)、函数(Function)。

注:Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。

2. typeof 和 instanceof 区别是什么?简单实现一下 instanceof。

typeof 适用于基础数据类型判断(不包含null),返回值为该类型的字符串。
返回值(七种):number, string, boolean, undefined, function, object, symbol 。

instanceof 一般用于判断引用类型,返回值为布尔值 (A instanceof B, A 是否在B的原型链上)

3. 其中typeof null是什么, 为什么?

结果: “object”

js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息。

  • 000:对象
  • 010:浮点数
  • 100:字符串
  • 110:布尔
  • 1:整数

但是, 对于 undefined和 null 来说,这两个值的信息存储是有点特殊的。
null:所有机器码均为0
undefined :用 −2^30 整数来表示

4. [‘1’, ‘2’, ‘3’].map(parseInt)运行结果?

结果:[1, NaN, NaN]

parseInt(string,[ radix])
radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。
如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x”或 “0X”开头,将以 16 为基数。
如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
返回值。如果被解析参数的第一个字符无法被转化成数值类型,则返回 NaN。

arr.map(function callback( currentValue[, index[, array]]) {
// return element for new_array
}[, thisArg])
callback生成新数组元素的函数,使用三个参数:
currentValue。callback 数组中正在处理的当前元素。
index可选。callback 数组中正在处理的当前元素的索引。
array可选。map 方法调用的数组。
thisArg可选。执行 callback 函数时值被用作this。

2.JavaScript对象的底层数据结构是什么?

在Pthyon里面叫做字典,在Ruby/Perl里面叫散列表。

5.基本类型对应的内置对象,以及他们之间的装箱拆箱操作?

String()、Number()、Boolean()、RegExp()、Date()、Error()、Array()、Function()、Object()、symbol()
装箱:把基本数据类型转化为对应的引用数据类型的操作(每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。机制如下:
(1)创建String类型的一个实例;(2)在实例上调用指定的方法;(3)销毁这个实例;
拆箱:将引用类型对象转换为对应的值类型对象
它是通过引用类型的valueOf()或者toString()方法来实现的。如果是自定义的对象,你也可以自定义它的valueOf()/tostring()方法,实现对这个对象的拆箱。

6.理解值类型和引用类型?

值类型占用空间固定,保存在栈里面,操作的是值本身;引用类型占用空间不固定,保存在堆中,操作的是指向对象的一个指针。
注2:栈一般存放变量的值,内存空间由系统自动分配和释放;堆一般存放复杂对象,内存空间为动态分配,不主动释放的话,可能会由垃圾回收机制自动回收。
数据在内存中的存储结构,也就是物理结构,分为两种:顺序存储结构和链式存储结构。

  • 顺序存储结构:是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的。数组就是顺序存储结构的典型代表。
  • 链式存储结构:是把数据元素存放在内存中的任意存储单元里,也就是可以把数据存放在内存的各个位置。这些数据在内存中的地址可以是连续的,也可以是不连续的。链表就是顺序存储结构的典型代表。

和顺序存储结构不同的是,链式存储结构的数据元素之间是通过指针来连接的,我们可以通使用指针来找到某个数据元素的位置,然后对这个数据元素进行一些操作。

7.null和undefined的区别?

undefined和null在if语句中,都会被自动转为false,相等运算符甚至直接报告两者相等。
null表示”没有对象”,即该处不应该有值。典型用法是:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义。典型用法是:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
注: 最初区分:null是一个表示”无”的对象,转为数值时为0;undefined是一个表示”无”的原始值,转为数值时为NaN。

8.至少可以说出三种判断JavaScript数据类型的方式,以及他们的优缺点,如何准确的判断数组类型?

  1. typeof,返回基础数据类型(除null,NaN), 无法区分引用类型(均为”object”)

typeof null // “object” typeof NaN // “number”
instanceof,只能用来判断复杂数据类型,因为instanceof 是用于检测构造函数(右边)的 prototype 属性是否出现在某个实例对象(左边)的原型链上。

  1. constructor(构造函数的构造器)

constructor 作用和 instanceof 非常相似。但 constructor 检测 Object 与 instanceof 不一样,还可以处理基本数据类型的检测。
(1)null 和 undefined 是无效的对象,因此是不会有 constructor 存在的,这两种类型的数据需要通过其他方式来判断。
(2)函数的 constructor 不稳定,主要是类的原型进行重写,在重写的过程中有可能把之前的constructor给覆盖了,这样检测出来的结果就是不准确的

  1. Object.toString.call(obj), 返回”[object, 类型]”,注意返回的格式及大小写,前面是小写,后面是首字母大写。

对于 Number,String,Boolean,Array,RegExp,Date,Function 原型上的 toString 方法都是把当前的数据类型转换为字符串的类型
注:Array.isArray(object)用于判断一个对象是否为数组

9.可能发生隐式类型转换的场景以及转换原则,应如何避免或巧妙应用

  1. 转换为boolean类型, !操作符,> < == 操作符 (==才会进行类型隐式转换,===先比较类型是否相同,不同则直接返回false)
    2. 转换为number类型 ,+ - * / % 操作符, ++ 操作符
    3. 转换为string类型, + 操作符 (只要两边有一个为字符串,则+为字符串连接符。当为null,boolean或者undefined时编译器先调用Number()将其转换成数值类型。)12 + “34” 或 “12” + 34 均为 “1234”
    符:强制类型转换Number、Boolean、parseInt、parseFloat、String
    常见类型转换
    2string,toString、join、JSON.stringify
    2array, split、Array.prototype.of、Array.prototype.from

12.代码的输出结果?

  1. let a = 3;
  2. let b = new Number(3);
  3. let c = 3;
  4. console.log(a == b);
  5. console.log(a === b);
  6. console.log(b === c);

输出: true false false
解释:==会引发隐式类型转换,右侧的对象类型会自动拆箱为Number类型。new Number()是一个内置的函数构造函数,是对象类型。

14.函数的执行结果?

function sum(a, b) { return a + b; } sum(1, “2”);
输出:”12” 原因: 隐式类型转换

15.下面这些值哪些是假值?

0; new Number(0); (“”); (“ “); new Boolean(false); undefined;
JavaScript中只有6个假值: undefined、null、NaN、0、’’ (empty string)、false
函数构造函数,如new Number和new Boolean都是真值。

16. !!null; !!""; !!1; 下面代码的输出是什么?

输出:false false true


4.”attribute” 和 “property” 的区别是什么?

Attribute(特性)

  1. attribute特性由HTML定义,所有出现在HTML标签内的描述节点都是attribute特性。
  2. attribute特性的类型总是字符串类型。拿上边的DIV为例,document.getElementById('test').getAttribute('custom-attr') 或者$('#test').attr('custom-attr')总是返回字符串类型

Property(属性)

  1. property属性属于DOM对象,DOM实质就是javascript中的对象。我们可以跟在js中操作普通对象一样获取、设置DOM对象的属性,并且property属性可以是任意类型。
  2. 非自定义的attribute特性与property有1:1的映射关系,比如:id,class,title等。
  3. 非自定义的property(attribute)改变的时候,其对应的attribute(property)在多数情况下也会改变。
  4. 当对应的property改变的时候,attribute特性value的值一直未默认值,并不会随之改变。
  5. (最佳实践)在javascript中我们推荐使用property属性因为这个属性相对attribute更快,更简便。尤其是有些类型本该是布尔类型的attribute特性。比如:”checked”, “disabled”, “selected”。浏览器会自动将这些值转变成布尔值传给property属性。

    5.Promise.all 是干什么的?

    用于执行其参数中的所有promise,当其参数中的所有promise均为resolve时,promise.all返回的promise为resolve,当参数promise中存在reject时,其reject为参数promise中第一个reject的值。

    6. 跨域的问题如何解决,什么是同源策略?

    答案:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源,同源是指协议相同域名相同端口相同,解决方案:JSONP,WebSocket,CORS。

    7. 如何实现服务器如何主动向浏览器发送数据?试写出三种实现方式并简要阐述。

    答案: 轮询,Websocket, 服务器发送事件(Server-sent events)

    8.下面代码的输出是什么?

    1. (() => {
    2. let x, y;
    3. try {
    4. throw new Error();
    5. } catch (x) {
    6. (x = 1), (y = 2);
    7. console.log(x);
    8. }
    9. console.log(x);
    10. console.log(y);
    11. })();

    A: 1 undefined 2 B: undefined undefined undefined
    C: 1 1 2 D: 1 undefined undefined
    答案: A 原因: catch接收的变量x是属于catch作用域的。

    9.什么是event loop?

    答案:javascript的事件循环,由于 JS 是单线程的,同步执行任务会造成浏览器的阻塞,所以我们将 JS 分成一个又一个的任务,通过不停的循环来执行事件队列中的任务。这就使得当我们挂起某一个任务的时候可以去做一些其他的事情,而不需要等待这个任务执行完毕。
    关键步骤如下:

  6. 执行最旧的task(一次)

  7. 检查是否存在microtask,然后不停执行,直到清空队列(多次)
  8. 执行render

task主要包含:**setTimeout****setInterval****setImmediate****I/O****UI交互事件**
microtask主要包含:**Promise****process.nextTick****MutaionObserver**
整个最基本的Event Loop如图所示:

  • queue可以看做一种数据结构,用以存储需要执行的函数
  • timer类型的API(setTimeout/setInterval)注册的函数,等到期后进入task队列(这里不详细展开timer的运行机制)
  • 其余API注册函数直接进入自身对应的task/microtask队列
  • Event Loop执行一次,从task队列中拉出一个task执行
  • Event Loop继续检查microtask队列是否为空,依次执行直至清空队列

[p1汇总]js - 图1

执行优先级:
说回正经的,如果从规范来看,microtask优先于task执行。那如果有需要优先执行的逻辑,放入microtask队列会比task更早的被执行,这个特性可以被用于在框架中设计任务调度机制。
如果从node的实现来看,如果时机合适,microtask的执行甚至可以阻塞I/O,是一把双刃剑。
综上,高优先级的代码可以用Promise/process.nextTick注册执行。
执行效率:
node的实现来看,setTimeout这种timer类型的API,需要创建定时器对象和迭代等操作,任务的处理需要操作小根堆,时间复杂度为O(log(n))。而相对的,process.nextTicksetImmediate时间复杂度为O(1),效率更高。
如果对执行效率有要求,优先使用process.nextTicksetImmediate

1.理解ECMAScript和JavaScript的关系?

javascript实现包含三个方面
1. ECMAscript(根据MCMA-262标准,实现的通用脚本语言规范)
2. DOM— js需要支持对DOM的维护,通过document,element对象实现.这些都是在ES中没有的.
3. BOM— js需要支持对BOM的维护,通过window对象实现.这些都是在ES中没有的.

2.熟练运用es5、es6提供的语法规范

3.熟练掌握JavaScript提供的全局对象(例如Date、Math)、全局函数(例如decodeURI、isNaN)、全局属性(例如Infinity、undefined)

4.熟练应用map、reduce、filter 等高阶函数解决问题

5.setInterval需要注意的点,使用settimeout实现setInterval

setTimeout中两个参数分别是回调函数和时间(毫秒)。
1、回调函数不能传递带参数的函数
2、setInterval 周期性的调用函数或计算方法,关闭用clearInterval 。setInterval 和clearInterval 是一对一的关系。比如想要对同一个按钮在不同场景中,使用周期性的调用不同的函数,那么需要先关掉上一个setInterval,再设定另一个setInterval

  1. function newSetInterval (callbackFunction, millisecond) {
  2. function inner() {
  3. callbackFunction()
  4. setTimeout(inner, millisecond)
  5. }
  6. inner()
  7. }

6.JavaScript提供的正则表达式API、可以使用正则表达式(邮箱校验、去重等)解决常见问题?

  1. /^\w+@[a-z0-9]\.[a-z]+$/.test()
  2. str.replace(/(.)(\1)+/g,'$2') //相邻元素去重
  3. // 排序 -> 去重
  4. var sortedStr = str.split("").sort().join("")
  5. sortedStr.replace(/(.)(\1)+/g,'$2')

7.JavaScript异常处理的方式,统一的异常处理方案?

1、try..catch 2、 window.onerror 两种方式捕获异常
throw new Error 抛出异常

8.下面代码的输出是什么?

  1. const numbers = [1, 2, 3];
  2. numbers[10] = 11;
  3. console.log(numbers);

输出: [1, 2, 3, empty × 7, 11] 解释:当你为数组中的元素设置一个超过数组长度的值时,JavaScript会创建一个名为“空插槽”的东西。 这些位置的值实际上是undefined

9.下面代码的输出是什么?

  1. function getAge(...args) {
  2. console.log(typeof args);
  3. }
  4. getAge(21);

A: "number" B: "array" C: "object" D: "NaN"
答案: C 扩展运算符(... args)返回一个带参数的数组。 数组是一个对象,因此typeof args返回object

10.下面代码的输出是什么?

  1. class Chameleon {
  2. static colorChange(newColor) {
  3. this.newColor = newColor;
  4. }
  5. constructor({ newColor = "green" } = {}) {
  6. this.newColor = newColor;
  7. }
  8. }
  9. const freddie = new Chameleon({ newColor: "purple" });
  10. freddie.colorChange("orange");

A: orange B: purple C: green D: TypeError
答案: D 原因: Uncaught TypeError: freddie.colorChange is not a function
colorChange方法是静态的。 静态方法仅在创建它们的构造函数中存在,并且不能传递给任何子级。 由于freddie是一个子级对象,函数不会传递,所以在freddie实例上不存在colorChange方法:抛出TypeError

9.下面代码的输出是什么?

  1. function bark() {
  2. console.log("Woof!");
  3. }
  4. bark.animal = "dog";

A: Nothing, this is totally fine! B: SyntaxError. You cannot add properties to a function this way.
C: undefined D: ReferenceError
答案: A 解释:var b = new bark(); console.log(b.animal); // undefined

11.下面代码的输出是什么?

  1. function Person(firstName, lastName) {
  2. this.firstName = firstName;
  3. this.lastName = lastName;
  4. }
  5. const member = new Person("Lydia", "Hallie");
  6. Person.getFullName = () => this.firstName + this.lastName;
  7. console.log(member.getFullName());

A: TypeError B: SyntaxError C: Lydia Hallie D: undefined undefined
答案: A member.getFullName is not a function
改写Person.prototype.getFullName = function () { }, 此时使用剪头函数, 输出为NaN
方式2,number.getFullName = function () { }, 可避免方法被添加至构造函数, 节约内存空间

12.下面代码的输出是什么?

  1. function Person(firstName, lastName) {
  2. this.firstName = firstName;
  3. this.lastName = lastName;
  4. }
  5. const lydia = new Person("Lydia", "Hallie");
  6. const sarah = Person("Sarah", "Smith");
  7. console.log(lydia);
  8. console.log(sarah);

输出: Person {firstName: “Lydia”, lastName: “Hallie”} undefined
解释:对于sarah,我们没有使用new关键字。 使用new时,它指的是我们创建的新空对象。 但是,如果你不添加new它指的是全局对象!

13.下面代码的输出是什么?

  1. function getPersonInfo(one, two, three) {
  2. console.log(one);
  3. console.log(two);
  4. console.log(three);
  5. }
  6. const person = "Lydia";
  7. const age = 21;
  8. getPersonInfo`${person} is ${age} years old`;

A: Lydia 21 ["", "is", "years old"]
B: ["", "is", "years old"] Lydia 21
C: Lydia ["", "is", "years old"] 21
答案: B 解释: 如果使用标记的模板字符串,则第一个参数的值始终是字符串值的数组。 其余参数获取传递到模板字符串中的表达式的值!

14.下面代码的输出是什么?

  1. const obj = { 1: "a", 2: "b", 3: "c" };
  2. const set = new Set([1, 2, 3, 4, 5]);
  3. obj.hasOwnProperty("1");
  4. obj.hasOwnProperty(1);
  5. set.has("1");
  6. set.has(1);

A: false true false true B: false true true true
C: true true false true D: true true true true
答案: C

15.下面代码的输出是什么?

  1. const a = {};
  2. const b = { key: "b" };
  3. const c = { key: "c" };
  4. a[b] = 123; // a: {[object Object]: 123}
  5. a[c] = 456; // a: {[object Object]: 456}
  6. console.log(a[b]);

A: 123 B: 456 C: undefined D: ReferenceError
答案: B 原因:对象键自动转换为字符串, 变成了[Object object]

ES6

1. es6有哪些新特性, 这些新特性解决了哪问题?

  1. 新的变量声明方式:let和const。let声明变量作用在块级作用域
  2. 模版字符串,``包裹${}拼接。减少代码量增强可读性
  3. 新的函数声明方式:箭头函数。函数内部this指向函数上级
  4. 函数的参数默认值, function foo(name = ‘Jack’’)
  5. 解构赋值,[a, b] = [‘a’, ‘b’] 。独特的用例:交换两个变量的值。
  6. Spread / Rest 展运算符, […[1,2,3]] -> [1,2,3]。实现对数组的拷贝(concat()方法)
  7. iterable类型:Set与Map
  8. class类
  9. promoise
  10. 模块化, export\import

    2. 给出[…”Lydia”]执行结果?

    输出:[“L”, “y”, “d”, “i”, “a”]

    8.如何对数组去重?(常见六种)

  • set […new Set(testData)]
  • for-of + object 利用对象key不重复
  • sort + for 排序后重复元素相邻
  • for-of + includes
  • filter + indexOf 过滤遍历全部数组, 过滤条件是第一次出现当前元素与当前索引不相等

testData.filter((d, index) => testData.indexOf(d) === index)

  • for + for + splice
    1. for (let i=0; i<res.length; i++) {
    2. for (let j=i+1; j < res.length; j++) {
    3. if (res[j] === res[i]) {
    4. res.splice(j, 1)
    5. j--
    6. }
    7. }
    8. }

    9.Promise.all

    Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

    10.实现promise

    关于元编程的说法正确的是?(多选)

A.Reflect用于替代直接调用Object的方法

B.Reflect是一个函数对象,需要使用new操作符

C.Proxy用于自定义的对象的行为,比如修改set和get

D.Symbol、Reflect 和 Proxy 是属于 ES6 元编程范畴的,能“介入”对象的底层操作进行的过程中,并加以影响

答案:A C D

B:Reflect不是一个函数对象,没有constructor,所有不要使用new操作符

  1. es7有哪些新特性?
  2. Array.prototype.includes()
  3. 指数操作符, a ** b 等价于 Math.pow(a, b)
  4. es8有哪些新特性?
    • async/await
    • Object.prototype.values()\entries()
    • String.prototype.padStart\padEnd
    • 函数参数列表结尾允许逗号
    • Object.getOwnPropertyDescriptors()
    • SharedArrayBuffer对象
    • Atomics对象
  5. es9有哪些新特性?
    • 异步迭代
    • Promise.finally()
    • Rest/Spread 属性
    • 正则表达式命名捕获组(Regular Expression Named Capture Groups)
    • 正则表达式反向断言(lookbehind)
    • 正则表达式dotAll模式
    • 正则表达式 Unicode 转义
    • 非转义序列的模板字符串
  6. es10有哪些新特性?
    • 行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
    • 更加友好的 JSON.stringify
    • 新增了Array的flat()方法和flatMap()方法
    • 新增了String的trimStart()方法和trimEnd()方法 // target_str = origin_str.replace(/^\s|\s$/g, “”)
    • Object.fromEntries()
    • Symbol.prototype.description
    • String.prototype.matchAll
    • Function.prototype.toString()现在返回精确字符,包括空格和注释
    • 简化try {} catch {},修改 catch 绑定
    • 新的基本数据类型BigInt
    • globalThis
    • import()
    • Legacy RegEx
    • 私有的实例方法和访问器

3.Symbol类型在实际开发中的应用、可手动实现一个简单的Symbol

  1. Symbol 值通过 Symbol 函数生成,使用 typeof,结果为 “symbol”
  2. Symbol 函数前不能使用 new 命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。
  3. instanceof 的结果为 false
  4. Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
  5. 如果 Symbol 的参数是一个对象,就会调用该对象的 toString 方法,将其转为字符串,然后才生成一个 Symbol 值。
  6. Symbol 函数的参数只是表示对当前 Symbol 值的描述,相同参数的 Symbol 函数的返回值是不相等的。
  7. Symbol 值不能与其他类型的值进行运算,会报错。
  8. Symbol 值可以显式转为字符串。
  9. Symbol 值可以作为标识符,用于对象的属性名,可以保证不会出现同名的属性。
  10. Symbol 作为属性名,该属性不会出现在 for…in、for…of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回。但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名。
  11. 如果我们希望使用同一个 Symbol 值,可以使用 Symbol.for。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
  12. Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key。