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 > true false == 0 > true
-
===
严格等于
'1' === 1 > false false === 0 > false
-
!
逻辑取反
for(let i=0; i<100; i++){ // 注意运算优先级的问题, 不能写成!i%2 if (!(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_name constructor(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) } }