JavaScript进阶
循环语句
-
for循环
// 类似python中的for i in range(20)for(let i=0; i<20; i++){console.log(i) }
-
while循环
const MAX_TIMES = 20; let cur = 0 while (cur < MAX_TIMES){cur++;console.log(cur) }
-
do while
do {cur ++;console.log(cur); }while (cur < MAX_TIMES)
-
和
while循环
有什么区别?do while
一定先执行一遍代码块的表达式;
-
-
for in
遍历对象的属性
let myObj = {a: 1, b:2, c:3, d:4} for (let e in myObj){console.log(e, myObj[e]); }
-
for of
遍历可迭代对象的元素
let myArray = [1, 2, 3, 4, 5] for (let e of myArray){console.log(e); }
-
常见的可迭代对象有哪些?
-
Array
-
Set
-
Map
-
String
-
arguments
function foo(a, b){console.log(arguments); }foo(1, 2)
-
-
-
forEach
let myArray = [1, 2, 3, 4, 5] myArray.forEach(function (e){console.log(e * e); })
条件语句
-
一个简单的判断语句
for(let i=0; i<100; i++){if (i%2===0){console.log("偶数", i)}else if(i < 0){console.log("负数不判断")}else{console.log("奇数", i)} }
-
逻辑运算符
-
==
对比操作数是否相同, 操作数会尝试进行类型转换后的比较, 不推荐做为比较符号.
'1' == 1 > truefalse == 0 > true
-
===
严格等于
'1' === 1 > falsefalse === 0 > false
-
!
逻辑取反
for(let i=0; i<100; i++){// 注意运算优先级的问题, 不能写成!i%2if (!(i%2)){console.log("偶数", i)}else if(i < 0){console.log("负数不判断")}else{console.log("奇数", i)} }
-
&&
和||
&&
表示AND||
表示OR
-
选择语句
-
switch case
function foo(arg){switch (arg){case 'a':console.log(arg, 1);break;case 'b':console.log(arg, 2);break;case 'c':console.log(arg, 3);break;default:console.log('default')} }foo('e')
异常处理
try{表达式
}catch (e){表达式
}finally{表达式
}
-
一个基本的异常捕获
function foo(){try{throw TypeError('test');}catch (e){console.log('Error', e);}finally{console.log('Done!')} }foo()
-
处理具体的异常
处理具体的异常, 只能通过if条件语句来进行类型判断
function foo(){try{throw TypeError('test');}catch (e){if (e instanceof TypeError){console.log("TypeError")}else{console.log('Error', e);}}finally{console.log('Done!')} }foo()
-
抛出异常
throw
可以抛出任意对象, 让catch
去捕获function foo(){try{throw {'a':1};}catch (e){if (e instanceof TypeError){console.log("TypeError")}else{console.log('Error', e);}}finally{console.log('Done!')} }foo()
对象和类
js当中其实没有明确的类的概念, js当中的类只是创建对象的模板. 它的本质还是一个特殊的函数
class
关键字只是一层封装了原型链的语法糖, 但是方便我们理解.
-
声明类
class Rectangle {constructor(height, width) {this.name = 'ractangle';this.height = height;this.width = width;} }
-
创建对象/实例
let rectangle = new Rectangle(2, 5); console.log(rectangle)
-
类方法
-
构造方法
constructor
是一种用来创建和初始化class
创建的对象; -
实例方法
class Rectangle {constructor(height, width) {this.name = 'ractangle';this.height = height;this.width = width;}getArea(){return this.height * this.width;} }
-
静态方法
static staticMethod(){console.log("calling static method")}
-
getter
和setter
get area(){return this.getArea()}set area(value){this._value = value}
-
-
类继承
class Square extends Rectangle {constructor(a) {super(a, a);this.name = 'square'} }let suqare = new Square(10) console.log(suqare.area)
-
私有方法和属性
通过
#
来声明一个私有方法或者属性, 只允许类内部调用class Square extends Rectangle {// 私有属性需要事先进行声明#new_nameconstructor(a) {super(a, a);this.#new_name = 'square'}get new_name(){return this.#getName()}#getName(){return this.#new_name} }
构造函数和this
在JS中通过构造函数来创建对象的场景更多, 而不是通过class
-
声明一个构造函数
function Rectangle(height, width){this.name = 'rectangle';this.width = width;this.height = height;this.getArea = function (){return this.height * this.width} }
-
创建对象/实例
let rectangle = new Rectangle(10, 2) console.log(rectangle.getArea())
-
通过
call
方法继承第一个参数是要绑定的对象, 其他参数代表调用函数的参数.
call
方法将Rectangle
下的属性和方法绑定到this
上去这里的this指代的就是实例化的square对象
function Square(a){Rectangle.call(this, a, a);this.name = 'square' }
-
apply
和
call
方法几乎没有区别, 只是传入的参数的方式不同function Square(a){Rectangle.apply(this, [a, a]);this.name = 'square' }
-
-
this
不能简单地认为
this
指向的就是实例对象.可以简单地认为谁调用该函数,
this
就指向谁.
原型链式继承
在传统面向对象编程中, 我们继承的实现方式都是在创建实例时, 将类中定义的属性和方法都复制到实例中去.
但是JS中继承是在对象/实例和它的构造器之间创立一个链接, 这个链接就是__proto__
-
原型链
在
js
中每个构造函数拥有一个原型对象prototype
, 对象从原型继承方法和属性. 而当前对象继承的原型对象可能也有原型, 这样就形成了一条原型链square.__proto__.__proto__.__proto__
-
__proto__
和prototype
的区别?__proto__
是每个对象/实例都有的属性, 而prototype
只是构造函数的属性.
-
-
原型链式继承
当前对象/实例square找不到getArea方法时, 会继续在原型链中寻找.
function Square(a){this.height = a;this.width = a;this.name = 'square' }Square.prototype = new Rectangle() let square = new Square(10) console.log(square.getArea())
异步JS
JS引擎只是浏览器中的一个线程, 所以在JS当中没有线程和进程的概念.JS的高性能是通过一个基于事件循环的异步并发模型来完成.
-
事件循环
在页面环境中, 我们移动鼠标, 点击页面元素或者执行一段JS, 都可以认为当前是一个消息. 当前的消息会被丢进一个无限循环的消息队列当中, 每一个消息都关联着用来处理这个消息的回调函数
document.addEventListener("click", function(e){console.log(e)})
-
事件循环的伪代码
while(queue.waitForMessage()){queue.processNextMessage(); }
-
消息的执行
- JS在执行同步消息任务时, 会执行至完成.
- JS在执行异步消息任务时, 遇到异步操作, 会将该消息丢回消息队列, 等待下一次调度执行.
-
callbacks(异步回调)
最常见于浏览器的监听事件, 本质就是将回调函数做为参数传递给后台执行的其他函数, 其他函数在执行完毕后自动调用回调函数
document.addEventListener("click", function(e){console.log(e)})
-
回调地狱
// 银行转账的伪代码 transferAccount(cash,dealCash(cash, function(cash_status){authAccount(cash_status, function(order){transferStart(order, function(status){sendSMS(status)}, transferExceptionCallback)}, authExceptionCallback)}, cashExceptionCallback) )
Promise
Promise
是一个对象, 它代表了一个异步操作的最终完成或者失败的结果.
-
声明一个简单的
promise
对象let promiseOjb = new Promise((resolve, reject) => {setTimeout(() => {resolve("success");reject("failed");}, 3*1000) }).then(result => {console.log("result => ", result)})console.log(promiseOjb)
-
promise
的状态-
pending
初始状态, 表示未接收到结果
-
fullfill
已兑现, 表示操作成功完成
-
reject
已拒绝, 表示操作失败
-
-
在使用
promise
时, 需要注意以下约定- 回调函数时在当前事件循环结束后自动调用的;
- 通过
then
添加的回调函数不管异步操作是否成功都会执行; - 通过调用多次
then
可以添加多个回调函数, 他们按插入顺序依次执行, 这个方式就叫做链式调用;
-
在
promise
中处理异常-
声明一个简单的xhr请求
function xhrRequest(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onload = function(){resolve(xhr.responseText)};xhr.onerror = () => reject(xhr.status);xhr.send()}).then(result=>{console.log("SUCCESS", result)}).catch(error=>{console.log("FAILURE", error)}) }xhrRequest("http://www.baidu.com") xhrRequest("https://www.baidu.com") xhrRequest("https://www.baidu.com/asdfasdf/")
- 当前环境下, 客户端请求到达了服务端, 服务端也正常地返回了结果. 那么不管状态码是200还是404, 都算是一次成功的请求, 所以结果由
then
回调进行处理, 而不是catch
. 如果有需要可以自己通过状态码判断后执行不同的流程.
- 当前环境下, 客户端请求到达了服务端, 服务端也正常地返回了结果. 那么不管状态码是200还是404, 都算是一次成功的请求, 所以结果由
-
链式调用
function xhrRequest(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onload = function(){resolve(xhr.responseText)};xhr.onerror = () => reject(xhr.status);xhr.send()}).then(result=>{console.log("SUCCESS", result); return result}).then(result=>{console.log("SUCCES 2", result)}).catch(error=>{console.log("FAILURE", error)}) }
- 不管当前链式调用有多长, 异常都会进到
catch
回调函数当中.catch
也可以进行链式调用, 但是一般一个函数只有一个catch
回调函数.
- 不管当前链式调用有多长, 异常都会进到
-
async/await
async
本质就是将函数转换为promise
-
通过
async
和await
等待完成结果function xhrRequest(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onload = function(){resolve(xhr.responseText)};xhr.onerror = () => reject(xhr.status);xhr.send()}) }async function requestBaidu(url){let result = await xhrRequest(url);console.log("DONE!", result) }
-
异常处理
function xhrRequest(url){return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open('GET', url);xhr.onload = function(){resolve(xhr.responseText)};xhr.onerror = () => reject(xhr.status);xhr.send()}) }async function requestBaidu(url){try{let result = await xhrRequest(url);console.log("DONE!", result)}catch (e){console.log("FAILURE", e)} }