js 解析表达式,js的算法案例

大家好,给大家分享一下js 解析表达式,很多人还不知道这一点。下面详细解释一下。现在让我们来看看!

01. 模拟实现 new

首先,这里呢,先简单说明一下new关键字的基本作用,有以下三点:

  • 创建一个新的对象
  • 把Person方法的原型prototype挂载到了obj的原型链__proto__
  • 返回一个新的对象

所以呢,我们可以根据它的这三个特性,来自定义个一个方法objectFactory,如下所示:

function person(name, age) {
  this.name = name
  this.age = age

  return this
}

function objectFactory() {
	const obj = new Object(); // 1. 创建一个新的对象
	const Constructor = [].shift.call(arguments);  
	obj.__proto__ = Constructor.prototype; // 2. 把Person方法的原型挂载到了obj的原型链上
	const ret = Constructor.apply(obj, arguments); 
	return typeof ret === "object" ? ret : obj;  // 3. 返回一个新的对象
}

objectFactory(person, 'michel', '18')
02. 防抖函数实现

首先呢,这里简单介绍一下,什么是防抖函数,防抖函数呢,就是在一段时间内,多次执行,只算最后一次,所以呢,按照这个需求,我们如下实现:

var btn = document.getElementById('btn');
btn.addEventListener('click',fn(()=>{
    console.log('12321');
}));

function fn(callback){
    let timer = null;
    return function(){
        console.log('click')
        clearTimeout(timer);
        timer = setTimeout(()=>{
            callback()
        },3000);
    }
}

控制台打印:
在这里插入图片描述

03. 节流函数实现

这里我们来了解下节流函数,什么是节流函数呢,在某些时候,我们可能会很快的触发事件,但是我们的页面只需要响应一次,所以我们需要做一下节流,测试代码如下:

const throttle = (callback) => {
let flag = true
	return () => {
		console.log('click')
		if(!flag) return
		flag = false;
		setTimeout(() => {
			  flag = true;
			  callback()
		}, 3000)	
	}
}

let btn = document.querySelector('#btn');
btn.addEventListener('click', throttle(() => {
	console.log('输出执行结果')
})  );

最终执行结果:
在这里插入图片描述
从这个最终的效果,我们可以发现无论我们怎么点,或者快速点击多少次,这里只是在三秒后输出了对应的结果,这就是节流的一个概念,无论你怎么点,怎么触发,这段时间我就执行这一次,当然实现的核心就是这个flag这个开关进行控制,至于为什么会有美女,当然也是为了开发体验啦,作为程序猿们,你懂得 ~

04. 自定义事件Emiter实现

我们首先来了解一下,什么是自定义事件,如果大家使用过addEventListener这种类型的事件绑定,那大家对事件监听应该是非常熟悉的,那我们如何来手动实现一个我们自己的订阅-发布模式的自定义事件呢,好我们根据需求编辑如下:

class Eventer {
	constructor(){
		this.events = new Map();
	}
	
	// 在序列中找到并且执行方法
	emit(event_name){
		let handler = this.events.get(event_name);
		// 判断是否带参数
		if(arguments.length > 0){
			handler.apply(this, arguments)
		}else{
			handler.call(this)
		}
	}
	
	// 监听并塞入序列
	on(event_name, fn){
		if(!this.events.get(event_name)){
			this.events.set(event_name, fn);
		}
	}
	
	// 删除事件
	delete(event_name){
		if(this.events.get(event_name)){
			this.events.delete(event_name);
		}
	}
}

// 调用执行
let btn = document.getElementById('btn');
let emiter = new Eventer();
emiter.on('click-btn', function(){
	console.log('你点击按钮啦');
})
btn.onclick = function(){
	emiter.emit('click-btn');
}

浏览器控制台,测试案例打印效果:
在这里插入图片描述

05. 实现 instanceOf

首先我们需要分析一下indanceOf的功能和作用,它有什么功能和作用呢?它可以检测一个方法的原型prototype是否挂载到了另外一个对象的原型链上了,所以返回值就是true或者false;理解清楚它的作用之后呢,我们就来实现一下:

const instance_of = (obj, fn) => {
	let fn_prototype = fn.prototype; // 获取 fn 的原型 prototype
	let obj_proto = obj.__proto__; // 获取 obj 的第一级原型链 __proto__

	while(true){
		// 如果为null 说明找到顶 返回 fasle
		if(obj_proto == null) return false;
		// 如果相等 就说明查找到了 返回 true
		if(obj_proto === fn_prototype) return true;
		
		// 继续往上级原型链查找
		obj_proto = obj_proto.__proto__;
	}

}

// 测试代码片段
function Person(name, age){
	this.name = name,
	this.age = age;
}

let child = new Person('Lee', 18);
let obj = new Object();

console.log(instance_of(child, Person)) // true
console.log(instance_of(child, Object))  // true
console.log(instance_of(child, Array)) // false
console.log(instance_of(obj, Person)) // false
06. 实现一个 call

这里呢,首先我们需要弄明白,call的作用是什么?改变函数内部的this指向,并且执行这个方法;所以呢,我们分三步来模拟:

  1. 首先把这个方法变成这个对象的属性
  2. 然后执行它
  3. 最后再删除它
  4. 这当中呢,需要注意的点有,如果不传入对象,this指向window;如果有参数,需要把参数也得给带进去;函数执行有返回值,需要把返回值给带出去;
Function.prototype._call = function(obj){
	obj = obj || window;
	obj.fn = this; // 1. 首先把这个方法变成这个对象的属性
	let arr = [];
	for(let i=1;i<arguments.length;i++){
		arr.push(arguments[i]);
	}
	let result = obj.fn(...arr);  // 2. 然后执行它
	delete obj.fn;  // 3. 最后再删除它
	return result;
}

// 测试代码片段
function Person(){
	return this.name + ',' +this.age;
}

function Child(){
	this.name = 'Lee';
	this.age = 8;
}

let child = new Child();
let people = Person._call(child)
console.log(people) // Lee, 8

浏览器控制台打印效果:
在这里插入图片描述

07. 实现一个apply

实现apply和实现call基本类似,唯一有一点区别就是call传递的是参数类型,apply传递的是类数组类型,所以呢,我们需要做一点改变,编辑如下:

Function.prototype._apply = function(obj, arrs){
	obj = obj || window;
	obj.fn = this; // 1. 首先把这个方法变成这个对象的属性
	
	// 判断是否传递数组
	let arr = [];
	if(arrs){	
		for(let i=1;i<arrs.length;i++){
			arr.push(arrs[i]);
		}
	}
	
	let result = obj.fn(...arr);  // 2. 然后执行它
	delete obj.fn;  // 3. 最后再删除它
	return result;
}

function Person(){
	return this.name + ',' +this.age;
}

function Child(){
	this.name = 'Lee';
	this.age = 8;
}

let child = new Child();
let people = Person._apply(child)
console.log(people)  // Lee,8
09. 实现一个Object.create

首先我们需要明确Object.create的作用,它是干什么的呢?它可以创建一个新对象,并且这个新对象有一个特性,什么特性呢?传进去的对象会挂载到新对象的原型链上面,所以我们根据这样一个特性,模拟实现编辑如下:

function create(obj){
    function fn(){ };
    fn.prototype = obj;

    return new fn();
}
10. 模拟实现一个JSON.parse

玩过前端的都知道JSON.parseJSON.stringify的基本作用是什么,那我们怎么模拟实现一个JSON.parse的功能呢?我们可以使用eval解析字符串的功能来编译我们的字符串,最终解析成对象,编辑如下:

var json = '{"name":"lee", "age":25}';
var obj = eval("(" + json + ")");

这里呢,顺带着把JSON.stringify的原理实现给贴过来,方便查阅:

function json2str(o) {
    let arr = [];
    const fmt = function(s) {
        if(typeof s == 'object' && s !== null){
            return json2str(s);
        }
        // undefine symbol function的时候 设置为空, 注意区分 '' 与 `"${s}"`, 后者多了 "", 不是真正的空
        if(s === null) {
            return null
        } else if (s === false) {
            return false
        } else if(/^(undefined|symbol|function)$/.test(typeof s)) {
            return ''
        } else {
            return `"${s}"`
        }
    }
    for (var i in o) {
        // 如果是空 就代表是 undefine symbol function  就不用了,去掉
        if(fmt(o[i]) === null || fmt(o[i]) === false || fmt(o[i])){
            arr.push(`"${i}":${fmt(o[i])}`)
        }
    }
    return `{${arr.join(',')}}`
}
11. 类继承

类继承呢,一直都是比较火热的一个话题,也是前端开发者面试的时候遇到的很频繁的一个问题,但是呢,大多数互联网资料,都声明有好几种实现方法,但都有缺点,有缺点就说明没办法正常类继承嘛,所以,这里整理了下,比较规范完整的类继承,实现的主要方向:(1)完成父级方法内部的this拷贝 (2)完成父级方法原型prototype的引用,这里使用上面的Object.create(parent_prototype)进行原型拷贝 (3)最终我们需要重新设置子方法的construtor的指向

Parent.prototype.list = [123,2131,1231,41,90];
Parent.prototype.say = function () {
    console.log("Parent prototype")
};
function Parent () {
    this.name = 'parent';
    this.lists = [1, 2, 3];
}
function Child () {
    Parent.call(this);  // 子类里执行父类构造函数
    this.type = 'child';
}

Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

//以下是测试代码
var c1 = new Child();
var c2 = new Child();

console.log(c1, c2);
console.log(c1 instanceof Child, c1 instanceof Parent);
console.log(c1.constructor);
12. 手写实现一个Promise

1、首先,我们需要明确Promise的具体功能的使用,它是如何使用的?它有哪些特性?我们才好下手去写一个方法模拟它的功能,首先我们需要明白什么事函数式编程,把函数当成参数传递是目前很多第三方库源码比较流行的一种做法;这个Promise它可以传递两个方法参数进入函数,第一个是resolve,第二个呢reject,所以我们需要在函数内部构建两个方法resolvereject(也可以取其他名字)。

2、当然我们需要定义三个状态pendingresolvedrejectd这是三个状态,为什么呢?因为在Promise当中运行机制是pending => resolved 或者 pending => rejected,不能是rejected <=> resolved这种类型的,所以呢我们需要在修改状态前,判断当前的状态是否是pending状态

3、new Promise( ( resolve, reject) => { resolve() } ) 中调用resolve方法的时候,是如何在then方法中的第一个毁掉方法参数中获取呢?这里需要知道一点then方法接收两个参数,这两个参数呢都是函数,第一个方法会在resolved状态下触发,第二个方法会在rejected方法中触发;所以只需要我们初始化Promise的时候调用了resolve()方法,我们就需要把当前状态修改成resolved状态,然后通过this获取,在then方法中判断,这样呢,我们就可以在then中获取状态和值,并且传递给第一个回调方法

  1. 接下来,我们需要来解决这个异步的问题,如果我们在 new Promise( ( resolve, reject) => { /* 异步代码 */ } )
    使用了setTimeout等异步代码,那我们该如何处理呢?这里呢,我们可以使用这个数组塞入我们的这个方法,在Promise方法内部的resolve执行的时候,再来调用这个then的第一个回调方法
~function(){
    
    // 构建一个自己的 Promise 构造函数
    function Promises(exector){
        
        // 存储成功或者失败的值
        let self = this;
        this.value = undefined;
        this.reason = undefined;
        // 追加一个状态
        this.status = 'pending';
        // 存储then成功的回调方法
        this.onSuccessedArr = [];
        // 存储then失败的回调方法
        this.onFailedArr = [];

        
        // 成功执行
        function resolve(v){
            // 判断是否处于 pending 状态 ,不过不是禁止往下执行
            if(self.status === 'pending'){
                self.value = v;
                self.status = 'resolved';
                self.onSuccessedArr.forEach(v => v());
            }
        }

        // 失败执行
        function reject(v){
            // 判断是否处于 pending 状态
            if(self.status === 'pending'){
                self.reason = v;
                self.status = 'rejected';
                self.onFailedArr.forEach(v => v());
            }
        }
        
        // 对异常进行处理
        try{
            exector(resolve, reject);
        }catch(e){
            reject(e)
        }
    }

    // 我们将then方法添加到垢找方法的原型上 参数分别为成功和失败的回调
    Promises.prototype.then = function(onSuccessed, onFailed){
        
        // 获取 this
        let self = this;
        // 设置一个新的 promise 
        let promise_new = null;
        
        // 成功 resolve 
        if(self.status === 'resolved'){
            promise_new = new Promises((resolve, reject) => {
                onSuccessed(self.value);
            })
        }
        
        // 失败 reject 
        if(self.status === 'rejected'){
            promise_new = new Promises((resolve, reject) => {
                onFailed(self.reason);
            })
        }
        
        // 准备阶段 pending
        if(self.status === 'pending'){
            promise_new = new Promises((resolve, reject) => {
                self.onFailedArr.push(() => {
                onFailed(self.reason)
                });
                self.onSuccessedArr.push(() => {
                    onSuccessed(self.value)
                });
            })
        }
        
        return promise_new;
    }
    
    // 测试代码
    let promises = new Promises((resolve, reject) => {
        setTimeout(() => {
            resolve('2s之后我被执行了')
        },2000)

        setTimeout(() => {
            reject('5s之后我报了个错')
        },5000)
    });
   
     
    promises.then(res => {
        console.log(res)
    }, err => {
        console.log(err)
    }).then();
    
    
}();

目前只是实现了一个简版,完整版的,大家可以查阅如下地址:https://www.imooc.com/article/30135

未完待续~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值