new
首先,这里呢,先简单说明一下new
关键字的基本作用,有以下三点:
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')
首先呢,这里简单介绍一下,什么是防抖函数,防抖函数呢,就是在一段时间内,多次执行,只算最后一次,所以呢,按照这个需求,我们如下实现:
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);
}
}
这里我们来了解下节流函数,什么是节流函数呢,在某些时候,我们可能会很快的触发事件,但是我们的页面只需要响应一次,所以我们需要做一下节流,测试代码如下:
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
这个开关进行控制,至于为什么会有美女,当然也是为了开发体验啦,作为程序猿们,你懂得 ~
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');
}
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
call
这里呢,首先我们需要弄明白,call的作用是什么?改变函数内部的this指向,并且执行这个方法;所以呢,我们分三步来模拟:
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
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
Object.create
首先我们需要明确Object.create
的作用,它是干什么的呢?它可以创建一个新对象,并且这个新对象有一个特性,什么特性呢?传进去的对象会挂载到新对象的原型链上面,所以我们根据这样一个特性,模拟实现编辑如下:
function create(obj){
function fn(){ };
fn.prototype = obj;
return new fn();
}
JSON.parse
玩过前端的都知道JSON.parse
和JSON.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(',')}}`
}
类继承呢,一直都是比较火热的一个话题,也是前端开发者面试的时候遇到的很频繁的一个问题,但是呢,大多数互联网资料,都声明有好几种实现方法,但都有缺点,有缺点就说明没办法正常类继承嘛,所以,这里整理了下,比较规范完整的类继承,实现的主要方向:(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);
Promise
1、首先,我们需要明确Promise
的具体功能的使用,它是如何使用的?它有哪些特性?我们才好下手去写一个方法模拟它的功能,首先我们需要明白什么事函数式编程,把函数当成参数传递是目前很多第三方库源码比较流行的一种做法;这个Promise
它可以传递两个方法参数进入函数,第一个是resolve
,第二个呢reject
,所以我们需要在函数内部构建两个方法resolve
和reject
(也可以取其他名字)。
2、当然我们需要定义三个状态pending
、resolved
、rejectd
这是三个状态,为什么呢?因为在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
中获取状态和值,并且传递给第一个回调方法
异步
的问题,如果我们在 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
未完待续~~~