手写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 ...
随机推荐
- Oracle有哪些诊断事件
作者:eygle | [转载请注出处]链接:https://www.eygle.com/archives/2004/12/oracle_diagnostics_events_list.html 经常有 ...
- MongoDB 最近遇到的几个小问题
(1)连接数据库时报错 ERROR Topshelf.Hosts.ConsoleRunHost.Run An exception occurred System.TimeoutException: A ...
- CodeForces - 1251C (思维+贪心+归并排序)
题意 https://vjudge.net/problem/CodeForces-1251C 一个字符串,相邻的偶数奇数不能交换位置,其他相邻的情况可以交换,问字符串代表的数最小是多少. 思路 相邻的 ...
- (七)Amazon Lightsail 部署LAMP应用程序之清除已安装服务
1.删除Lightsail实例 2.删除MySQL数据库 3.删除Amazon RDS数据库 4.删除负载均衡器 5.删除EC2实例 6.删除EC2安全组 7.删除快照
- 8.Java基础_if-else和switch选择语句
/* 选择语句(基本与C++相同) if-else语句: 格式一: if(关系式){ 语句体; } 格式二: if(关系式){ 语句体; } else{ 语句体; } 格式三: if(关系式){ 语句 ...
- 【转】带栗子的GDB教程
带栗子的GDB教程 原文链接:http://www.cprogramming.com/gdb.html作者:Manasij Mukherjee 一个好的调试软件是一个程序猿的工具箱里最重要的工具之一, ...
- 鲜贝7.3--navicat破解
Navicat for MySQL 安装软件和破解补丁: 链接:https://pan.baidu.com/s/1yy5JkrXk5NV129wzkzntqw 提取码:htc2 1.安装Navicat ...
- 05-01 seaborn
1.Seaborn 在上节中我们学习了matplotlib,这节课我们来看看另一个可视化的模块seaborn,它是基于matplotlib的更高级的开源库,主要用作于数据可视化,解决了matplotl ...
- 色彩缤纷的Python(改变字体颜色及样式)
色彩缤纷的python(改变字体颜色及样式) 在项目过程中,我们常常会因为输出信息的颜色与样式过于单调以至于让人在视觉上感到很杂乱,所以看下文: 在Linux终端中,使用转义序列来进行如上所述的显示, ...
- eclipse复制bpmn文件到idea下乱码问题处理
1.最近在学习工作流,在eclipse上画完了流程图,然后复制到idea下,发现节点的汉字是乱码的. 2.处理方案是修改idea的配置文件,如图,打开这两个文件,在文件末尾加上 -Dfile.enco ...