js基础(查漏补缺版)

只记录了不了解的知识

有兴趣可以详细了解一下JavaScript 基础知识

一、类型

1. BIgInt
  • BigInt 用于任意长度的整数。数字后面带n 例如 10n
    • BigInt 不支持一元加法
    • 不可以把 bigint 和常规数字类型混合使用
const a = 1234567890123456789012345678901234567890n //1n
const b = BigInt(1) //1n
  • 可以进行数学运算,比较运算符
    • 除法 5/2 的结果向零进行舍入,舍入后得到的结果没有了小数部分。对 bigint 的所有操作,返回的结果也是 bigint。
  • 由于 number 和 bigint 属于不同类型,它们可能在进行 == 比较时相等,但在进行 ===(严格相等)比较时不相等
  • 当在 if 或其他布尔运算中时,bigint 的行为类似于 number。
console.log(1n + 3n) //4n
alert(1n + 3n);//4
alert(5n / 2n); // 2

alert( 1 == 1n ); // true
alert( 1 === 1n ); // false
alert( 2n > 1 ); // true
2. symbol 类型symbol 类型

根据规范,只有字符串和symbol两种原始类型可以用作对象属性键,使用其他类型,会自动转成string类型

“symbol” 值表示唯一的标识符,可以使用 Symbol() 来创建这种类型的值:

let id = Symbol();

// id 是描述为 "id" 的 symbol
let id = Symbol("id");

symbol 保证是唯一的。即使创建了许多具有相同描述的 symbol,它们的值也是不同。描述只是一个标签,不影响任何东西。

let id1 = Symbol("id");
let id2 = Symbol("id");
console.log(id1 == id2); //false
注意:
  • symbol 不会被自动转换为字符串
    • 一种防止混乱的“语言保护”,因为字符串和 symbol 有本质上的不同,不应该意外地将它们转换成另一个
    let id1 = Symbol("id");
    alert(id1); //Error: Cannot convert a Symbol value to a string
    alert(id1.toString()); //Symbol(id)
    alert(id1.description);//id
隐藏属性:
  • symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
const obj = {
    name: 'jonm'
}// 属于另一个代码
const id = Symbol('id')
const id1 = Symbol('id')
obj[id1] = 2
//由于 user 对象属于另一个代码库,所以向它们添加字段是不安全的,可能会影响代码库中的其他预定义行为 但symbol属性不会被意外访问到。第三方代码不会知道新定义的 symbol,因此将 symbol 添加到 user 对象是安全的。

console.log('2', obj[id1], obj.id) //2 undefined 我们可以使用 symbol 作为键来访问数据
obj[id] = "Their id value";
console.log('1', obj[id]) //Their id value
console.log(obj)//{ name: 'jonm', [Symbol(id)]: 2, [Symbol(id)]: 'Their id value' }
//标识符和它们的标识符之间不会有冲突,因为 symbol 总是不同的,即使它们有相同的名字。

//冲突场景
const user = { name: "John" };
// 我们的脚本使用了 "id" 属性。
user.id = "Our id value";
// ……另一个脚本也想将 "id" 用于它的目的……
user.id = "Their id value"
// 砰!无意中被另一个脚本重写了 id!
console.log('user', user)//user { name: 'John', id: 'Their id value' }
  • 对象字面量中的 symbol
    • 要在对象字面量 {...} 中使用 symbol,则需要使用方括号把它括起来
const a = Symbol('a');
const obj = {
    name: 'jonm',
    [a]: 123
}
console.log('obj', obj[a]) //123//变量 id 的值作为键,而不是字符串 “id”
  • symbol 在 for…in 中会被跳过
    • Object.keys(user) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。
const a = Symbol('a');
const obj = {
    name: 'jonm',
    ww: "ww",
    [a]: 123
}
for (let key in obj) console.log(key)//name ww
  • Object.assign 会同时复制字符串和 symbol 属性
let id = Symbol("id");
let user = {
    [id]: 123
};
let clone = Object.assign({}, user);
alert(clone[id]); // 123

全局 symbol

symbol 总是不同的值,即使它们有相同的名字。如果希望同名的 symbol 相等,应该使用全局注册表Symbol.for(key) 返回(如果需要的话则创建)一个以 key 作为名字的全局 symbol。使用 Symbol.for 多次调用 key 相同的 symbol 时,返回的就是同一个 symbol

// 从全局注册表中读取
let id = Symbol.for("id"); // 如果该 symbol 不存在,则创建它
// 再次读取(可能是在代码中的另一个位置)
let idAgain = Symbol.for("id");
// 相同的 symbol
console.log(id === idAgain); // true
  • Symbol.keyFor

对于全局 symbol,Symbol.for(key) 按名字返回一个 symbol。相反,通过全局 symbol 返回一个名字,我们可 以使用 Symbol.keyFor(sym)

// 通过 name 获取 symbol
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
let sym1 = Symbol('sym1')
// 通过 symbol 获取 name
console.log(Symbol.keyFor(sym)); // name
console.log(Symbol.keyFor(sym2)); // id
console.log(sym.description)//name
console.log('sym1', Symbol.keyFor(sym1))//undefined
console.log(sym1.description)//sym1

Symbol.keyFor 内部使用全局 symbol 注册表来查找 symbol 的键。所以它不适用于非全局 symbol。如果 symbol 不是全局的,它将无法找到它并返回 undefined。也就是说,所有 symbol 都具有 description 属性。

系统 symbol表 https://tc39.es/ecma262/#sec-well-known-symbols
  • Symbol.hasInstance
  • Symbol.isConcatSpreadable
  • Symbol.iterator
  • Symbol.toPrimitive

Symbol.toPrimitive 允许我们将对象描述为原始值转换。我们很快就会看到它的使用。

总结

symbol 有两个主要的使用场景:

  • “隐藏” 对象属性。

如果我们想要向“属于”另一个脚本或者库的对象添加一个属性,我们可以创建一个 symbol 并使用它作为属性的键。symbol 属性不会出现在 for..in 中,因此它不会意外地被与其他属性一起处理。并且,它不会被直接访问,因为另一个脚本没有我们的 symbol。因此,该属性将受到保护,防止被意外使用或重写。

因此我们可以使用 symbol 属性“秘密地”将一些东西隐藏到我们需要的对象中,但其他地方看不到它。

  • JavaScript 使用了许多系统 symbol,这些 symbol 可以作为 Symbol.* 访问。我们可以使用它们来改变一些内建行为。例如,在本教程的后面部分,我们将使用 Symbol.iterator 来进行 迭代 操作,使用 Symbol.toPrimitive 来设置 对象原始值的转换 等等。

从技术上说,symbol 不是 100% 隐藏的。有一个内建方法 Object.getOwnPropertySymbols(obj) 允许我们获取所有的 symbol。还有一个名为 Reflect.ownKeys(obj) 的方法可以返回一个对象的 所有 键,包括 symbol。但大多数库、内建方法和语法结构都没有使用这些方法。

const a = Symbol('a');
const obj = {
    name: 'jonm',
    a: "ww",
    [a]: 123
}
console.log(Object.getOwnPropertySymbols(obj))//[ Symbol(a) ]
console.log(Reflect.ownKeys(obj))//[ 'name', 'a', Symbol(a) ]

二、交互(alert,prompt,confirm)

  1. confirm 函数显示一个带有 question 以及确定和取消两个按钮的模态窗口。
const r = confirm('1=2?')
alert(r)

限制:

  • 模态窗口的确切位置由浏览器决定。通常在页面中心。
  • 窗口的确切外观也取决于浏览器。我们不能修改它。

推荐:Ant design

三、运算符

  1. 空值合并运算符 '??'
  • a??b 如果a不为null/undefined 则??返回a ,否则返回b
  • 与|| 比较
    • 优先级相同
    • || 返回第一个真值。|| 无法区分 false、0、空字符串 "" 和 null/undefined
    • ?? 返回第一个已定义的值。

四、类型转换

// 字符串转换
//转换发生在输出内容的时候,也可以通过 String(value) 进行显式转换。原始类型值的 string 类型转换通常是很明显的.
//数字型转换
console.log(
  Number(undefined),
  Number(null),
  Number(true),
  Number(false),
  Number("122"),
  Number("122aa")
); //NaN 0 1 0 122 NaN

//布尔型转换
console.log(
  Boolean(-1),
  Boolean(2334),
  Boolean("火速好吃"),
  Boolean(" "), //true
  Boolean(""), //false
  Boolean(0),
  Boolean(undefined),
  Boolean(null),
  Boolean(NaN)
); //true true true true false false false false false

推荐阅读:

数据类型转换 | JavaScript 标准参考教程

彻底理解JavaScript中的类型转换-js类型转换

五、Eval:执行代码字符串(已不建议使用)

内建函数 eval 允许执行一个代码字符串。

let result = eval(code);
//打印字符串
const res = eval('console.log(1111)')
//更改外部变量
const a = 1;
eval("console.log('a=10')")

//严格模式
eval("let x = 5; function f() {}");
console.log(typeof x); // undefined(没有这个变量)
// 函数 f 也不可从外部进行访问
  • 代码字符串可以包含换行符、函数声明和变量
  • eval 内的代码在当前词法环境(lexical environment)中执行,因此它能访问外部变量,也可以改变外部变量
  • 严格模式下eval 有属于自己的词法环境。因此我们不能从外部访问在 eval 中声明的函数和变量
    • 不启用严格模式,eval 没有属于自己的词法环境,因此我们可以从外部访问变量 x 和函数 f
  • eval 访问外部变量的能力会产生副作用,也会导致代码压缩率变低
    • eval 中的代码没有使用外部变量,请以 window.eval(...) 的形式调用 eval,可以避免这种情况
  • 如果 eval 中的代码需要访问局部变量,我们可以使用 new Function 替代 eval,并将它们作为参数传递:
let f = new Function('a', 'alert(a)');
f(5); // 5

六、了解一下

1. 现代模式,"use strict" 严格模式现代模式,"use strict"

JavaScript在发展过程中,新的特性持续加入,旧功能仍旧被兼容

优点:有利于兼容旧代码

缺点: JavaScript 创造者的任何错误或不完善的决定也将永远被保留在 JavaScript 语言中。

use strict的诞生:ES5规范增加了新的语言特性并且修改了一些已经存在的特性。为了保证旧的功能能够使用,大部分的修改是默认不生效的,特殊的指令 —— "use strict" 可以明确地激活这些特性。

  • 处于脚本文件的顶部时,则整个脚本文件都将以“现代”模式进行工作,极少数人会放在函数的开头,可以只在该函数中启用严格模式
"use strict";

// 代码以现代模式工作
...
  • 确保"use strict" 出现在脚本的最顶部,否则严格模式可能无法启用 //只有注释可以出现在use strict上面
alert("some code");
// 下面的 "use strict" 会被忽略,必须在最顶部。

"use strict";

// 严格模式没有被激活
  • 没有类似于 "no use strict" 这样的指令可以使程序返回默认模式。一旦开启就没办法取消
  • 当代码全都写在了 class 和 module 中时,你则可以将 "use strict"省略掉
浏览器启用

使用 开发者控制台 运行代码时,请注意它默认是不启动 use strict 的

你可以使用Shift+Enter 按键去输入多行代码,然后顶层添加use strict

'use strict'; <Shift+Enter 换行>
//  ...你的代码
<按下 Enter 以运行>

//或则 比较丑的办法
  (function() {
  'use strict';

  // ...你的代码...
})()

2. 垃圾回收垃圾回收
  • JavaScript 中主要的内存管理概念是 可达性。
  • 垃圾回收是自动完成的,我们不能强制执行或是阻止执行。
  • 当对象是可达状态时,它一定是存在于内存中的。
  • 被引用与可访问(从一个根)不同:一组相互连接的对象可能整体都不可达,正如我们在上面的例子中看到的那样。

七、不建议

1. 使用三目? 运算符替代 if 语句
  • 可读性差
  • 禁止 break/continue 在 ‘?’ 的右边
  • 非表达式的语法结构不能与三元运算符 ? 一起使用。特别是 break/continue 这样的指令,例如
   for (let i = 0; i < 3;i++) {
     //可
        // if(i == 2){
        //  continue;
        // }
        // alert(i)
     //不可
        i != 2 ? alert(i) : continue;// continue 不允许在这个位置 会报错
      }