- 防抖
- 节流
- 模拟 new 运算符
- 模拟 instanceof
- 模拟 Function.prototype.apply()
- 模拟 Function.prototype.call()
- 模拟 Function.prototype.bind()
- 模拟 Array.prototype.forEach()
- 模拟 Array.prototype.map()
- 模拟 Array.prototype.filter()
- 函数柯里化
- 类数组转数组
- 实现深拷贝
- 继承的实现
- 实现 AJAX
- 多维数组扁平化
- setTimeout 模拟 setInterval
- setInterval 模拟 setTimeout
- sleep
- 数组去重的多种实现方式
- 解析 URL 参数
- 斐波那契数列
- 发布订阅
- 带图带事件的 桌面通知
- 原生 30 行代码实现视频截图
- 基于 URLSearchParams 或 URL 获取 queryString 的值
- 基于 atob 和 btoa 的 base64 编码和解码
- 非正则替换 的 html 代码 encode 和 decode
- 相对地址转换为绝对地址
- 基于 URL 或者 Crypto.getRandomValues 生成 UUID
- 基于 Array.from 的序列生成器
- 基于 sendBeacon 的安全的数据上报
- 数字千分位
- 生成随机字符串 ID
- Promise 顺序执行
- 延时执行 delay
- 进度值映射
- 滑滚动页面到顶部
- 禁止选择和复制
- 禁止图片拖拽
- 浏览器操作相关 browser 工具函数
- 获取 url 参数(第一种)
- 获取 url 参数(第二种)
- 修改 url 中的参数
- 删除 url 中指定的参数
- 获取窗口可视范围的高度
- 获取窗口可视范围宽度
- 获取窗口宽度
- 获取窗口尺寸
- 获取滚动条距顶部高度
- 获取滚动条距左边的高度
- 开启全屏
- 关闭全屏
- 返回当前滚动条位置
- 滚动到指定元素区域
- 平滑滚动到页面顶部
- http 跳转 https
- 检查页面底部是否可见
- 打开一个窗口
- 自适应页面(rem)
- 日期工具 date 工具函数
- cookie 存贮
- cookie 获取
- cookie 删除
- 更多的工具函数
- 数字千分位
- 截取字符串并加身略号
- 获取文件 base64 编码
- B 转换到 KB,MB,GB 并保留两位小数
- base64 转 file
- base64 转 blob
- blob 转 file
- file 转 base64
- 递归生成树形结构
- 遍历树节点
- 追溯父节点
- 寻找所有子节点
- 根据 pid 生成树形结构
- 查询数组中是否存在某个元素并返回元素第一次出现的下标
- Windows 根据详细版本号判断当前系统名称
- 判断手机是 Andoird 还是 IOS
- 函数防抖
- 函数节流
- 判断数据类型
- 生成指定范围随机数
- 数组乱序
- 数组交集
- 数组中某元素出现的次数
- 加法函数(精度丢失问题)
- 减法函数(精度丢失问题)
- 除法函数(精度丢失问题)
- 乘法函数(精度丢失问题)
- 递归优化(尾递归)
- 生成随机整数
- 去除空格
- 大小写转换
- 随机 16 进制颜色 hexColor
- 转义 html(防 XSS 攻击)
- 检测移动/PC 设备
- 隐藏所有指定标签
- 返回指定元素的生效样式
- 检查是否包含子元素
- 数字超过规定大小加上加号 +,如数字超过 99 显示 99+
继承的实现
原型链继承
原型链继承实现的原理就是将构造函数的原型设置为另一个构造函数的实例对象,这样就可以继承另一个原型对象的所有属性和方法,可以继续往上,最终形成原型链。
function Parent1(name, age) {
this.name = name,
this.age = age
}
Parent1.prototype.say = function() {
console.log(this.name)
}
function Child1(name) {
this.name = name
}
Child1.prototype = new Parent1()
Child1.prototype.constructor = Child1
let child1 = new Child1('南玖',18)
console.log(child1) //Child1 {name: '南玖'}
child1.say() // 南玖
缺点:
- 当实现继承后,另一个原型的实例属性,变成了现在这个原型的原型属性,然后该原型的引用类型属性会被所有的实例共享,这样继承原型引用类型属性的实例之间不再具有自己的独特性了。
- 在创建子类型的实例时,没有办法在不影响所有对象实例的情况下给超类型的构造函数中传递参数。
构造函数继承
为了解决原型中包含引用类型值的问题,开始使用借用构造函数,也叫伪造对象或经典继承
构造函数继承实现的原理就是在子类中调用父类构造函数来实现继承
function Parent2(age) {
this.age = age
this.say = function() {
console.log(this.name)
}
}
function Child2(name,age,gender) {
this.name = name
Parent2.call(this, age)
this.gender = gender
}
let child2 = new Child2('南玖', 18, 'boy')
console.log(child2) //Child2 {name: '南玖', age: 18, gender: 'boy'}
child2.say() // 南玖
优点: 可以传递参数以及避免了引用类型的属性被所有实例共享
缺点: 所有方法都在构造函数内,每次创建对象都会创建对应的方法,大大浪费内存
组合继承*
也叫伪经典继承,将原型链和借用构造函数的技术组合到一块。使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承。
function Parent3(age) {
this.age = age
}
Parent3.prototype.say = function() {
console.log(this.name)
}
function Child3(name,age,gender) {
this.name = name
Parent3.call(this, age)
this.gender = gender
}
Child3.prototype = new Parent3
Child3.prototype.constructor = Child3
let child3 = new Child3('南玖', 18, 'boy')
console.log(child3) //Child3 {name: '南玖', age: 18, gender: 'boy'}
child2.say() // 南玖
- 将 Child3 的原型指定为 Parent3 的一个实例,大致步骤和原型链继承类似,只是多了在 Child3 中借调 Parent3 的过程。
- 实例属性定义在构造函数中,而方法则定义在构造函数的新原型中,同时将新原型的 constructor 指向构造函数。
- 可以通过
instanceof
和isPrototypeOf()
来识别基于组合继承创建的对象。 - 避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JS 中最常用的继承模式。
原型式继承
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
function object(obj) {
function F(){}
F.prototype = obj
return new F()
}
let parent4 = {
age: 18,
name: '南玖',
say() {
console.log(this.name)
}
}
let child4 = object(parent4)
console.log(child4)
/**{[[Prototype]]: Object
age: 18
name: "南玖"
say: ƒ say()
[[Prototype]]: Object
}
*/
child4.say() // 南玖
在 object() 函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。从本质上讲,object() 对传入其中的对象执行了一次浅复制
- 这种原型式继承,要求必须要有一个对象可以作为另一个对象的基础
- 用这种方式创建的对象相当于是传入参数对象的副本
它其实就是 ES5 Object.create
的模拟实现,将传入的对象作为创建对象的原型
在只想让一个对象与另一个对象保持类似的情况下,原型继承是完全可以胜任的。原型模式下的缺点:引用类型属性的共享问题。
寄生继承
寄生式继承与原型式继承紧密相关,与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象。
function createAnother(original) {
var clone = object(original) //通过调用函数创建一个新对象
clone.say = function(){ // 以某种方式来增强这个对象
console.log('nanjiu')
};
return clone // 返回这个对象
}
缺点: 跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
寄生组合式继承*
组合继承是 JavaScript 最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}
function Parent6(age) {
this.age = age
}
Parent6.prototype.say = function() {
console.log(this.name)
}
function Child6(name, gender) {
this.name = name
this.gender = gender
}
// 使用
prototype(Child6, Parent6);
let child6 = new Child6('nanjiu', 'boy')
console.log(child6) // Child6 {name: 'nanjiu', gender: 'boy'}
child6.say() // nanjiu
总结
JavaScript 主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。
- 原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。
- 解决这个问题的技术是借用构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能往超类型构造函数中传递参数,但是没有函数复用。
- 使用最多的继承模式是组合继承,这种模式使用原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。
- 此外,还存在下列可供选择的继承模式。
- 原型式继承,可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以得到进一步改造。
- 寄生式继承,与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类型构造函数而导致的低效率问题,可以将这个模式与组合继承一起使用。
- 寄生组合式继承,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论