引言
由于嵌套回调导致处理错误变得非常困难,代码也更难阅读和调试,所以JavaScript 中的异步编程是基于 Promise实现。
在 Promise 返回给调用者的时候,操作往往还没有完成,但 Promise 对象可以让我们操作最终完成时对其进行处理(无论成功还是失败)。
JavaScript 常被描述为一种基于原型的语言 (prototype-based language)——每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。
原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),
这些属性和方法定义在 Object 的构造器函数 (constructor functions) 之上的prototype属性上,而非对象实例本身。
函数式编程:把操作尽量写成一系列嵌套的函数或者方法调用。
函数式编程特点:每个方法必须有返回值(本身对象),把函数或者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)。
属性和方法定义在 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)
添加一些属性到 doSomething 的原型prototype
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);
当你访问 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规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。
//创建一个新的 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}`;
});
var oo = {
name:"hello",
age:123,
getName:function(){
return oo.name;
}
}
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};