js应用技巧集合

目录

  1. 装饰器
  2. 对象继承

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 函数中,一般会接收三个参数:

  1. target:被修饰的类
  2. name:类成员的名字
  3. 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、对象继承

/**

作者:LinDaiDai_霖呆呆
链接:https://juejin.im/post/5e75e22951882549027687f9
来源:掘金

*/

  1. 原型链继承
  2. instanceof、isPrototypeOf()

  3. 构造器继承

  4. 组合继承

  5. 寄生组合继承

  6. 原型式继承

  7. 寄生式继承

  8. 混入方式继承

  9. class中的继承

 原型链继承

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实现继承不一定要constructorsuper,因为没有的话会默认产生并调用它们

  • extends后面接着的目标不一定是class,只要是个有prototype属性的函数就可以了

super相关:

  • 在实现继承时,如果子类中有constructor函数,必须得在constructor中调用一下super函数,因为它就是用来产生实例this的。

  • super有两种调用方式:当成函数调用和当成对象来调用。

  • super当成函数调用时,代表父类的构造函数,且返回的是子类的实例,也就是此时super内部的this指向子类。在子类的constructorsuper()就相当于是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

posted @ 2020-03-24 11:43  晚安早安  阅读(173)  评论(0编辑  收藏  举报