当前位置 : 主页 > 编程语言 > java >

JavaScript小技能:原型链的运作机制、Promise链

来源:互联网 收集:自由互联 发布时间:2022-10-26
引言 回调函数是一个被传递到另一个函数中的会在适当的时候被调用的函数,如事件处理程序就是一种特殊类型的回调函数。 由于嵌套回调导致处理错误变得非常困难,代码也更难阅


引言

  • 回调函数是一个被传递到另一个函数中的会在适当的时候被调用的函数,如事件处理程序就是一种特殊类型的回调函数。
  • 由于嵌套回调导致处理错误变得非常困难,代码也更难阅读和调试,所以JavaScript 中的异步编程是基于 Promise实现。

  • Promise是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。在基于 Promise 的 API 中,异步函数会启动操作并返回 Promise 对象。然后你可以将处理函数附加到 Promise 对象上,当操作完成时(成功或失败),这些处理函数将被执行。
  • 在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)。

  • JavaScript 通过​​原型链​​而不是类来支持面向对象编程
  • JavaScript 常被描述为一种基于原型的语言 (​​prototype-based language​​​)——​​每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性​​。

    原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (​​prototype chain​​),

    这些属性和方法定义在 Object 的构造器函数 (constructor functions) 之上的prototype属性上,而非对象实例本身。

  • JavaScript 同样支持函数式编程和链式编程。函数也可以被保存在变量中,并且像其他对象一样被传递。典型代表:Promise 链
  • 函数式编程:把操作尽量写成一系列嵌套的函数或者方法调用。

    函数式编程特点:​​每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)​​

    document.querySelector('html').onclick = function() {
    alert('别戳我,我怕疼。');
    }

    document.querySelector('html').addEventListener('click', () => {
    alert('别戳我,我怕疼。');
    });

    链式编程: 将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好a(1).b(2).c(3)。

    链式编程特点:方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)

    iOS小技能:链式编程在iOS开发中的应用​

    const fetchPromise = fetch('bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');

    fetchPromise
    .then( response {
    if (!response.ok) {
    throw new Error(`HTTP 请求错误:${response.status}`);
    }
    return response.json();
    })
    .then( json {
    console.log(json[0].name);
    })
    .catch( error {
    console.error(`无法获取产品列表:${error}`);
    });

    I 原型链的运作机制

    1.1 原型链的运作机制

    JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,这种关系常被称为​​原型链 (prototype chain)​​。

    JavaScript小技能:原型链的运作机制、Promise链_掘金·日新计划

    属性和方法定义在 Object 的构造器函数 (constructor functions) 之上的prototype属性上,而非对象实例本身。在对象实例和它的构造器之间建立一个链接(它是​​__proto__​​属性,是从构造函数的prototype属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。

    通过原型链继承的例子: 当你创建一个字符串​​var myString = 'This is my string.';​​​ ​​myString ​​​立即拥有了​​split()​​​、​​indexOf()​​​、​​replace()​​ 等方法。

    1.2 对象原型prototype 属性:继承成员被定义的地方

    prototype 属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都被储存在其中。

    于是 ​​Object.prototype.watch()​​​、​​Object.prototype.valueOf() ​​​等等成员,适用于任何继承自 Object() 的对象类型,包括使用构造器创建的实例。 ​​​Object.is()​​​、​​Object.keys()​​,以及其他不在 prototype 对象内的成员,不会被继承自 Object() 的对象类型”所继承。

    对象的原型是每个实例上都有的属性,可以通过​​Object.getPrototypeOf(obj)​​​获得。​​Object.getPrototypeOf(new Foobar())​​​和​​Foobar.prototype​​指向着同一个对象。

    函数也是一个对象类型,每个函数都有一个特殊的属性叫作原型(​​prototype​​)

    JavaScript小技能:原型链的运作机制、Promise链_json_02

    添加一些属性到 doSomething 的原型​​prototype​​

    JavaScript小技能:原型链的运作机制、Promise链_json_03

    1.3 修改原型

    prototype 属性添加一个新的方法farewell:

    function Person(first, last, age, gender, interests) {
    // 属性与方法定义
    };
    //创建了一个对象实例
    var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);
    //prototype 添加了一个新的方法
    Person.prototype.farewell = function() {
    alert(this.name.first + ' has left the building. Bye for now!');
    }

    prototype 属性添加一个新的属性foo

    function doSomething(){}
    doSomething.prototype.foo = "bar"; // add a property onto the prototype
    //使用 new 运算符来在现在的这个原型基础之上,创建一个 doSomething 的实例。
    var doSomeInstancing = new doSomething();
    doSomeInstancing.prop = "some value"; // add a property onto the object
    console.log("doSomeInstancing.prop: " + doSomeInstancing.prop);
    console.log("doSomeInstancing.foo: " + doSomeInstancing.foo);
    console.log("doSomething.prop: " + doSomething.prop);
    console.log("doSomething.foo: " + doSomething.foo);
    console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);
    console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);

    JavaScript小技能:原型链的运作机制、Promise链_掘金·日新计划_04

    当你访问 doSomeInstancing 的一个属性,浏览器首先查找 doSomeInstancing 是否有这个属性。如果 doSomeInstancing 没有这个属性,然后浏览器就会在 doSomeInstancing 的​​__proto__​​​中查找这个属性 (也就是 doSomething.prototype). 如果 doSomeInstancing 的 ​​__proto__​​​ 有这个属性,那么 doSomeInstancing 的 ​​__proto__​​​ 上的这个属性就会被使用。否则,如果 doSomeInstancing 的​​__proto__​​​没有这个属性,浏览器就会去查找 doSomeInstancing 的​​__proto__​​​的 ​​__proto__ ​​​,看它是否有这个属性。最后,原型链上面的所有的​​ __proto__​​​ 都被找完了,浏览器所有已经声明了的​​__proto__​​上都不存在这个属性,然后就得出结论,这个属性是 undefined.

    默认情况下,所有函数的原型属性的​​__proto__​​​就是 ​​window.Object.prototype​​

    1.4 对象定义模式

    在构造器(函数体)中定义属性、在 prototype 属性上定义方法。这样,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。

    // 构造器及其属性定义

    function Test(a,b,c,d) {
    // 属性定义
    };

    // 定义第一个方法

    Test.prototype.x = function () { ... }

    // 定义第二个方法

    Test.prototype.y = function () { ... }

    // 等等……

    II Promise链

    Promise是一个由异步函数返回的可以向我们指示当前操作所处的状态的对象。

    2.1 Promise状态

    Promise 有三种状态:

    • 待定(pending):初始状态,这是调用 fetch() 返回 Promise 时的状态,此时请求还在进行中。
    • 已执行完毕(fulfilled):意味着操作成功完成。当 Promise 完成时,它的 then() 处理函数被调用。

    fetch() 认为服务器返回一个错误(如404 Not Found)时请求成功,但如果网络错误阻止请求被发送,则认为请求失败。

    • 已拒绝(rejected):意味着操作失败。当一个 Promise 失败时,它的 catch() 处理函数被调用。

    在基于 Promise 的 API 中,异步函数会启动操作并返回 Promise 对象。然后你可以将处理函数附加到 Promise 对象上,当操作完成时(成功或失败),这些处理函数将被执行。

    2.2 Promise链

    当你的操作由几个异步函数组成,而且你需要在开始下一个函数之前完成之前每一个函数时,你需要的就是 Promise 链。

    const fetchPromise = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
    //链式使用 Promise
    fetchPromise
    .then( response {
    return response.json();//json() 也是异步的,response.json() 返回的是 Promise对象
    })
    .then( json {
    console.log(json[0].name);
    });

    2.3 合并使用多个 Promise

    应用场景:需要所有的 Promise 都得到实现,但它们并不相互依赖。

    实现方式1:​​ Promise.all() 方法​​。它接收一个 Promise 数组,并返回一个单一的 Promise。

    const fetchPromise1 = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
    const fetchPromise2 = fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found');
    const fetchPromise3 = fetch('https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json');

    Promise.all([fetchPromise1, fetchPromise2, fetchPromise3])
    .then( responses {//当且仅当数组中所有的 Promise 都被兑现时,才会通知 then() 处理函数并提供一个包含所有响应的数组,数组中响应的顺序与被传入 all() 的 Promise 的顺序相同。

    for (const response of responses) {
    console.log(`${response.url}:${response.status}`);
    }
    })
    .catch( error {//如果数组中有任何一个 Promise 被拒绝。此时,catch() 处理函数被调用,并提供被拒绝的 Promise 所抛出的错误。
    console.error(`获取失败:${error}`)
    });

    实现方式2: ​​Promise.any()​​。在 Promise 数组中的任何一个被兑现时它就会被兑现,如果所有的 Promise 都被拒绝,它也会被拒绝。

    2.4 async 和 await

    在一个函数的开头添加 async,就可以使其成为一个异步函数。

    async function myFunction() {
    // 这是一个异步函数
    //在异步函数中,你可以在调用一个返回 Promise 的函数之前使用 await 关键字。这使得代码在该点上等待,直到 Promise 被完成,这时 Promise 的响应被当作返回值,或者被拒绝的响应被作为错误抛出。

    await 强制异步操作以串联的方式完成

    async function fetchProducts() {
    try {
    // 在这一行之后,我们的函数将等待 `fetch()` 调用完成
    // 调用 `fetch()` 将返回一个“响应”或抛出一个错误
    const response = await fetch('https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json');
    if (!response.ok) {
    throw new Error(`HTTP 请求错误:${response.status}`);
    }
    // 在这一行之后,我们的函数将等待 `response.json()` 的调用完成
    // `response.json()` 调用将返回 JSON 对象或抛出一个错误
    const json = await response.json();//调用者得到的并不是 Promise,而是一个完整的 Response 对象,就好像 fetch() 是一个同步函数一样
    console.log(json[0].name);
    }
    catch(error) {
    console.error(`无法获取产品列表:${error}`);
    }
    }


    const jsonPromise = fetchProducts();////异步函数总是返回一个 Pomise
    jsonPromise.then((json) => console.log(json[0].name));

    2.5 实现返回 promises 的 APIs

    Promise() 构造器使用单个函数作为参数,把这个函数称作执行器(executor)。当你创建一个新的 promise 的时候你需要实现这个执行器,这个执行器本身采用两个参数,这两个参数都是函数,通常被称作 resolve 和 reject。

    //以被唤醒人的名字和一个在人被唤醒前以毫秒为单位的延迟作为参数。在延迟之后,本函数将会发送一个包含需要被唤醒人名字的 "Wake up!" 消息。
    function alarm(person, delay) {
    return new Promise((resolve, reject) => {
    if (delay < 0) {//检查 delay(延迟)是否为负数,如果是的话就抛出一个错误。
    throw new Error('Alarm delay must not be negative');
    }
    window.setTimeout(() => {
    resolve(`Wake up, ${person}!`);
    }, delay);
    });
    }

    III JSON(JavaScript Object Notation)

    是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

  • JSON用于在网站上表示和传输数据
  • ////通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。
    //创建一个新的 XMLHttpRequest 并监听它的 loadend 事件

    request.open('GET', requestURL);
    request.responseType = 'text'; // now we're getting a string!
    request.send();
    //以事件处理程序属性形式关联事件处理器
    request.onload = function() {
    var superHeroesText = request.response; // get the string from the response
    var superHeroes = JSON.parse(superHeroesText); // convert it to an object
    populateHeader(superHeroes);
    }
    //通过DOM Level 2 Events 函数 addEventListener()关联事件处理器
    xhr.addEventListener('loadend', () => {
    log.textContent = `${log.textContent}完成!状态码:${xhr.status}`;
    });
  • JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
  • //JSON 格式创建一个对象
    var oo = {
    name:"hello",
    age:123,
    getName:function(){
    return oo.name;
    }

    }
  • JSON 和 JS 对象互转: 利用浏览器内建的 JSON进行转换数据
  • //parse(): 以文本字符串形式接受 JSON 对象作为参数,并返回相应的对象。
    var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}
    //stringify(): 接收一个对象作为参数,返回一个对应的 JSON 字符串。
    var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'
  • 深拷贝多级对象
  • //深拷贝多级对象
    const cloneObj = JSON.parse(JSON.stringify(obj));//JSON.stringify 对象的时候,包含 function, undefined or NaN 值的属性会从对象中移除。
    //Shorthand for single level object
    let obj = {x: 20, y: 'hello'};
    const cloneObj = {...obj};


    网友评论