JS-变量

变量声明有四种方式: var、let、const、不使用任何关键字默认全局

  • var 属于 ES6 以前的主要的变量声明方式,具有变量提升的副作用。
  • let 属于 ES6,用来声明可修改的块作用域变量,具有暂时性死区的副作用。
  • const 属于 ES6,用来声明不可修改的块作用域变量,具有必须初始化和暂时性死区的副作用。
  • var/let/const 都不影响对象属性的改变

变量类型

js 中数据类型分为基本类型(包含基本包装类型)引用类型统称为变量

基本类型

基本类型(6 个): Null Undefined Number Boolean Symbol String

  • 基本包装类型(3):Boolean Number String
  • 基本类型存储在栈内存
  • 基本类型赋值都是值传递,不存在浅拷贝和深拷贝
  • Symbol 不能使用 new 创建, Symbol 的隐式类型转换(Symbol()+'')会报错

引用类型

引用类型(2 个):Object Function

  • 引用类型存储在堆内存
  • 引用类型赋值都是引用传递
    • 拷贝就是创建并重写新的引用传递
      • 浅拷贝就是对单个引用类型的创建并重写新的引用传递
      • 深拷贝就是对嵌套引用类型的创建并重写新的引用传递

基本包装类型

基本包装类型(3):Boolean Number String

实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。

引用类型与基本包装类型的主要区别就是对象的生存期

使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。来看下面的例子:

var str = "1024";
console.log(typeof str); // string
console.log(typeof Number(str)); // number
console.log(typeof new Number(str)); // object
console.log(str.substring(2)); // 24
str.color = "red"; // str 隐式转换 成 基本包装类型,赋值 str.color,执行成功并立即销毁 str.color
console.log(str.color); //  undefined,表明 str.color 已经被销毁
console.log(typeof str); // string

// 使用 new 构造函数是不一样的。
var str2 = new Number(str);
str2.color = "red";
console.log(str2.color); // red
1
2
3
4
5
6
7
8
9
10
11
12
13

变量区分

  • 基础类型区分使用 typeof 检测未经计算的操作数的类型
typeof null === "object";
typeof (() => {}) === "function";
1
2
  • 引用类型区分使用 instanceof 检测是否存在于某个对象的原型链上
[] instanceof Array === true;
1
  • 类型精准区分使用 Object.prototype.toString 返回一个表示该对象类型的字符串。
Object.prototype.toString.call() === "[object Undefined]";
1

对象

对象是一组数据和功能的集合。

Object 类型所具有的任何属性和方法也同样存在于更具体的对象实例中。

几乎所有的 JavaScript 对象都是 Object 的实例;

一个典型的对象继承了 Object.prototype 的属性(包括方法),尽管这些属性可能被遮蔽(亦称为覆盖)。但是有时候可能故意创建不具有典型原型链继承的对象,比如通过 Object.create(null)创建的对象,或者通过 Object.setPrototypeOf方法改变原型链。

常见的对象

JS 中的对象分为三类:

  1. 内置对象(静态对象):js 本身已经写好的对象,我们可以直接使用不需要定义它。
  2. 本地对象(非静态对象):必须 new 实例化才能使用其方法和属性的就是本地对象。
  3. 宿主对象:js 运行和存活的地方,它的生活环境就是 DOM(文档对象模式)和 BOM(浏览器对象模式)。

具体是:

  • JS 内置对象分为:
    • 值属性对象: Infinity NaN undefined globalThis
    • 函数属性对象: eval() uneval() isFinite() isNaN() parseFloat() parseInt() decodeURI() decodeURIComponent() encodeURI() encodeURIComponent()
    • 基本对象: Object Function Boolean Symbol
    • 错误对象: Error AggregateError EvalError InternalError RangeError ReferenceError SyntaxError TypeError URIError
    • 数字和日期对象: Number BigInt Math Date
    • 字符串对象: String RegExp
    • 可索引的集合对象: Array
    • 使用键的集合对象: Map Set WeakMap WeakSet
    • 结构化数据对象: ArrayBuffer SharedArrayBuffer Atomics DataView JSON
    • 控制抽象对象: Promise Generator GeneratorFunction AsyncFunction
    • 反射对象: Reflect Proxy
    • 国际化对象: Intl
    • WebAssembly 对象: WebAssembly
    • 其他: arguments
  • BOM 对象分为:Window Navigator Screen History Location Storage
  • DOM 对象分为:Document Element Attr NamedNodeMap Event Console CSSStyleDeclaration HTMLCollection

JS 对象的方法和属性

  • Object.constructor 保存着创建当前对象的函数。
  • Object.hasOwnProperty(propertyName) 检测属性是否在当前对象实例中。
  • isPrototypeOf(object) 检测传入对象是否是当前对象的原型。
  • propertyIsEnumerable(propertyName) 检测属性能否枚举访问。
  • toLocaleString() 返回对象的字符串表示,关联执行环境时区。
  • toString() 方法返回一个表示该对象的字符串。
  • valueOf() 方法返回指定对象的原始值。
  • Object.setPrototypeOf()
  • Object.getPrototypeOf()
  • Object.assign()
Object.assign({}, { a: 1 }, { b: 2 });
1
  • Object.create()
var obj = Object.create({a:1}, b:{
    configurable: true, // 默认 true;表示能否使用 delete 删除属性从而重新定义属性。
    enumerable: true, // 默认 true;表示能否通过 for-in 循环返回属性。
    writable: true, // 默认 true;表示能否修改属性值。
    value: undefined, // 默认 undefined;表示属性的值。
    set: undefined, // 默认 undefined;表示写入属性时调用函数。
    get: undefined // 默认 undefined;表示读取属性时调用函数。
})
1
2
3
4
5
6
7
8
  • Object.entries()
for (let [key, value] of Object.entries(obj)) {
}
1
2
  • __proto__/prototype
var fn = function () {};
var fn2 = () => {};
var fn3 = new fn();
console.log(Function.prototype); // ƒ () { [native code] }
console.log(Function.prototype.__proto__); // Object.prototype
console.log(Function.__proto__); // Function.prototype
console.log(Object.__proto__); // Function.prototype
console.log(Object.prototype.__proto__); // null
console.log({}.__proto__); // Object.prototype
console.log({}.prototype); // undefined
console.log(fn.__proto__); // Function.prototype
console.log(fn.prototype); // { constructor: fn, __proto__: Object.prototype }
console.log(fn2.__proto__); // Function.prototype
console.log(fn2.prototype); // undefined
console.log(fn3.__proto__); // { constructor: fn, __proto__: Object.prototype }
console.log(fn3.prototype); // undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • prototype

创建 JS 对象

// 定义对象
var obj = new Object();
var obj = {};
Object.create({}).__proto__; // {__proto__}
// 定义对象属性
Object.defineProperty(obj, "attr", {
    configurable: true, // 默认 true;表示能否使用 delete 删除属性从而重新定义属性。
    enumerable: true, // 默认 true;表示能否通过 for-in 循环返回属性。
    writable: true, // 默认 true;表示能否修改属性值。
    value: undefined, // 默认 undefined;表示属性的值。
    set: undefined, // 默认 undefined;表示写入属性时调用函数。
    get: undefined // 默认 undefined;表示读取属性时调用函数。
});
// 创建一个没有原型的对象
Object.create(null); // {}
Object.create(null).__proto__; // undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

遍历对象

// Object.create(新创建对象的原型对象, [一个对象])
var a = { a: "aa", [Symbol("as")]: "as" };
var bc = {
    b: {
        value: "b",
        writable: true,
        enumerable: true,
        configurable: true
    },
    c: {
        value: "c",
        writable: true,
        enumerable: false,
        configurable: true
    },
    [Symbol("bs")]: {
        value: "bs",
        writable: true,
        enumerable: true,
        configurable: true
    }
};

var obj = Object.create(a, bc);

console.log(obj); // 输出 {b: "b", c: "c", Symbol(bs): "bs"}
console.log(obj.__proto__); // 输出 {a: "aa", Symbol(as): "as"}
console.log(Object.getOwnPropertySymbols(a)); // 输出 [Symbol(as)]
console.log(Object.getOwnPropertyNames(a)); // 输出 ["a"]

// 遍历方法 1 使用 Object.getOwnPropertyNames(包含不可枚举属性)
console.log(Object.getOwnPropertyNames(obj)); // 输出 ["b", "c"]

// 遍历方法 2 使用 Object.keys (只包含可枚举属性)
console.log(Object.keys(obj)); // 输出["b"]

// 遍历方法 3 使用 Object.values - forin; (只包含可枚举属性、原型链的可枚举属性)
for (const k in obj) console.log(k); // 输出 b a

// 遍历方法 4 使用  forof + Object.entries (只包含可枚举属性)
for (const [k, v] of Object.entries(obj)) console.log(k); // 输出 b

// 遍历方法 5 使用  forof + [Symbol.iterator] (只包含可枚举属性)
try {
    for (const k of obj) console.log(k);
} catch (e) {
    console.log(e); // Uncaught TypeError: obj is not iterable
}
obj[Symbol.iterator] = function () {
    const keys = Object.keys(this);
    let i = 0;
    return {
        next() {
            return i < keys.length
                ? {
                      value: keys[i++],
                      done: false
                  }
                : {
                      value: undefined,
                      done: true
                  };
        }
    };
};

for (const k of obj) console.log(k); // 输出 b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

parseInt

parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数, radix 是 2-36 之间的整数,表示被解析字符串的基数。

  • string 要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用 ToString 抽象操作)。字符串开头的空白符将会被忽略。

  • radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。

补充:如果 radix 是 undefined、0 或未指定的,JavaScript 会假定以下情况:

  1. 如果输入的 string 以 "0x"或 "0x"(一个 0,后面是小写或大写的 X)开头,那么 radix 被假定为 16,字符串的其余部分被当做十六进制数去解析。
  2. 如果输入的 string 以 "0"(0)开头, radix 被假定为 8(八进制)或 10(十进制)。具体选择哪一个 radix 取决于实现。ECMAScript 5 澄清了应该使用 10 (十进制),但不是所有的浏览器都支持。因此,在使用 parseInt 时,一定要指定一个 radix。
  3. 如果输入的 string 以任何其他值开头, radix 是 10 (十进制)。
  4. 如果第一个字符不能转换为数字,parseInt 会返回 NaN。
["1", "2", "3"].map(parseInt);
["1", "2", "3"].map((value, index, array) => parseInt(value, index, array));
parseInt("1", 0); // radix 为 0 时,且 string 参数不以 “0x” 和 “0” 开头时,按照 10 为基数处理。这个时候返回 1
parseInt("2", 1); // radix 为 1 时,找不到处理方式,无法解析,返回 NaN
parseInt("3", 2); // radix 为 2(2进制)表示的数中,'3' 在 2 进制中是一个非法的值,2进制中只能存在0和1,返回 NaN

["10", "10", "10", "10", "10"].map(parseInt); // [10, NaN, 2, 3, 4]
1
2
3
4
5
6
7