JavaScript基础

执行上下文

创建阶段

当调用函数时,会进入创建上下文阶段,此阶段会绑定this、生成作用域链、生成变量。

1、this
var b = 20;
	var obj = {
	b:40,
	c: this.b + 20,
	a: function {
	return this.b;
	}
上述c的值为40,this.b为全局的b,因为obj在全局环境,没有单独生成一个作用域,this指向window。

function f () {
	function g () {};
}
f和g的this都为window。因为g没有赋给一个对象,因此都是全局调用。
在函数回调过程中,可能发生this的变化,如setTimeout等调用,this会变为window。解决办法:可以将this暂存起来。
call、apply、bind实现

要点:arguments时类数组,不是真实数组,不能直接用
bind返回值是一个待执行的闭包函数,执行时内部调用了apply方法,将function绑定到新的对象上。

2.作用域链
作用域链主要用于闭包,闭包会导致内存泄漏。
闭包形成条件:A函数内部有函数B引用了A的内部变量,那么A函数即为闭包函数。

执行阶段

执行阶段,变量会从栈中弹出。一些函数执行结果相同,但是入栈出栈顺序不一定相同。

原型

  • protoType只有函数有,Function既是函数也是对象,Function.proto = Function.protoType;
  • instanceof用于判断是否在某个函数的原型对象上
  • new做了什么?new创建了一个对象的实例。创建一个新对象,并让该对象指向构造函数的原型,使得该对象成为构造函数的实例,并通过apply方法执行构造函数的方法并返回结果。
 function myNew(constructor, args) {
 	let obj = {};
 	obj.__proto__ = constructor.protoType;
 	let res = constructor.apply(obj, args);
 	return res instanceof Object ? res : constructor;
 }
  • 组合寄生式继承:该继承方法既可以继承父类,又可以将子类和父类的原型分开
function Parant(name) {
	console.log(this.name);
}
function Children(name, age) {
	Parant.call(this, name); // 构造函数式继承
	this.age = age;
}
Children.__proto__ = Object.create(Parent.protoType); // 原型继承,Children的原型指向父类的原型实例
Children.protoType.constructor = Children; // 修改原型的构造函数,使得children和Parent分离

深拷贝

深拷贝方法:JSON.Parse(JSON.Stringify(obj)),递归。
JSON.Stringify无法拷贝function,null,undefined等值。
首层深拷贝方法有:slice,concat,assign,这些方法只会拷贝第一层,即简单类型的值。引用类型依然是浅拷贝。

// 递归实现深拷贝
var deepClone = (obj) => {
	if (typeof obj !== 'object' || obj === null) return obj;
	let hash = new Map(); // 使用hash解决循环引用的问题
	let newObj = Array.isArray(obj) ? [] : {};
	// 递归过程中发现有循环,终止递归,返回hash表存的值
	// 如果该对象已经被拷贝过,则直接返回引用
	if (hash.has(obj)) {
		return hash.get(obj);
	}
	hash.set(obj, newObj); // 存储遍历过的值
	for (let key in obj) {
		if (obj.hasOwnProperty(key)) {
			hash.set(key, obj[key]);
			deepClone(obj[key]);
		} else {
			newObj[key] = obj[key];
		}
	}
	return newObj;
}

事件循环

浏览器事件循环和node.js事件循环不同。
浏览器事件循环:JavaScript是单线程模式,一个时段只能执行一个任务。同步任务是主线程直接执行,异步任务是放入宏任务、微任务队列,等待同步任务执行完后再调用执行。
再每一次任务循环中,会先执行所有的同步代码,然后清空所有的微任务,执行下一次的宏任务,然后清空所有微任务…
async await是Promise的语法糖。其相当于如下写法: Promise.resolve(new Promise()).then(callback);resolve会返回 一个promise对象,直到其状态变为resolve或rejected,then回调才会调用,注册到微任务队列,因此then的微任务在下一次的循环中。
微任务有:promise,async await,then,proccess.nextTick。
宏任务:setInterval,setTimeout,setImmidiate

函数式编程

函数式编程区别于命令式编程。函数式编程注重结果,不关心实现的过程。而命令式编程注重过程。
函数式编程一个输入得到一个确切的输出,且该输出可作为下一个的输入。因此,函数式编程可理解为对中间的过程进行细粒度封装,主函数再调用封装函数,最后的结果就是由封装出的各个中间函数组合调用而得到。这种写法可增加代码的可读性。
纯函数:无状态:无论调用多少次,每次的输入对应的输出都相同。数据不可变:不能改变输入参数的值。
柯里化函数可实现纯函数。
柯里化封装

// 柯里化通用方法

webworker

可通过webWorker实现多线程调用。JavaScript主线程任务繁重时,通过webWorker可避免浏览器阻塞。

let worker = new Worker(url); // 将代码的地址给worker,即可开启线程执行相应的代码

worker通过postMessage,onMessage进行通信。不过传递的是值的副本,而不是引用。

es6、es7、es8

1、var、let、const

var存在变量提升,且创建时会为其赋值undefined;let也有变量提升,只是创建时不会为其进行值的初始化(没有将变量绑定在词法环境),因此无法访问。这无法访问的阶段就是“暂时性死区”。
var的变量提升是声明提升,赋值不会提升

var val = 10;
function f() {
	console.log(val);
	var val = 20;
	console.log(val);
	this.val = 30;
}
f();
console.log(val);
// 输出依次是undefined、20、30;
// 第一个val打印的是函数内部的val,声明提升了,但是赋值未提升,所以是undefined,this.val改变的是全局环境的val。

const声明的变量必须初始化参数。如果是引用类型,可以修改其属性,只是地址不变。
function函数和var一样,会变量提升。

2、export导出

export {} 导出的是对象的引用,而export default导出的是值。

3、去重方法

let arr = [1, 3, 2, 1, 3, 2, 4, 6];
//1、 使用set
const newArr = Array.from(new Set(arr));

// 2、使用indexOf: 只会返回匹配到的第一个字符的下标
const newArr = arr.filter((i, index) => arr.indexOf(i) === index);

// 3、使用forEach + includes
let newArr = [];
arr.forEach(i => {
	if (!newArr.includes(i)) {
		newArr.push(i);
	}
})

// 4、使用reduce + includes
const newArr = arr.reduce((pre, curr) => pre.includes(curr) ? pre : [...pre, curr], [])

// 5、考虑时间复杂度,使用hash表存储遍历过的值,单次循环搞定
function duplicateRemove(arr) {
	let hash = new Map();
	let newArr = [];
	for(let i of arr) {
		if (!hash.has(i)) {
			newArr.push(i);
			hash.set(i, true);
		}
	}
	return newArr;
}

Promise

关于promise的题:https://juejin.cn/post/6844903509934997511

// 手写promise
// 定义状态
const PENDING = 'pending';
const FULLFILLED = 'fullfilled';
const REJECTED = 'rejected';
class Promise {
	// 当我们写promise时,有resolve,reject两个参数。这两个参数是promise内部实现的。
	// executor是外部传入的整个函数
	constructor(executor) {
		this.state = PENDING; // 默认pending状态
		this.successCallbacks = []; // pending时,then中执行的回调存储在数组中,等状态resolve再执行
		this.failCallbacks = [];
		this.value = undefined; // resolve的值
		this.reason = undefined; // rejected的值
		
		let resolve = (value) => {
			if (this.state === PENDING) {
				this.state = FULLFILLED;
				this.value = value;
				this.successCallbacks.forEach(fn => fn());
			}
		}
		let reject = (value) => {
			if (this.state === PENDING) {
				this.state = REJECTED;
				this.reason = reason;
				this,failCallbacks.forEach(fn => fn());
			}
		}
		try {
			executor(resolve, reject);
		} catch(e) {
			reject(e);
		}
	
	// then方法返回的也是promise
	then(onFullFilled, onRejected) {
		// 传入的应为函数,如果不是,则返回当前resolve的值
		onFullFilled = typeof onFullFilled === 'function' ? onFullFilled : value => value;
		onRejected = typeof onRejected === 'function' ? onRejected : reason => throw reason;
		;
		let promise = new Promise((resolve, reject) => {
		// 规范要求then里的回调必须异步调用
			if (this.state === PENDING) {
			    // 将回调函数存储起来
			    this.successCallbacks.push(
			        setTimeout(() =>  {
			            try {
			                const v = onFullFilled(this.value);
			                // 将结果传入下一个then回调
			                resolvePromise(promise, v, resolve, reject)
			            }catch(e){ reject(e) };
			        })
			    )
			    this.failCallbacks.push(
			        setTimeout(()=> {
			           try {
			              const v = onRejected(this.reason);
			              resolvePromise(promise, v, resolve, reject);
			           }catch(e){ reject(e) };
			        })
			    )
			}
			if (this.state === FULLFILLED) {
			   setTimeout(() => {
			   // 执行onFullFilled,将结果传入下一个回调
			   }, 0);
			}
			ifthis.state === REJECTED) {
			   setTimeout(() => {
			   // 执行onRejected,将结果传入下一个回调
			   }, 0);
			}
		})
	}
}

function resolvePromise(promise, value, resolve, reject) {
    if (promise === value) {
       return reject(new Error(“循环引用”));
    }
    if (value instanceof Promise) {
       try{
          const v = value.then;
          // 保证只执行一次
          let isCalled =false;
          // 再次执行then回调
          then.call(v, y => {
             if (isCalled) return;
             isCalled = true;
             resolvePromise(promise, y, resolve, reject);
          }, err => {
             if (isCalled) return;
             isCalled = true;
             reject(err);
          }
       }catch(e){
         reject(e);
       }
    } else {
       resolve(value);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值