js应用技巧集合
目录
1、装饰器
/**
作者:sh22n
链接:https://juejin.im/post/5e7822c3e51d4526f23a45ae
来源:掘金
*/
类装饰器
装饰类的时候,装饰器方法一般会接收一个目标类作为参数。下面是一个给目标类增加静态属性 test
的例子:
const decoratorClass = (targetClass) => { targetClass.test = '123' } @decoratorClass class Test {} Test.test; // '123'
利用高阶函数的属性,还可以给装饰器传参,通过参数来判断对类进行什么处理。
const withLanguage = (language) => (targetClass) => { targetClass.prototype.language = language; } @withLanguage('Chinese') class Student { } const student = new Student(); student.language; // 'Chinese'
类属性装饰器
类属性装饰器可以用在类的属性、方法、get/set
函数中,一般会接收三个参数:
- target:被修饰的类
- name:类成员的名字
- descriptor:属性描述符,对象会将这个参数传给
Object.defineProperty
装饰器组合
如果你想要使用多个装饰器,那么该怎么办呢?装饰器是可以叠加的,根据离被装饰类/属性的距离来依次执行。
class Person {
@time
@log
say() {}
}
例子:防抖和节流
const throttle = (time) => { let prev = new Date(); return (target, name, descriptor) => { const func = descriptor.value; if (typeof func === 'function') { descriptor.value = function(...args) { const now = new Date(); if (now - prev > wait) { fn.apply(this, args); prev = new Date(); } } } } } class App extends React.Component { componentDidMount() { window.addEveneListener('scroll', this.scroll); } componentWillUnmount() { window.removeEveneListener('scroll', this.scroll); } @throttle(50) scroll() {} }
const debounce = (time) => { let timer; return (target, name, descriptor) => { const func = descriptor.value; if (typeof func === 'function') { descriptor.value = function(...args) { if(timer) clearTimeout(timer) timer = setTimeout(()=> { fn.apply(this, args) }, wait) } } } }
2、对象继承
/**
*/
原型链继承
Child.prototype = Parent.prototype
function Parent (name) {
this.name = name
this.sex = 'boy'
this.colors = ['white', 'black']
}
function Child () {
this.feature = ['cute']
}
var parent = new Parent('parent')
Child.prototype = parent
var child1 = new Child('child1')
child1.sex = 'girl'
child1.colors.push('yellow')
child1.feature.push('sunshine')
var child2 = new Child('child2')
console.log(child1) // Child{ feature: ['cute', 'sunshine'], sex: 'girl' }
console.log(child2) // Child{ feature: ['cute'] }
console.log(child1.name) // 'parent'
console.log(child2.colors) // ['white', 'black', 'yellow']
console.log(parent) // Parent {name: "parent", sex: 'boy', colors: ['white', 'black', 'yellow'] }
优点:
- 继承了父类的模板,又继承了父类的原型对象
缺点:
- 如果要给子类的原型上新增属性和方法,就必须放在
Child.prototype = new Parent()
这样的语句后面 - 无法实现多继承(因为已经指定了原型对象了)
- 来自原型对象的所有属性都被共享了,这样如果不小心修改了原型对象中的引用类型属性,那么所有子类创建的实例对象都会受到影响(这点从修改
child1.colors
可以看出来) - 创建子类时,无法向父类构造函数传参数(这点从
child1.name
可以看出来)
instanceof、isPrototypeOf()
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
实例对象a instanceof 构造函数B
检测a
的原型链(__proto__)
上是否有B.prototype
,有则返回true
,否则返回false
。
isPrototypeOf() 与 instanceof 相反。
function Parent () { this.name = 'parent' } function Child () { this.sex = 'boy' } Child.prototype = new Parent() var child1 = new Child() console.log(child1 instanceof Child) // true console.log(child1 instanceof Parent) // true console.log(child1 instanceof Object) // true
child1 instanceof Child
的查找顺序:
child1 -> child1.__proto__ -> Child.prototype
child1 instanceof Parent
的查找顺序:
child1 -> child1.__proto__ -> Child.prototype
-> Child.prototype.__proto__ -> Parent.prototype
构造器继承
function Child () { Parent.call(this, ...arguments) }
function Parent (name) { this.name = name } function Child () { this.sex = 'boy' Parent.call(this, 'child') } var child1 = new Child() console.log(child1) //
优点:
- 解决了原型链继承中子类实例共享父类引用对象的问题,实现多继承,创建子类实例时,可以向父类传递参数(见题目
3.3
)
缺点:
- 构造继承只能继承父类的实例属性和方法,不能继承父类原型的属性和方法(见题目
3.4
) - 实例并不是父类的实例,只是子类的实例(见题目
3.5
) - 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
组合继承
// 原型链继承
Child.prototype = new Parent()
// 构造继承
function Child () {
Parent.call(this, ...arguments)
}
// 修正
constructor Child.prototype.constructor = Child
寄生组合继承
// 构造继承 function Child () { Parent.call(this, ...arguments) } // 原型式继承 Child.prototype = Object.create(Parent.prototype) // 修正constructor Child.prototype.constructor = Child
原型式继承
var child = Object.create(parent)
寄生式继承
function createAnother (original) { var clone = Object.create(original);; // 通过调用 Object.create() 函数创建一个新对象 clone.fn = function () {}; // 以某种方式来增强对象 return clone; // 返回这个对象 }
混入方式继承
function Child () { Parent.call(this) OtherParent.call(this) } Child.prototype = Object.create(Parent.prototype) Object.assign(Child.prototype, OtherParent.prototype) Child.prototype.constructor = Child
class中的继承
class Child extends Parent {
constructor (...args) {
super(...args)
}
}
ES6中的继承:
-
主要是依赖
extends
关键字来实现继承,且继承的效果类似于寄生组合继承 -
使用了
extends
实现继承不一定要constructor
和super
,因为没有的话会默认产生并调用它们 -
extends
后面接着的目标不一定是class
,只要是个有prototype
属性的函数就可以了
super相关:
-
在实现继承时,如果子类中有
constructor
函数,必须得在constructor
中调用一下super
函数,因为它就是用来产生实例this
的。 -
super
有两种调用方式:当成函数调用和当成对象来调用。 -
super
当成函数调用时,代表父类的构造函数,且返回的是子类的实例,也就是此时super
内部的this
指向子类。在子类的constructor
中super()
就相当于是Parent.constructor.call(this)
。 -
super
当成对象调用时,普通函数中super
对象指向父类的原型对象,静态函数中指向父类。且通过super
调用父类的方法时,super
会绑定子类的this
,就相当于是Parent.prototype.fn.call(this)
。
ES5继承和ES6继承的区别:
-
在
ES5
中的继承(例如构造继承、寄生组合继承) ,实质上是先创造子类的实例对象this
,然后再将父类的属性和方法添加到this
上(使用的是Parent.call(this)
)。 -
而在
ES6
中却不是这样的,它实质是先创造父类的实例对象this
(也就是使用super()
),然后再用子类的构造函数去修改this
。