- 原型链
- 原型对象:绝大部分的函数(少数内建函数除外)都有一个prototype属性,这个属性是原型对象用来创建新对象的实例,而所有被创建的对象都会共享原型对象,因此这些对象便可以访问原型对象的属性
- 原型链
- 每个对象都有__proto__属性,此属性指向该对象的构造函数的原型
- 对象可以通过__proto__与上游的构造函数的原型对象连接起来,而上游的原型对象也有一个__proto__,这样就形成了原型链
- 数组判断
/** * ES6 */ if (Array.isArray(value)) { return true; } /** * 考虑兼容性 */ if (!Array.isArray) { Array.isArray = function (arg) { return Object.prototype.toString.call(arg) === '[object Array]'; } }复制代码
- ES6模块与CommonJs模块
- CommonJs和ES6 Module都可以对引入的对象进行赋值,即对对象内部属性的值进行改变
- CommonJs是对模块的浅拷贝,CommonJs可以对变量重新赋值,即可以改变指针指向
- ES6 Module模块的引用,即ES6 Module只存只读,不能改变其值,指针指向不变,类似const
- import的接口是只读状态,不能修改其变量值,即不能修改其变量的指针指向,但可以改变变量内部指针指向
- JS不可变对象实现
- 深克隆(性能差,不适合大规模使用)
- Immutable.js(性能良好,但需要学习额外的API)
- immer(利用Proxy特性,性能良好,上手成本低)
- JS参数传递方式
- 基本类型按值传递
var a = 1; function test(x) { x = 10; console.log(x); } test(a); // 10; console.log(a); // 1复制代码
- 复杂类型按引用传递
var a = { a: 1, b: 2 }; function test(x) { x.a = 10; console.log(x); } test(a); // {a: 10, b: 2} console.log(a); // {a: 10, b: 2}复制代码
- 按共享传递
var a = { a: 1, b: 2 } function test(x) { // 传递的是副本,和原对象指向同一内存地址 x = 10; // 只修改副本本身的值不会改变指针指向,如果是修改的内部属性,则会修改原对象的属性值,因为是同一地址引用 console.log(x); } test(a); // 10 console.log(a); // {a: 1, b: 2}复制代码
- 基本类型按值传递
- JS类型
- 原始类型
- Boolean
- Null
- Undefined
- Number
- String
- Symbol
- BigInt(没有正式发布)
- 复杂类型
- Object
- 原始类型
- BigInt
- JS中Number.MAX_SAFE_INTEGER表示最大安全数字,计算结果是9007199254740991,超出该范围会出现精度丢失
- 通常的方案是用第三方库来解决,因为在实际应用中比较普遍存在,所以官方提出了BigInt解决方案
- null与undefined
- null表示为空,代表是个空对象(null本身也是对象)
- undefined表示“不存在”
- 0.1 + 0.2为什么不等于0.3
- 将0.1转换为二进制表示:0.00011...(无限重复0011)
- 通过科学计数法表示:1.10011001...(无限重复1001)* 2
- 转换为IEEE754标准表示: 0.100000000000000005551115123126
- 类型转换规则:在if语句、逻辑语句、数学运算逻辑、==等情况下都可能出现隐式类型转换
- 类型转换原理: 通过ToPrimitive将引用类型转为原始类型
/** * 1.如果变量为字符串,直接返回 * 2.如果!IS_SPEC_OBJECT(x),直接返回 * 3.如果IS_SYMBOL_WRAPPER(x),则抛出异常 * 4.否则会根据传入的hint来调用DefaultNumber和DefaultString,比如如果为Date对象,会调用DefaultString */ function ToPrimitive (x, hint) { if (IS_STRING(x)) { return x; } if (!IS_SPEC_OBJECT(x)) { return x; } if (IS_SYMBOL_WRAPPER(x)) { throw MakeTypeError(kSymbolToPrimitive); } if (hint == NO_HINT) { hint = (IS_DATE(x)) ? STRING_HINT : NUMBER_HINT; } return (hint == NUMBER_HINT) ? DefaultNumber(x) : DefaultString(x); }复制代码
- 变量提升:JS引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行,这造成的结果,就是所有变量的声明语句,都会被提升到代码的头部,这就叫做变量提升
- JS作用域链:其本质是JS在执行过程中会创建可执行上下文,可执行上下文中的词法环境含有外部词法环境的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,这些引用串联起来一直指向全局的词法环境,因此形成了作用域链
- this
- this的指向不是在编写时确定的,而是在执行时确定的
- 默认情况下,this指向全局对象,比如在浏览器就是指向window
- 如果函数被调用的位置存在上下文对象时,那么函数是被隐式绑定的
- 可以通过call,apply,bind显式改变this指向
- new调用构造函数绑定(优先级最高的绑定)
- 箭头函数的this
- 箭头函数没有属于自己的this
- 箭头函数里面的this是捕获其所在上下文的this的值
- 箭头函数不会被new调用
- 闭包
- 闭包是什么:函数和声明该函数词法环境的组合(函数和函数内可访问变量的总和)
- 闭包的作用:隐藏变量,可实现私有变量、特权变量、储存变量等
- JS基本类型和复杂类型的存储
- 基本类型储存在栈中
- 基本类型一旦被闭包引用则会成为常驻内存,储存在内存堆中
- 复杂类型会存储在内存堆中
- async/await:async函数,就是Generator函数的语法糖,它建立在Promise上,并且与所有现有的基于Promise的API兼容;async函数的调用不会造成代码阻塞,但是await会引起async函数内部代码阻塞
- Async: 声明一个异步函数
- 自动将常规函数转为Promise,返回值也是一个Promise对象
- 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
- 异步函数内部可以使用await
- Await: 暂停异步功能执行
- 放置在Promise调用之前,await强制其它代码等待,直到Promise完成并返回结果
- 只能与Promise一起使用,不适用于回调
- 只能在async函数内部使用
- Async: 声明一个异步函数
- async/await相比Promise的优势
- 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调用也带来了额外的阅读负担
- Promise传递中间值非常麻烦,而async/await几乎是同步的写法,非常优雅
- 错误处理友好,async/await可以使用成熟的try/catch,Promise的错误捕获非常冗余
- 调试友好,Promise的调试很差,由于没有代码块,不能在返回表达式的箭头函数中设置断点,调试器只能跟踪同步代码的“每一步”
JS基础必知必会
最新推荐文章于 2022-07-12 17:21:01 发布