手写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 ...
随机推荐
- 【转载】Android开发中巧用Activity和Fragment
1.Activity的生命周期 1)多个Activity组成Activity栈,当前活动位于栈顶.我们先来看看各种Activity基类的类图: 当Activity类定义出来之后,这个Activity何 ...
- MySQL 部署分布式架构 MyCAT (三)
配置垂直分表 修改 schema.xml (db1) cd /data/mycat/conf cp schema.xml schema.xml.rwh # 修改配置 vi schema.xml < ...
- apicloud含有微信支付。支付宝支付和苹果内购的代码
apicloud含有微信支付.支付宝支付和苹果内购的代码 <!DOCTYPE html> <html> <head> <meta charset=" ...
- day3_7.1
1.python的注释 python中为了使代码更容易被人看懂,提高团队开发效率,可使用注释.代码中的注释不加入编译中. 注释有单行注释,如 #这是一段注释 和多行注释: ''' 这是一行注释 ''' ...
- sql 以某个字段分组,另一个字段为参加比较的列,取得前n项的值
假设表A有三个字段 { id int: subject varchar(20): socre int: } 语句为 select * from A x where (select count(*) ...
- 牛客小白月赛18 Forsaken给学生分组
牛客小白月赛18 Forsaken给学生分组 Forsaken给学生分组 链接:https://ac.nowcoder.com/acm/contest/1221/C来源:牛客网 Forsaken有 ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- Ubuntu18.4编译pmon,缺少makedepend和pmoncfg
提示makedepend找不到解决方法:$ apt-cache search makedependxutils-dev - X Window System utility programs for d ...
- 一、SqlServer查询今天的数据-多写法对比性能问题
-- 目标:查询当天的所有数据 -- 说明:表数据行数:960w --方法一:使用格式化被查询条件与格式化当前时间比对 ),)),) --方法二:使用函数DATEDIFF 比对 --方法三:使用传统比 ...
- phpize安装PHP扩展
安装编译完成php源码后忘记安装一些扩展可以通过phpize来安装 拿lnmp1.6安装举例 安装完成lnmp后发现有些扩展没有 lnmp1.6的安装脚本会在lnmp1.6里生成src,里面是lnmp ...