基本类型(值类型)
- 数值(Number):用于表示数字,包括整数和浮点数。
- 字符串(String):用于表示文本,由单引号或双引号括起来的字符序列。
- 布尔值(Boolean):用于表示逻辑值,只有两个取值,true和false。
- 空值(Null):表示"没有对象",即该处不应该有值;
typeof null === "object"
- 未定义(Undefined):表示"缺少值",就是此处应该有一个值,但是还没有定义
- 在ES5标准中,
undefined
被设置为不可写 - 但在其之前,
undefined
实际上是一个全局属性,可以被修改。所以babel会将其编译成void 0
,来确保兼容性
- 在ES5标准中,
- 大整数(BigInt):用于表示任意精度的整数,通常用于表示超出Number类型范围的整数。
- 不可以把 bigint 和常规数字类型混合使用
- 创建 bigint 的方式有两种:在一个整数字面量后面加 n 或者调用 BigInt 函数,该函数从字符串、数字等中生成 bigint
- 符号(Symbol):用于表示唯一的标识符。(ES6引入)
- 永远不会有返回值的类型(Never):表示那些永远不会有返回值的函数的返回值类型。通常用于抛出异常或无限循环等情况。
- 任意类型(Any):表示可以是任何类型的值,通常用于在编写代码时不确定变量的类型,或者需要处理多种类型的情况。
pattern
- 所有的内建对象都遵循相同的模式(pattern):
- 方法都存储在 prototype 中(Array.prototype、Object.prototype、Date.prototype 等)。
- 对象本身只存储数据(数组元素、对象属性、日期)。
参考:
null和undefined的区别
let animal = {
jumps: null
};
let rabbit = {
__proto__: animal,
jumps: true
};
alert( rabbit.jumps ); // true,来自于 rabbit
delete rabbit.jumps;
alert( rabbit.jumps ); // null, 来自于animal
delete animal.jumps;
alert( rabbit.jumps ); // undefined,不再有这样的属性存在
原始值
定义
在 JavaScript 中,原始值(原始数据类型)是一种既非对象也无方法或属性的数据。有 7 种原始数据类型:
- string
- number
- bigint
- boolean
- undefined
- symbol
- null
特点
- 所有原始值都是不可变的,即它们的值不能被修改
- 基本类型没有方法,但仍然表现得像有方法一样。当在原始值上访问属性时,JavaScript 自动将值装入包装对象中,并访问该对象上的属性
- 可以任意地被创建,且没有生命周期,因此不能作为weakmap的键
- 特殊值 null 和 undefined 比较特殊。它们没有对象包装器,所以它们没有方法和属性。并且它们也没有相应的原型。
参考:
Symbol
- Symbol 同时是 JavaScript 里最特殊和最不特殊的东西了。
最特殊
- 代码
console.log(Symbol('foo') === Symbol('foo'));
// false
const symbolOne = Symbol('foo');
const symbolTwo = Symbol('foo');
console.log(symbolOne === symbolTwo); // false
const obj = {};
obj[symbolOne] = 'hello';
console.log(obj[symbolTwo]); // undefined
console.log(obj[symbolOne]); // 'hello'
- 传给 Symbol 构造器的字符串只是对它的一个描述,同一个领域下面同一个描述的 symbol 也是独特的。
最不特殊
const symbolOne = Symbol.for('foo');
const symbolTwo = Symbol.for('foo');
console.log(symbolOne === symbolTwo); // true
const obj = {};
obj[symbolOne] = 'hello';
console.log(obj[symbolTwo]); // 'hello'
- Symbol.for(str) 新建了一个根据你字符串唯一的 symbol 对象。重点是,跨了领域之后,它还是一样的。。
const iframe = document.querySelector('iframe');
const iframeWindow = iframe.contentWindow;
console.log(Symbol.for('foo') === iframeWindow.Symbol.for('foo')); // true
参考
引用数据类型(对象类型)
包括
- Object
- Array
- Function
- Date
- RegExp
ES6新增
- Map
- WeakMap
- Set
- WeakSet
Array 和object的关系
数组在内部也是对象,但它的原型是Array.prototype
和原始类型的区别
- 定义方式不同:原始类型(如数字、字符串、布尔值、null和undefined)是直接赋值给变量的,而引用类型(如对象、数组和函数)是通过引用赋值给变量的。
- 存储方式不同:原始类型的值直接存储在栈中,而引用类型的值存储在堆内存中,然后在栈中保存对该内存地址的引用。
- 比较方式不同:原始类型的比较是按值进行的,即比较它们的实际值是否相等;而引用类型的比较是按引用进行的,即比较它们是否指向同一内存地址。
- 传递方式不同:将原始类型的值赋给新变量时,会复制该值;而将引用类型的值赋给新变量时,只会复制指向该值的引用。
类型判断
typeof
:
- 用于检查变量的数据类型。它返回一个表示变量类型的字符串
- 示例
let a = {}; typeof a; //'object' let b = []; typeof b; //'object'
- 缺点: 只能检测8种: es5的string,boolean,number,function,object,undefined es6的symbol,以及最新的bigint
- 历史遗留问题
- 在 JavaScript 的早期版本中,值的内部类型使用了一个 32 位的标识符,其中某些位被用于表示类型。对于对象和 null,这个标识符的值是相同的,导致 typeof null 返回 ‘object’
typeof null === "object" //true typeof null !== "null" // true
- 在 JavaScript 的早期版本中,值的内部类型使用了一个 32 位的标识符,其中某些位被用于表示类型。对于对象和 null,这个标识符的值是相同的,导致 typeof null 返回 ‘object’
instance of
:
- 用于检查一个对象是否是另一个对象的实例,或者是否属于某个类的实例
- 示例
class Animal {} class Dog extends Animal {} const dog = new Dog(); console.log(dog instanceof Dog); // 返回 true console.log(dog instanceof Animal); // 返回 true
Object.toString.call
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('hi') // "[object String]"
Object.prototype.toString.call({a:'hi'}) // "[object Object]"
Object.prototype.toString.call([1,'a']) // "[object Array]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(() => {}) // "[object Function]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
如何实现instanceof?
wip
如何确定传递的值是否是一个数组?
- 答: 使用
Array.isArray()
Array.isArray()
的特别之处Array.isArray()
拒绝原型链中带有Array.prototype
,而实际不是数组的对象,但instanceof Array
会接受。- 对在另一个领域里面创建的数组也会返回 true
- 对Array 的子类也会返回 true
class SpecialArray extends Array {} const specialArray = new SpecialArray(); Array.isArray(specialArray) // true
- 领域(realm)
- 一个领域包含了 JavaScript 的全局对象。
- 跑在 worker 里面的代码,和跑在页面里面的代码,是属于不同领域的。
- iframe 之间也是这样(属于不同领域)。但是,同源的 iframe 们共享一个 ECMAScript agent,意味着对象这个东西可以在不同领域之间传递:
- 参考
Object
概念
- 在 JavaScript 中,对象,可以被看作是一组属性的集合。它是唯一可变的值。事实上,函数也是具有额外可调用能力的对象。
- 对象属性名字可以是任意字符串,包括空串。
- 如果对象属性名字不是合法的 javascript 标识符,它必须用引号包裹。
- 对象的原型(prototype)指向另一个对象或者 null
访问和设置Object的属性
访问
- 点符号表示法
obj.propertyName
- 适用于静态属性名
- 方括号表示法
obj["propertyName"]
- 适用于动态属性名或包含特殊字符的属性名
- 上述二者都属于属性访问器(Property accessors)
- 点符号表示法
设置
- 语法
Object.defineProperty(obj, prop, descriptor)
- 示例:
const obj = {}; Object.defineProperty(obj, 'hiddenProp', { value: 'hidden', configurable:false,//default enumerable: false,//default writable: false //default });
- 语法
设置访问器属性
- 不能直接定义
- 示例
const book = { hideYear: 2004, edition: 1 }; Object.defineProperty(book, "year", { get: function(){ return this.hideYear; }, set: function(newValue){ if (newValue > 2004) { this.hideYear = newValue; this.edition += newValue - 2004; } } }); book.year = 2005; alert(book.edition); //2
区别
- 使用属性访问器分配一个值, 添加的属性是可写、可枚举、可配置的
- 默认情况下,使用
Object.defineProperty()
添加的属性是不可写、不可枚举和不可配置的 - 若属性为不可配置的话,那么无法再修改其特性
删除
- 只能删除对象的可配置属性,
delete obj.name;
- 若属性为不可配置的话,尝试删除在非严格模式下会被忽略,严格模式下会抛出错误
- 只能删除对象的可配置属性,
参考
字面量初始化对象语法
定义:
- 一个用大括号({})括起来的以逗号分隔的列表,包含了一个对象的零个或多个属性名称和相关值
- 是 Object.create() 的一种语法糖
区别于JSON:
- JSON 是对象字面语法的一个真子集
简写函数名
const o = { property: function (){} } //等价于 const o = { property(){} }
Object.create()
- 定义: 以一个现有对象作为原型,创建一个新对象
- 语法:
Object.create(proto, propertiesObject)
- 示例:
o = Object.create(Object.prototype, { // foo 是一个常规数据属性 foo: { writable: true, configurable: true, value: "hello", }, // bar 是一个访问器属性 bar: { configurable: false, get() { return 10; }, set(value) { console.log("Setting `o.bar` to", value); }, }, });
- 特点:
- 可以控制每个属性的可枚举性、可配置性等,而这在字面量初始化对象语法中是做不到的
- 直接创建了一个以 传入的第一个参数 作为原型的新对象,不涉及构造函数
- 由于没有构造函数,不会自动初始化属性,所有属性需要手动添加
- 可用于模拟
new
function myNew(constructor,...args){ const obj = Object.create(constructor.prototype); const res = constructor.apply(obj,args); return res instanceof Object ? res : obj }
- 参考: Object.create() - JavaScript | MDN
Object.assign()
- copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object.
- 示例
var name = 'tom' var a = {name:name} var b = Object.assign(a) console.log(b.name) //tom
- 参考:Object.assign() - JavaScript | MDN
遍历Object
- for in 和 for of
- 对于Object,用
for in
来遍历Object的key,但无法用for of
来遍历valueconst person = { name: "Alice", age: 30, gender: "female" } for (let key in person) { console.log(key + ': ' + person[key]); } // name: Alice // age: 30 // gender: female for (let val of person){ console.log(val); } // TypeError: person is not iterable
for in
的缺陷- 会遍历原型链上的可枚举属性
- 属性遍历顺序不一定是属性定义顺序
- 只会遍历字符串类型的属性键
Object.entries()
- returns an array of a given object’s own enumerable string-keyed property key-value pairs.
- 举例:
const obj = { foo: "bar", baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
- 参考:Object.entries() - JavaScript | MDN
Object.keys()
Object.values()
Object.getOwnPropertyNames()
- 可以返回对象自身的所有字符串属性名,包括不可枚举的属性
- 示例
const obj = {}; Object.defineProperty(obj, 'hiddenProp', { value: 'hidden', enumerable: false }); obj.visibleProp = 'visible'; console.log(Object.getOwnPropertyNames(obj)); // 输出 ["hiddenProp", "visibleProp"]
Object.getOwnPropertyDescriptors()
- 可以返回对象自身所有字符串属性的名和属性描述符,包括不可枚举的属性
Object.getOwnPropertySymbols()
- 获取对象自身的所有 Symbol 属性键
- 示例:
const object1 = {}; const a = Symbol('a'); const b = Symbol.for('b'); object1[a] = 'localSymbol'; object1[b] = 'globalSymbol'; object1.c = 'a'; console.log(Object.getOwnPropertySymbols(object1)); // [Symbol("a"), Symbol("b")]
Object
和object
的区别
Object
:这是一个内置的构造函数,用于创建对象。例如,你可以使用new Object()
来创建一个新的对象实例。object
:这是一个数据类型的名称,表示所有引用类型的对象,包括数组、函数、正则表达式等。它是 JavaScript 中的一种原始类型,用于描述一类数据结构。
Object
和Map
的区别
意外的键
安全性:
- Map 可以安全地与用户提供的键值一起使用。
- 可能会导致对象注入攻击,可以通过使用 null 原型对象来缓解
键的类型:
- Map 的键可以为任何值
- Object 的键必须为 String 或 Symbol
键的顺序
- Map 对象按照插入的顺序迭代条目、键和值。
- 没有单一机制可以迭代对象的所有属性
大小
- Map 中的项目数量很容易从其 size 属性中获得
- 通过获取 Object.keys() 返回的数组的长度
迭代
- Map 是可迭代对象,所以它可以直接迭代。
- Object 没有实现迭代协议,因此对象默认情况下不能直接通过 JavaScript 的 for…of 语句进行迭代。
性能
- Map在涉及频繁添加和删除键值对的场景中表现更好。
- Object未针对频繁添加和删除键值对进行优化。
序列化和解析
- Map没有对序列化或解析的原生支持。
JSON.stringify()
,JSON.parse()
Function
new Function
- 语法:
let func = new Function ([arg1, arg2, ...argN], functionBody);
- 环境: 如果我们使用 new Function 创建一个函数,那么该函数的
[[Environment]]
并不指向当前的词法环境,而是指向全局环境,这样做的好处是避免了与使用压缩程序而产生冲突的问题 - 使用场景: 在复杂的 Web 应用程序中,我们需要从服务器获取代码或者动态地从模板编译函数时
- 参考: “new Function” 语法
将字符串转换为可执行的表达式的方式
eval()
- 可以执行包含 JavaScript 表达式的字符串。
- 示例
const expression = "2 + 2"; const result = eval(expression); console.log(result); // 输出: 4
- 注意:由于安全性和性能问题,它在现代 JavaScript 中并不推荐用于不受信任的输入,因为它会执行任意代码,可能引发安全漏洞。
Function
构造函数
- 可以将字符串转化为函数,并在调用时执行表达式。
- 比
eval()
更加安全,因为它不访问外部作用域。 - 示例
const expression = "return 2 + 2;"; const func = new Function(expression); const result = func(); console.log(result); // 输出: 4
- 模板字符串与插值表达式
- 在模板字符串中使用插值可以直接执行 JavaScript 表达式:
- 虽然这不是直接将字符串解析为表达式的方式,但在编写包含表达式的字符串时非常有用,尤其是结合模板渲染。
- 示例
const a = 2; const b = 3; const result = `${a + b}`; console.log(result); // 输出: 5
setTimeout
/setInterval
setTimeout
和setInterval
允许将字符串作为代码来执行- 但这种方式与
eval()
类似,同样不推荐用于不受信任的输入 - 这种方式 JavaScript 中也不提倡,最好传递一个函数。
- 示例
setTimeout("console.log('Executed!')", 1000);
import()
(ES2020) 虽然import()
是用于动态模块加载的,但它可以动态加载 JavaScript 模块文件,将其作为表达式执行。
import('./module.js').then((module) => {
module.someFunction();
});
虽然不是直接执行字符串表达式,但它可以动态引入外部代码模块。