js中的数据类型
基本类型:String,Number,Boolean,null,Undefined,Symbol,BigInt
引用数据类型:Object,Array,Function,Date,RegExp
es6相对于es5的变化
let和const以及var
箭头函数
模板字符串
解构赋值
扩展运算符
Promise
模块的 Import 和 Export(es6的模块系统)
Map、Set
Array、String新增的api(Array.from(),String.prototype.startsWith(),String.prototype.endsWith())
闭包
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
闭包的优点:
1. 逻辑连续,当闭包作为另一个函数调用的参数时,避免你脱离当前逻辑而单独编写额外逻辑。
2. 方便调用上下文的局部变量。
3. 加强封装性,第2点的延伸,可以达到对变量的保护作用。
闭包的缺点:
1.常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
2.还有有一个非常严重的问题,那就是内存浪费问题,这个内存浪费不仅仅因为它常驻内存,更重要的是,对闭包的使用不当会造成无效内存的产生。
常见的闭包的应用:防抖、节流、模块化
new的原理及手写new函数
原理:
创建一个空对象obj。
给空对象obj的__proto__(隐式原型)赋值为构造函数的prototype(原型对象)。
将this指向这个空对象obj。
将构造函数的参数传入空对象的方法中,并执行方法。
return新对象obj.
手写源码:
function myNew(Class, ...args) {
// 1.创建一个空对象
let obj = {};
// 2.将空对象的__proto__指向构造函数的prototype
obj.__proto__ = Class.prototype;
// 3.将this指向空对象
// 4.执行构造函数中的代码,为空对象添加属性和方法(call方法会改变this的指向,并且自动执行)
Class.call(obj, ...args);
// 5.返回新的对象
return obj;
}
Promise的核心原理和手写源码
关于Promise的几个核心疑问:
1、Promise怎么控制then中的函数的执行?
then中的回调函数并不会都马上立即执行,而是会先被注册,放入回调执行队列中,只有当状态发生改变的时候,才会触发执行整个队列,即发布订阅模式。
2、Promise是如何实现连续then调用的?
then方法执行的时候,会new一个新的Promise,因为then方法是其原型上的,所以可以连续调用。
手写源码:摘自51CTO的wx60d5b00b4a867
/**
* 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
* 2. executor 接受两个参数,分别是 resolve 和 reject
* 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
* 4. promise 的状态一旦确认,就不会再改变
* 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled,
* 和 promise 失败的回调 onRejected
* 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
* 如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
* 如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
* 7. then 的参数 onFulfilled 和 onRejected 可以缺省
* 8. promise 可以then多次,promise 的then 方法返回一个 promise
* 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
* 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
* 11.如果 then 返回的是一个promise,那么会等这个promise执行完,promise如果成功,
* 就走下一个then的成功,如果失败,就走下一个then的失败
*/
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function Promise(executor) {
let self = this;
self.status = PENDING;
self.onFulfilled = [];//成功的回调
self.onRejected = []; //失败的回调
//PromiseA+ 2.1
function resolve(value) {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
self.onFulfilled.forEach(fn => fn());//PromiseA+ 2.2.6.1
}
}
function reject(reason) {
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
self.onRejected.forEach(fn => fn());//PromiseA+ 2.2.6.2
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
// PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
let self = this;
// PromiseA+ 2.2.7
// setTimeout用于模拟异步
let promise2 = new Promise((resolve, reject) => {
if (self.status === FULFILLED) {
//PromiseA+ 2.2.2
//PromiseA+ 2.2.4 --- setTimeout
setTimeout(() => {
try {
//PromiseA+ 2.2.7.1
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
//PromiseA+ 2.2.7.2
reject(e);
}
});
} else if (self.status === REJECTED) {
//PromiseA+ 2.2.3
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
} else if (self.status === PENDING) {
self.onFulfilled.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
self.onRejected.push(() => {
setTimeout(() => {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
function resolvePromise(promise2, x, resolve, reject) {
let self = this;
//PromiseA+ 2.3.1
if (promise2 === x) {
reject(new TypeError('Chaining cycle'));
}
if (x && typeof x === 'object' || typeof x === 'function') {
let used; //PromiseA+2.3.3.3.3 只能调用一次
try {
let then = x.then;
if (typeof then === 'function') {
//PromiseA+2.3.3
then.call(x, (y) => {
//PromiseA+2.3.3.1
if (used) return;
used = true;
resolvePromise(promise2, y, resolve, reject);
}, (r) => {
//PromiseA+2.3.3.2
if (used) return;
used = true;
reject(r);
});
} else {
//PromiseA+2.3.3.4
if (used) return;
used = true;
resolve(x);
}
} catch (e) {
//PromiseA+ 2.3.3.2
if (used) return;
used = true;
reject(e);
}
} else {
//PromiseA+ 2.3.3.4
resolve(x);
}
}
call、apply、bind的原理极其源码
关于this指向,众所周知有这么几种情况:
普通函数的this指向是:谁调用指向谁。
箭头函数的this指向是:在哪里定义的指向谁。
全局作用域下,浏览器的this指向window、nodejs的this指向global。
定时器中的this指向全局。
注:在非严格模式和严格模式下,this的指向有一些不同的。
// call的源码(apply和call的唯一区别就是传参的形式不同)
const myCall = function (...arguments){
// 没有传参的时候,直接绑定全局作用域
const that = arguments[0] || window
// myCall被调用的时候,this为我们实际要执行的函数,如下面对类数组的操作,在调用call的时候,里面的this就是slice.
const params = [].slice.call(arguments,1)
that.fn = this // 我们接一下这个函数
that.fn(...params)
delete that.fn // 执行完毕后,我们删除绑定,避免造成修改
}
Function.prototype.myCall = myCall、
// bind的源码
const myBind = function(...arguments){
const that = arguments[0] || window
const params = [].slice.call(arguments,1)
const fn = this
return function(){
that.fn = fn
that.fn(...params)
delete that.fn
}
}
7.深拷贝、浅拷贝
深拷贝:不同的指针,不同的堆内存空间。数据独立
浅拷贝:不同的指针,相同的堆内存空间。数据相互影响
常见的深拷贝方法:
1. 使用递归的方式实现深拷贝
//使用递归的方式实现数组、对象的深拷贝
function deepClone1(obj) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
//进行深拷贝的不能为空,并且是对象或者是
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone1(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
2. 通过 JSON 对象实现深拷贝
//通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone2(obj) {
var _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone;
}
JSON对象实现深拷贝的一些问题
*无法实现对对象中方法的深拷贝
3. 通过jQuery的extend方法实现深拷贝
var array = [1,2,3,4];
var newArray = $.extend(true,[],array);
4、es6的展开语法
let a = {
name : '张三',
age : '18'
}
let b = {...a};
b.name = '李四';
console.log('a:',a);
console.log('b:',b);
*注意:es6的展开语法只针对第一层,对多层还是浅拷贝方式