手写instanceof (详解原型链) 和 实现绑定解绑和派发的事件类
A instanceof B 是判断 A 是否继承自B,是返回true, 否返回false
再精确点就是判断B 是否 再 A 的 原型链上,
什么是原型链,举个例子:
我们定义三个对象:
const grandFather = {
name: 'liu',
age: 80
},father = {
age: 50
},son = {
age: 18
};
怎么让这三个对象形成一种继承关系呢 ? 让father 和 son 继承 grandFather 的 name 属性
我们知道对象上有一个内部属性__proto__ , 该属性的属性值指向改对象的原型,
我们是否可以这样写:
const grandFather = {
name: 'liu',
age: 80
},father = {
age: 50,
__proto__: grandFather
},son = {
age: 18,
__proto__: father
};
console.log(father.name);
console.log(son.name);
ok ,打印出:

原理就是如果对象本身没有查找的属性, 就会沿着原型链也就是__proto__属性往上找直到为null为止。
这就是原型链的概念。
但是__proto__前后加双下划线说明它本质是一个内部属性, 而不是一个正式的对外的API,只是由于浏览器广泛支持,才被加入了ES6,
所以只有浏览器必须部署这个属性,其他运行环境不一定要部署,因此,无论从语义的角度,还是从兼容性的角度,都最好不要使用这个属性,
而是使用Object.setPrototyleOf(写操作), Object.getPrototyleOf(读操作), Object.create(生成操作),
所以我们改成这样:
const grandFather = {
name: 'liu',
age: 80
},father = {
age: 50,
},son = {
age: 18,
};
Object.setPrototypeOf(father, grandFather);
Object.setPrototypeOf(son, father);
console.log(father.name);
console.log(son.name);
打印结果是一样的。
又或者这样:
const grandFather = {
name: 'liu',
age: 80
},
father = Object.create(grandFather),
son = Object.create(father);
father.age = 50;
son.age = 18;
console.log(father.name);
console.log(son.name);
打印结果也是一样的。
原型链大家弄清楚了 我们就可以写一个instanceof 的 方法了:
function instanceofMy (A, B) {
const proto = Object.getPrototypeOf(A), prototype = B.prototype;
if (proto === null || proto === undefined) {
return false;
} else if (proto === prototype) {
return true;
} else {
return instanceofMy(Object.getPrototypeOf(proto), B);
}
}
// 测试
console.log(instanceofMy({}, Object));
console.log(instanceofMy([], Array));
function Test() {}
let test = new Test();
console.log(instanceofMy(test, Test));
console.log(instanceofMy('', Array));
测试结果:

利用递归来沿着原型链往上查找, 有同学不想用递归,太耗费内存了,我们可以改成while循环:
function instanceofMy (A, B) {
let proto = Object.getPrototypeOf(A), prototype = B.prototype;
while (proto !== null && proto !== undefined) {
if (proto === prototype) {
return true;
} else {
proto = Object.getPrototypeOf(proto);
}
}
return false;
}
proto = null 作为while循环的出口, 出来了就return false。
---------------------------------------------------------------------------分割线
实现绑定/派发自定义事件的事件类
有时候我们需要自定义一个事件并在特定的条件下手动触发该事件绑定的函数,
这个时候我们就需要这样一个事件类:
class Event {
constructor() {
// 存事件和回调函数的对象
this.cache = {};
}
// 绑定事件
on (type, callback) {
if (this.cache[type] && !this.cache[type].includes(callback)) {
this.cache[type].push(callback);
} else if (this.cache[type] && this.cache[type].includes(callback)) {
return this;
} else {
this.cache[type] = [callback];
}
return this;
}
// 触发事件
trigger(type, ...params) {
if (this.cache[type]) {
this.cache[type].forEach(fn => {
fn(...params);
})
}
return this;
}
// 解绑事件
off(type, callback) {
// 传了callback清除指定callback, 没传清空数组
if (this.cache[type] && this.cache[type].includes(callback)) {
this.cache[type].split(this.cache[type].findIndex(callback), 1);
} else if (this.cache[type] && !callback) {
this.cache[type] = [];
}
return this;
}
}
const event = new Event();
// 测试
function start(str) {
console.log(str + ' start');
}
function end(str) {
console.log(str + ' end');
}
event.on('start', start)
.on('start', end)
.trigger('start', 'Hello world')
.off('start')
;
我们先给一个自定start事件绑定两个函数,然后传参触发, 最后解绑
打印结果:

这样这个事件类就写完了
手写instanceof (详解原型链) 和 实现绑定解绑和派发的事件类的更多相关文章
- 前端面试手写代码——模拟实现new运算符
目录 1 new 运算符简介 2 new 究竟干了什么事 3 模拟实现 new 运算符 4 补充 预备知识: 了解原型和原型链 了解this绑定 1 new 运算符简介 MDN文档:new 运算符创建 ...
- JavaScript提高篇之面向对象之单利模式工厂模型构造函数原型链模式
1.单例模式 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- 手写promise
写在前面: 在目前的前端分开中,我们对于异步方法的使用越来越频繁,那么如果处理异步方法的返回结果,如果优雅的进行异步处理对于一个合格的前端开发者而言就显得尤为重要,其中在面试中被问道最多的就是对Pro ...
- javascript中的原型和原型链(三)
1. 图解原型链 1.1 “铁三角关系”(重点) function Person() {}; var p = new Person(); 这个图描述了构造函数,实例对象和原型三者之间的关系,是原型链的 ...
- JavaScript ES5类 原型 原型链 组合、原型、寄生式继承
ES5类 原型 原型链 继承 JavaScript中,原型是相对于构造函数(类)的叫法(或者说概念),原型链是相对于构造函数(类)的实例对象的叫法. 对于JavaScript对象,如果在对象自身上找 ...
- mnist手写数字识别——深度学习入门项目(tensorflow+keras+Sequential模型)
前言 今天记录一下深度学习的另外一个入门项目——<mnist数据集手写数字识别>,这是一个入门必备的学习案例,主要使用了tensorflow下的keras网络结构的Sequential模型 ...
- 浅析MyBatis(二):手写一个自己的MyBatis简单框架
在上一篇文章中,我们由一个快速案例剖析了 MyBatis 的整体架构与整体运行流程,在本篇文章中笔者会根据 MyBatis 的运行流程手写一个自定义 MyBatis 简单框架,在实践中加深对 MyBa ...
- 【js基础修炼之路】— 我理解的原型链
提起原型链,大家并不陌生,但是对于新人来说一提到原型方面的东西就会比较懵.在我自一次面试的时候,面试官也给我提了这样的问题,当时就按照我的理解说了一些,但是很肤浅,在此我希望给刚入门的前端小伙伴聊一下 ...
- js---15深拷贝浅拷贝 原型链
//&&得到的结果不是布尔类型,如果前面都是 true就执行最后一个,并返回最后一个表达式的值,前面有一个为false,后面不执行,返回前面表达式的值 var a = 3; var b ...
随机推荐
- 协议分析中的TCP/IP网络协议
- 链路层 第一层包含源mac及目的mac,随着传输路径的变化会发生变化,在mac之后,是下层网络协议的类型,图中,下层为IP协议. 在协议解析中,需要关注的是图中type这个字段的内容. - 网络层 ...
- CocoPods原理
CocoaPods 的原理是将所有的依赖库都放到另一个名为Pods的项目中, 然而让住项目依赖Pods项目, 这样,源码管理工作任务从主项目移到了Pods项目中. 1.Pods项目最终会编译成一个名为 ...
- c语言的全排列
在c语言中实现全排列,对于刚接触c语言,还没学习算法的人来说,比较困难了吧.估计大佬也不会看这种基础的东西,全排列实现的办法很多,在c++中有一个专门的函数可以使用,但是在c中实现就有点困难了.如果你 ...
- nginx location 配置详解
指令作用 匹配指定的请求uri(请求uri不包含查询字符串,如http://localhost:8080/test?id=10,请求uri是/test) 语法形式 location [ = | ~ | ...
- JavaScript—数据可视化(ECharts)
Echarts具有丰富的图表,可以说是数据可视化的神器: 1.下载Echarts 官网下载地址:https://echarts.baidu.com/index.html 2.Echarts引用案例—柱 ...
- CentOS7破解root密码
第一步: reboot重启系统,进入修改密码步骤,出现此界面后,按e进行编辑 2.找到linux16这一段中的 ro crashkernel=xxx, 将 ro 改成rw init=/sysroot ...
- IDEA maven项目添加自己的jar包依赖
在pom中添加<dependency> <groupId>com.sim</groupId> <artifactId>SM-1.60</artif ...
- aspx使用KindEditor副文本框插件出现检测到有潜在危险
web配置添加 <httpRuntime requestValidationMode="2.0" /> aspx页面添加 ValidateRequest=&q ...
- linux下查看程序占用多少内存
执行 ps auxVSZ(或VSS)列 表示,程序占用了多少虚拟内存:RSS列 表示, 程序占用了多少物理内存:虚拟内存可以不用考虑,它并不占用实际物理内存. 或执行top 命令 VIRT(或VSS) ...
- 【bzoj4006】[JLOI2015]管道连接(斯坦纳树+dp)
题目链接 题意: 给出\(n\)个点,\(m\)条边,同时给出\(p\)个重要的点以及对应特征. 现在要选出一些边,问使得这\(p\)个所有特征相同的点相连,问最小代价. 思路: 斯坦纳树的应用场景一 ...