手写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 ...
随机推荐
- MES系统在小批量电子行业生产管理中的应用
小批量电子产品生产管理的主要问题 电子电器制造类企业,既有单件小批量生产,也有批量生产:有按库存生产,也有按订单生产,属于典型的离散制造行业.因产品的不同其生产工艺流程也不尽相同,生产设备的布置不是按 ...
- pyhton 基础数据的爬取1
1. 什么是网络爬虫? 在大数据时代,信息的采集是一项重要的工作,而互联网中的数据是海量的,如果单纯靠人力进行信息采集,不仅低效繁琐,搜集的成本也会提高.如何自动高效地获取互联网中我们感兴趣的信息 ...
- Pycharm创建Django项目
1. 点击菜单栏的File--->New Project 2. 打开Terminal, 进入刚刚创建的路径执行如下命令: python manage.py startapp app01 显示效果 ...
- 错误:shell 打开出现一大堆 错误 declare -x 之类的消息
像图中这种情况:这是什么情况呢? 原因:可能是你最近修改了.bashrc 或者 bash_profile 之类的文件.其中export 命令,要求export 命令写在单独的一行上: 就像下面这样,如 ...
- 4. 海思Hi3519A MPP从入门到精通(四 视频输出)
VO(Video Output,视频输出)模块主动从内存相应位置读取视频和图形数据,并通过相应的显示设备输出视频和图形. 1. 基本概念 3519A芯片支持的显示/回写设备.视频层和图形层见下表. 注 ...
- 问题解决:import paddle.fluid出错:DLL load failed: 找不到指定的模块
问题描述: 使用Pycharm编程,导入paddlepaddle库出错.即:import paddle.fluid出错:DLL load failed: 找不到指定的模块 解决方法: 补上缺失的DLL ...
- ORM对象关系映射:
django配置orm: django使用mysql数据库: 首先cmd创建库 settings配置mysql数据库: DATABASES = { 'default': { 'ENGINE': 'dj ...
- jQuery中的属性(四)
1. attr(name|properties|key,value|fn), 设置或返回被选元素的属性值 参数说明: name:属性名称 properties:作为属性的“名/值对”对象 key,va ...
- 【未完成】【oracle】存储过程按日期循环
............. DECLARE V_IMPORT_DATE DATE; V_BUSI_DATE DATE; OUT_RET NUMBER; - ...
- Ultimate Chicken Horse GameProject第三次迭代成果文档
经过三次迭代我们实现了游戏的基本功能 项目文档的github链接:https://github.com/k6tok12355/Ultimate-Chicken-Horse 下面是我们在第一次迭代中设定 ...