JS数据类型
基础类型:undefined、Null、Boolean、String、Number、Symbol、BigInt。(存储在栈内存)
引用类型:Array、Function、Object、RegExp、Date、Math。(存储在堆内存)
JS类型检测
typeof: 基础类型正常检测(除了null),引用类型返回'object'(除了function)。
instanceof:基础类型不能检测,引用类型可以正常检测。
Object.prototype.toString.call(): 基础和引用类型都能检查,返回 "[object Xxxx]"
JS浅拷贝
object.assign(target,...sources);
let cloneObj = {...obj};
[].concat();
arr.slice();
assign和扩展运算缺点:1、不能拷贝对象的继承属性。2、不能拷贝对象的不可枚举属性。3、可以拷贝 Symbol 属性。
JS深拷贝
1、SON.parse(JSON.stringify(x))
缺点: 函数、undefined、Symbol 序列化会消失;
Date 会变成字符串;
RegExp 会变成空对象;
NaN、Infinity、-Infinity会变成null;
不能拷贝'不可枚举的属性';
不能拷贝'对象的原型链';
不能拷贝'对象的循环引用'。
2、递归(for in 遍历参数,typeof是引用类型递归,否则直接赋值)
缺点: 不能拷贝'不可枚举的数据'、'Symbol类型'。;
不能正确拷贝 Array、Date、RegExp、Error、Function 的引用类型;
不能拷贝'对象的循环引用'。
3、改进递归
1、不可枚举的属性 及 Symbol类型。可以使用Reflect.ownKeys 方法;
2、判断参数是 Date、RegExp 类型,则直接生成一个新的实例返回;
3、利用 Object.getOwnPropertyDescriptors() 获得对象的所有属性 以及 对应的特性,结合Object.create() 创建一个新的对象,并继承传入原对象的原型链;
4、利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以防止内存泄漏,作为检测循环引用很有帮组,如果循环引用则返回 WeakMap 存储的值。
JS继承
原型链继承
Child.prototype = new Patent()
缺点:原型属性共享问题。
构造函数继承
Function Child() { Parent.call(this) }
缺点:不能继承父类原型上的属性和方法
组合式继承
原型和构造函数结合
缺点:父类多构造一次
原型式继承
Object.create(parent)
缺点:多实例中的引用类型属性指向相同的内存
寄生式继承
借助方法添加属性和方法。
Object.create('用作新对象原型的对象','新对象定义额外属性的对象')。
Let clone = Object.create(original);clone.fun = function(){...};return clone;
缺点:多实例中引用数据类型属性指向相同的内存
寄生组合继承
将parent,Child传入clone方法中。减少一次构造的过程。
Child.prototype = Object.create(parent.prototype);Child.prototype = Child;
继承中最优的方案
Es6 extends继承
使用babel-loader 编译可以看出也是用的寄生组合继承。
JS new、apply、bind、call
new 大致分为以下步骤:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象(this指向新对象);
- 执行构造函数中的代码(为这个对象添加属性);
- 返回新对象。
使用场景:
(1) 判断数据类型。
Object.prototype.toString.call([]).replace(/^[object (S+)]$/, '$1')
(2) 类数组借用方法。
Array.prototype.push.call({0:'java',1:'script',length:2},'jack','lily')//{0:'java',1:'script',2:'jack',3:'lily',length:4}
(3) 获取最大/最小值。
Math.max.apply(Math,[13,6,10,11,16]);// 16
Math.min.apply(Math,[13,6,10,11,16]);// 6
(4) 继承
function child(){ parent.call(this) }
child.prototype = new parent();
child.prototype.constructor = child;
JS 闭包
全局作用域
挂在window对象下的变量;未定义直接赋值的变量是全局变量。
缺点:容易引起‘变量命名的冲突’。
函数作用域
函数中定义,在函数内部才能访问到它;函数被执行完,这个局部变量会被销毁。
块级作用域
使用let关键字,有‘暂时性死区’特点,定义之前不能被使用。
闭包
红宝书:闭包是指有权访问另外一个函数作用域中的变量的函数。
闭包产生的本质就是:当前环境中存在指向父级作用域的引用。
表现形式:1.返回一个函数;2.定时器、事件监听、Ajax请求;3.作为函数参数传递的形式;4.IIFE(立即执行函数)。
解决循环输出问题:1.利用IIFE;2.使用 ES6 中的 let;3.定时器传入第三个参数。
JS 数组
*构造器
Array();字面量;Array.of();Array.from()。
*类型判断
[] instanceof Array;
[].constructor === Array;
Array.prototype.isPrototypeOf([]);
Object.getPrototypeOf([]) === Array.prototype;
Object.prototype.toString.apply([]) === ‘[object Array]’
Array.isArray([])
*改变自身的方法
pop、push、shift、unshift、reverse、sort、splice,ES6 的copyWithin、fill。
*不改变自身的方法
concat、join、slice、toString、toLocaleString、indexOf、lastIndexOf、为形成标准的toSource,以及ES7 的 includes。
*遍历的方法
forEach、every、some、filter、map、reduce、reduceRight,ES 中的 entries、find、findIndex、keys、values。
JS 类数组
*arguments
Object.prototype.toString.call(arguments); // [object Arguments]
callee 属性:被调用函数
*HTML Collection
它是HTML DOM 对象的一个接口,返回包含了获取到的 DOM 元素集合,返回的类型是类数组对象。它是及时更新的,当文档中的DOM变化,它会随之变化。
Object.prototype.toString.call(document.forms[0]); // [object HTMLFormElement]
*NodeList
节点集合,由 querySelector 返回的。可以使用 for...of 来迭代。它是实时集合,文档中的节点树发生变化,NodeList 也随之变化。
var list = document.querySelectorAll(‘input[type=checkbox]’);
Object.prototype.toString.call(list); // [object NodeList]
*应用场景
遍历参数操作
定义链接字符串函数
Array.prototype.slice.call(arguments,1).join(separa);
myConcat(‘,’,’red’,’blue’,’orange’);
传递参数使用
bar.apply(this,arguments);
*类数组转换为数组
Array.prototype.push.call(arrayLike,’jack’,’lily’);
Array.prototype.slice.call(arguments);
Array.prototype.concat.apply([],argument);
Array.from(arguments);
[...arguments];
JS数组扁平化
JS 数组排序
冒泡排序
两个循环,比较两个元素,调换顺序。
快速排序
基线,递归,合并。
插入排序
数组本身进行调整。首先循环遍历 i = 0开始,当前值比前面值大,进行交换。
选择排序
归并排序
JS sort实现
先将元素转换为字符串,然后再进行排序。
arr.sort([compareFunction]) compareFunction用来执行按某种顺序进行排列的函数,不写元素按照转换为字符串的各个字符的 Unicode 位点进行排序。
sort 会根据元素个数是 n 分情况排序:
1.当 n <= 10 时,采用插入排序;
2.当 n > 10 时,采用三路快速排序;
3.当 10 < n <= 1000 时,采用中位数最为哨兵元素;
4.当 n > 1000 时,每隔 200 ~ 215 个元素挑出一个元素,放到一个新数组中,然后对他排序,找到中间位置的数,以此作为中位数。
当 n 足够小的时候,插入排序的时间复杂度 O(n) 要优于快速排序的 O(nlogn)。因此数据量小的时候会采用插入排序。
JS 异步编程方案
*回调函数
问题:回调地狱
*Promise
优点:可以将异步操作以同步操作的流程表达出来,避免层层嵌套的回调函数。
缺点:使用 Promise 链式调用过多,其根本上没有解决回调地狱,依然难以维护。不过Promise 提供了 all 方法。
一个 Promise 有三种状态:pending、fulfilled 和 rejected。
*Generator
特点:交出函数的执行权,需要暂停的方法可以用 yield 标注,返回的是迭代器。
使用 * 来标注它是Generator 函数,next()依次调用,最后返回”{value:undefined,done:true}”的结果,标识该Generator 函数已经执行完毕。
*async/await
ES7提供新的一步方案:async/await。Async 是 Generator 函数的语法糖;写起来使得 JS 的异步代码像同步。
JS 代码是如何被浏览器引擎编译、执行的?
通过V8引擎,先把js代码转化成AST,然后通过解释器将AST转成字码节,在通过编译器将字码节转成机器码,最后在进行垃圾回收, 至此整个js就的执行就完成了。