• 浅析 JavaScript 中的方法链


    JavaScript 中方法链

    你一定曾经用过 jQuery 之类的库,可能看到过类似的东西。在进行级联时主要有两种方法:一种是一个接一个的执行方法,另一种是在同一行上。在纯 JavaScript 中这种做法也很普遍。你可以在数组、字符串和 promise 看到它。

    在这些情况下所有的过程都是相同的。首先引用要使用的对象。然后根据需要使用多种方法。但不是单独使用这些方法,而要一个接一个地使用。基本上是把它们链接在一起。先看一些例子。

    方法链的例子

    在处理字符串时有两种方法。第一个种不用方法链,这要求必须在字符串上分别使用每个方法,这样必须每次都引用这个字符串。

    第二种方式是用方法链。这时可以用所有想要的字符串方法。写出的代码也可以是单行或多行,这取决于你的习惯。而且只需要引用一次字符串。尽管结果相同,但是代码量却有很大的差异。

    // 在字符串上使用方法链的例子
    let myStr = ' - Hello-world. '

    // 不用方法链:
    myStr = myStr.toLowerCase()
    myStr = myStr.replace(/-/g, ' ')
    myStr = myStr.trim()

    // 用方法链:
    myStr = myStr.toLowerCase().replace(/-/g, ' ').trim()

    // 多行方法链的写法:
    myStr = myStr
    .toLowerCase()
    .replace(/-/g, ' ')
    .trim()

    // 查看 "myStr" 的值
    console.log(myStr)
    // Output:
    // 'hello world.'

    在数组上也能用方法链:

    // 在数组上使用方法链的例子
    let myArray = [1, 7, 3, null, 8, null, 0, null, '20', 15]

    // 不用方法链:
    myArray = myArray.filter(el => typeof el === 'number' && isFinite(el))
    myArray = myArray.sort((x, y) => x - y)

    // 使用方法链:
    myArray = myArray.filter(el => typeof el === 'number' && isFinite(el)).sort((x, y) => x - y)

    // 多行方法链的写法:
    myArray = myArray
    .filter(el => typeof el === 'number' && isFinite(el))
    .sort((x, y) => x - y)

    // 查看 "myArray" 的值.
    console.log(myArray)
    // Output:
    // [ 0, 1, 3, 7, 8 ]

    Promise 是一个很好的例子,因为在使用时差不多全都是方法链。首先创建一个 promise,然后添加适当的处理函数。

    // 创建 Promise
    const myPromise = new Promise((resolve, reject) => {
    // 创建一个假延迟
    setTimeout(function() {
    // 用一条简单的消息解决诺言 promise
    resolve('Sorry, no data.')
    }, 1000)
    })

    // 使用方法链:
    myPromise.then((data) => console.log(data)).catch(err => console.log(error))

    // 多行方法链的写法:
    myPromise
    .then((data) => console.log(data))
    .catch(err => console.log(error))
    // Output:
    // 'Sorry, no data.'

    方法链是怎样工作的

    接下来研究它是怎样工作的。答案很简单,是因为 this 。

    假设有一个对象。如果在该对象内使用 this ,它会引用这个对象。如果创建该对象的实例或副本,则 this 将会引用这个实例或副本。当你使用某些字符串或数组方法时,实际上是在用一个对象。

    const myObj = {
    name: 'Stuart',
    age: 65,
    sayHi() {
    // 这里的 this 是 myObj 的引用
    return `Hi my name is ${this.name}.`
    },
    logMe() {
    console.log(this)
    }
    }

    myObj.sayHi()
    // Output:
    // 'Hi my name is Stuart.'

    myObj.logMe()
    // Output:
    // {
    // name: 'Stuart',
    // age: 65,
    // sayHi: ƒ,
    // logMe: ƒ
    // }

    如果是字符串,则使用的是原始数据类型。但是你所使用的方法例如 toLowerCase() ,存在于 String 对象的原型中。在对象上使用方法链还有一个关键要素: this 。

    为了使链起作用,方法必须返回与其一起使用的对象,也就是必须返回 this 。就像接力赛跑时的接力棒一样。

    在 JavaScript 中实现方法链

    为了使方法链有效,必须满足三个条件:首先,需要一些对象。其次,该对象需要一些以后可以调用的方法。第三,这些方法必须返回对象本身,它们必须返回 this 才能使用方法链。

    让我们创建一个简单的对象 person 。 person 有 name , age 和 state 属性。 state 用来表示当前处于什么状态。要想改变这个状态,需要用到几个方法: walk() , sleep() , eat() , drink() , work() 和 exercise() 。

    由于我们希望所有这些方法都是可链的,所以它们都必须返回 this 。另外代码中还有一个用来把当前状态记录到控制台的工具方法。

    // 创建 person 对象
    const person = {
    name: 'Jack Doer',
    age: 41,
    state: null,
    logState() {
    console.log(this.state)
    },
    drink() {
    // 修改 person 的 state.
    this.state = 'Drinking.'

    // 把状态输出到控制台
    this.logState()

    // 返回 this 值。
    return this
    },
    eat() {
    this.state = 'Eating.'
    this.logState()
    return this
    },
    exercise() {
    this.state = 'Exercising.'
    this.logState()
    return this
    },
    sleep() {
    this.state = 'Sleeping.'
    this.logState()
    return this
    },
    walk() {
    this.state = 'Walking.'
    this.logState()
    return this
    },
    work() {
    this.state = 'Working.'
    this.logState()
    return this
    }
    }

    //
    person
    .drink() // Output: 'Drinking.'
    .exercise() // Output: 'Exercising.'
    .eat() // Output: 'Eating.'
    .work() // Output: 'Working.'
    .walk() // Output: 'Walking.'
    .sleep() // Output: 'Sleeping.'

    // 写在一行上
    person.drink().exercise().eat().work().walk().sleep()
    // Output:
    // 'Drinking.'
    // 'Exercising.'
    // 'Eating.'
    // 'Working.'
    // 'Walking.'
    // 'Sleeping.'

    方法、链、this 和箭头函数

    必须使用 this 也意味着无法使用箭头函数创建方法链。因为在箭头函数中, this 没有绑定到对象的实例,而是全局对象 window 的引用。如果返回 this ,那么返回的不是对象本身而是 window 。

    另一个问题是从箭头函数内部访问和修改对象属性。由于 this 是全局对象 window ,所以不能用它来引用对象及其属性。

    如果你一定要使用箭头函数,必须想办法绕过这种方法。不用 this 来引用该对象,必须直接通过其名称引用该对象,也就是用对象名替换所有出现在箭头功能内的 this 。

    // 创建 person 对象
    const person = {
    name: 'Jack Doer',
    age: 41,
    state: null,
    logState() {
    console.log(this.state)
    },
    drink: () => {
    person.state = 'Drinking.'

    person.logState()

    return person
    },
    eat: () => {
    person.state = 'Eating.'

    person.logState()

    return person
    },
    exercise: () => {
    person.state = 'Exercising.'

    person.logState()

    return person
    },
    sleep: () => {
    person.state = 'Sleeping.'

    person.logState()

    return person
    },
    walk: () => {
    person.state = 'Walking.'

    person.logState()

    return person
    },
    work: () => {
    person.state = 'Working.'

    person.logState()

    return person
    }
    }

    //
    person
    .drink() // Output: 'Drinking.'
    .exercise() // Output: 'Exercising.'
    .eat() // Output: 'Eating.'
    .work() // Output: 'Working.'
    .walk() // Output: 'Walking.'
    .sleep() // Output: 'Sleeping.'

    这样做的缺点是灵活性不好。如果如果用Object.assign() 和 Object.create()复制对象,所有箭头函数仍然会硬连接到原始对象。

    // 创建原始 person 对象
    const person = {
    name: 'Jack Doer',
    age: 41,
    state: null,
    logState() {
    // 打印整个对象
    console.log(this)
    },
    drink: () => {
    person.state = 'Drinking.'

    person.logState()

    return person
    },
    eat: () => {
    person.state = 'Eating.'

    person.logState()

    return person
    }
    }

    // 让 person eat
    person.eat()
    // Output:
    // {
    // name: 'Jack Doer',
    // age: 41,
    // state: 'Eating.',
    // logState: ƒ,
    // drink: ƒ,
    // eat: ƒ
    // }

    // 基于person对象创建新对象。
    const newPerson = new Object(person)

    // 修改 "name" 和 "age" 属性
    newPerson.name = 'Jackie Holmes'
    newPerson.age = 33

    // 让 newPerson drink。
    // 这会打印 Jack Doer 而不是 Jackie Holmes。
    newPerson.drink()
    // Output:
    // {
    // name: 'Jack Doer',
    // age: 41,
    // state: 'Drinking.',
    // logState: ƒ,
    // drink: ƒ,
    // eat: ƒ
    // }

    但是,如果用  Object()  构造函数,就不会发生上述问题。如果用 new 关键字的和 Object() 构造造函数,将会创建独立的新对象。当你对这个新对象使用某个方法时,它将仅对这个新对象有效,而对原始对象无效。

    // 创建原始 person 对象
    const person = {
    name: 'Jack Doer',
    age: 41,
    state: null,
    logState() {
    // 打印整个对象
    console.log(this)
    },
    drink: () => {
    person.state = 'Drinking.'

    person.logState()

    return person
    },
    eat: () => {
    person.state = 'Eating.'

    person.logState()

    return person
    }
    }

    // 让 person eat.
    person.eat()
    // Output:
    // {
    // name: 'Jack Doer',
    // age: 41,
    // state: 'Eating.',
    // logState: ƒ,
    // drink: ƒ,
    // eat: ƒ
    // }

    // 基于 person 对象创建新对象
    const newPerson = new Object(person)

    // 修改 "name" 和 "age" 属性
    newPerson.name = 'Jackie Holmes'
    newPerson.age = 33

    // 让 newPerson drink.
    newPerson.drink()
    // Output:
    // {
    // name: 'Jackie Holmes',
    // age: 33,
    // state: 'Drinking.',
    // logState: ƒ,
    // drink: ƒ,
    // eat: ƒ
    // }

    如果你一定要用箭头功能,并想要复制对象的话,最好用 Object() 构造函数和 new 关键字创建这些副本。否则只需要用常规函数就够了。

    http://www.ssnd.com.cn 化妆品OEM代加工

    方法链和类

    如果你喜欢使用 JavaScript 类,也可以在JavaScript中使用方法链接。除了语法略又不同外,整个过程和对象是一样的。但是要注意所有可链的方法都必须返回 this 。

    // 创建 Person 类
    class Person {
    constructor(name, age) {
    this.name = name
    this.age = age
    this.state = null
    }

    logState() {
    console.log(this.state)
    }

    drink() {
    this.state = 'Drinking.'

    this.logState()

    return this
    }

    eat() {
    this.state = 'Eating.'

    this.logState()

    return this
    }

    sleep() {
    this.state = 'Sleeping.'

    this.logState()

    return this
    }
    }

    // 创建 Person 类的实例
    const joe = new Person('Joe', 55)

    // 使用方法链
    joe
    .drink() // Output: 'Drinking.'
    .eat() // Output: 'Eating.'
    .sleep() // Output: 'Sleeping.'

    方法链是非常有用的,它可以帮你编写更短、更易读的代码。

  • 相关阅读:
    Linux下链接数据库图形化工具
    基于深度学习的中文语音识别系统框架(pluse)
    Centos配置深度学习开发环境
    aishell数据处理为thchs30格式
    GRU-CTC中文语音识别
    深度学习知识树,别处传过来,用于自己知识整理。
    kaldi HMM-GMM全部训练脚本分解
    使用深度学习来破解 captcha 验证码(转)
    kaldi
    神经网络最优化方法
  • 原文地址:https://www.cnblogs.com/qianxiaox/p/15012756.html
Copyright © 2020-2023  润新知