All syntax of Javascript
var
用
var
声明定义变量会有提升
,如下 ????function foo() { console.log(num); var num = 20; } foo(); // 控制台打印 undefined 复制代码
之所以没有报错,是因为
js
解析的时候看成等价于如下代码:function foo() { var num; console.log(num) num = 20; } foo(); // 变量已声明,但未赋值,所以打印 undefined 复制代码
这就是所谓的提升,把所有变量声明都拉到当前作用域顶部,即可以在
变量未声明之前引用
。因此,反复多次使用
var
声明同一个变量也没有问题,解析时会认为在作用域顶部进行了变量声明,接着对变量多次赋值,后边的值覆盖前边的值。let
let
和var
差不多,最主要的区别在于:let
声明的范围是块作用域
,且没有变量提升,var
声明的范围是函数作用域
????// var 声明 if(true){ var num = 1; console.log(1); } console.log(num); // 控制台打印 1 // let 声明 if(true){ let num = 1; console.log(num); } console.log(num); // 报错 Uncaught ReferenceError: num is not defined 复制代码
之所以不能被引用,是因为用
let
声明的num
只能作用域该块内部。let
不允许同一块作用域内多次声明同一个变量名在循环中使用 var 和 let 的区别
// var for(var n = 0; n < 5 ; n++){ setTimeout(() => console.log(n);, 0); } // 打印 5 5 5 5 5 // let for(let n = 0; n < 5 ; n++){ setTimeout(() => console.log(n);, 0); } // 打印 0 1 2 3 4 复制代码
const
同
let
一样,作用范围为块作用域,没有变量提升,唯一区别是:const
声明时必须初始化变量,且只能声明常量
,即不可修改的量。如果
const
声明的变量值为一个对象,对其内部属性修改并不违反const
的限制。// const 声明 if(true){ const num = 2; console.log(num); } console.log(num); // 报错 num 未定义 const obj = {}; obj.name = 'Javascript'; // 不会出错 复制代码
使用
var
声明的全局变量和函数会成为window
对象的属性和方法,而let
和const
不会。
TDZ(暂时性死区)
上边说到,
var
声明的变量在作用域中会有提升,即在声明之前可以引用,而用let
声明的变量没有提升,在声明之前引用会报错,所以在let
声明之前的执行瞬间被称为
暂时性死区(Temporal Dead Zone)
。
数据类型
Javascript
有6
种基本类型和3
种引用类型
基本类型
Number
String
Boolean
Undefined
Null
Symbol(ES6新增)
引用类型
Array
Function
Object
流程语句
if
语句do-while
语句while
语句for
语句for-in
语句for-of
语句可迭代对象可使用,即
iterator
。switch
语句continue
和break
变量、作用域和内存
数据分为
原始值(基本类型)
和引用值(引用类型)
,原始值
存储在栈
中,引用值
的引用变量名 存储在栈
中,实际对象存储在堆
中。
因此,当两类值进行赋值和复制时有所不同
// 原始值(基本类型) let num1 = 5; let num2 = num1; // 将 num1 值为 5,num2 初始化为 num1 时,num2 也为 5. 两个变量是完全独立的,就是把一个栈房间的东西复制到另一个栈房间,互不干涉。 如图: 复制代码
// 引用值(引用类型) let obj1 = { num : 'Javascript', age : 26 } let obj2 = obj1; // obj1 的值会复制给 obj2,但实际上给 obj2 的是一个指针(地址),该指针指向存在堆内存中的对象。 复制代码
因为
JavaScript
不能直接操作内存,所以在操作对象时实际上操作的是该对象的引用
而非其本身,obj1
和obj2
实际上为真实对象的地址,二者都指向存储在堆内存的真实对象。 ????
当通过方法改变
obj1
及obj2
任意一个的值时,改变的是堆内存
中的原始对象,所以另一个也会改变。
类型判断
typeof
// 使用 typeof 判断一个变量是否为原始类型 let a = 22; let b = 'hello'; let c = true; let d; let e = null; console.log(typeof a); // number console.log(typeof b); // string console.log(typeof c); // boolean console.log(typeof d); // undefined console.log(typeof e); // object 复制代码
typeof
对原始值很有用,但对引用值用处不大,我们知道一个引用值是对象,但我们不关心他是不是对象,而是关心它是什么类型的对象所以,JavaScript 提供了一个
instanceof
操作符 ????instanceof
// instanceof let f = { name : 'javascript' }; let g = function(){ console.log(11) }; let h = [1,2,3,4,5]; console.log(f instanceof Object); // true console.log(g instanceof Function); // true consoel.log(h instanceof Array); // true 复制代码
instanceof
通过检测引用类型变量的原型链的构造函数返回布尔值,true
表示为该应用类型,false
反之。Object.prototype.toString()
typeof
只能用来判断原始类型的变量,instanceof
只能用来判断引用类型的变量。当我们只有一个变量,而不知道它是何类型时,
Object.prototype.toString()
能帮助我们更精确地判断类型。// Object.prototype.toString() let a = 22; let b = 'hello'; let c = true; let d; let e = null; let f = { name: 'javascript' }; let g = function () { console.log(11) }; let h = [1, 2, 3, 4, 5]; function type(val){ return Object.prototype.toString.call(val) } console.log(type(a)); // [object Number] console.log(type(b)); // [object String] console.log(type(c)); // [object Boolean] console.log(type(d)); // [object Undefined] console.log(type(e)); // [object Null] console.log(type(f)); // [object Object] console.log(type(g)); // [object Function] console.log(type(h)); // [object Array] 复制代码
作用域和作用域链
作用域
作用域即变量可使用的范围,当声明全局变量时,该变量在当前 js 文件或当前 script 标签内都可访问。
当在函数内部声明变量时,该变量为局部变量,仅在此函数内可访问。
// index.js 全局变量 let a = 26; function foo(){ return ++aa; } foo(); // 27 复制代码
a
为全局变量,所以foo
函数可以在内部访问a
,并返回结果。
// index.js 局部变量 function fo(){ let bb = 26; console.log(bb); } fo(); // 26 console.log(bb); // error Uncaught ReferenceError: bb is not defined 复制代码
此例在
fo
函数内部声明一个变量bb
,该变量为局部变量,只能在该函数内部访问。
作用域链
顾名思义,作用域为一条链,在链内都可访问。
// index.js let cc = 26; function test(){ function qq(){ function ww(){ console.log(cc); } ww(); } qq(); } test(); // 打印 26 复制代码
当代码嵌套比较深得时候,最内层的嵌套访问变量时会按照作用域链依次向上寻找,直到找到为止。如图:
垃圾回收
标记清理
引用计数(Javascript 已不再使用该算法,某些旧版本 IE 仍然使用)
基本引用类型
Date
日期对象,含有操作日期的各种方法。
最简单的应用:
let currDate = new Date(); console.log(currDate); // 设置一个指定日期 let date = new Date('2021/01/01'); console.log(date); // Fri Jan 01 2021 00:00:00 GMT+0800 (中国标准时间) 复制代码
常用方法
date.toLocalString() 将日期转为本地字符串日期
date.getFullYear() 获取年份
date.getMonth() 获取月份,从0开始,即 0 == 1 月
date.getDay() 获取星期,0-6
date.getDate() 获取天数
date.getHours() 获取小时
date.getMinntes() 获取分钟
date.getSeconds() 获取秒钟
date.getMilliseconds() 获取秒钟
date.getTime() 获取从计算元年( 1970/01/01 00:00:00 )开始到现在过了多少毫秒
Date.now() 立刻获得当前时间戳的毫秒值
date.toLocalDateString() 返回本地年月日字符串
toLocalTimeString() 返回本地时间字符串
RegExp
正则表达式,该对象内容偏多且我很少使用,不多叙述,可自行查询。
包装类(String、Number、Boolean)
都知道这三种类型为基本数据类型,但当我们对它们进行赋值及其他操作时,
js
会在后台“偷偷的”转为包装类,调用属性及方法时也如此,调用完以后会将其再转为基本数据类型。例如:
let str = 'javascript' let idx = str.indexOf('j') console.log(idx) // 0 // 字面上可以看到,我们是直接通过基本数据类型调用的方法,实则不然。 // 其实是: let str = new String('javascript'); let idx = str.indexOf('j'); console.log(idx); // 0 复制代码
只有对象才会有方法,因此我们操作的基本数据类型的方法都是
js
“偷偷”转为包装类
后再调用的。
Global(Window)
????window作为JS Global对象
Math
该对象内含有所有数学操作的属性及方法
常用属性及方法
Math.PI 返回
π
值Math.abs() 取绝对值
Math.max() 取最大值
Math.min() 取最小值
Math.floor() 向下取整,不管小数位
Math.ceil() 向上取整,小数位干掉,整数位+1
Math.trunc() 向下取整,直接干掉小数位
Math.round() 四舍五入
Math.pow(x, y) 返回
x的y次方
,简写:x ** y
Math.sqrt() 立方根
Math.cbrt() 平方根
Math.random() 随机数,0-1之间
Math.tan(rad)
Math.sin(rad)
Math.cos(rad)
集合引用类型
Object
Array
Map
可迭代,即可使用
for...of...
循环,keys()
为实例中键值对的键的集合,values()
为所有值得集合,entries()
为实例中所有键值对的集合
基本API
get( ) 获取参数对应的值
set( ) 为
Map
实例映射(添加)键值对,返回映射后的实例,因此set()
可以链式使用has( ) 判断是否含有参数对应的值,返回布尔值
delete( ) 删除参数对应的键值对
clear( ) 清空
Map
实例的键值对
WeakMap
不可迭代,且没有
clear()
方法,其他方法同Map
,唯一区别在于WeakMap
键值对的键只能是Object
或继承自Object
的类型。
Set
可迭代,因为
Set
没有键,所以keys()、values()、entries()
返回的迭代器对象都是值。
基本API
add( ) 添加值,可链式使用
has( ) 是否含有参数值,返回布尔值
delete( ) 删除参数成员,返回布尔值
clear( ) 清空
Set
实例成员
WeakSet
不可迭代,没有
clear()
方法,其他同Set
,唯一区别在于WeakSet
键值对的键只能是Object
或继承自Object
的类型。
迭代器与生成器
Iterator
迭代器是一种接口、是一种机制。
为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:
为各种数据结构提供一个统一的、简便的访问接口;
使得数据结构的成员能够按某种次序排列;
主要供
for...of
消费。
Iterator本质上,就是一个指针对象。
过程
创建一个指针对象,指向当前数据结构的起始位置。
第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。不断调用指针对象的
next
方法,直到它指向数据结构的结束位置。普通函数实现
iterator
function myIter(obj){ let i = 0; return { next(){ let done = (i >= obj.length); let value = !done ? obj[i++] : undefined; return { value, done, } } } } 复制代码
原生具备 Iterator 接口的数据结构
Array
Map
Set
String
函数的 arguments 对象
NodeList对象
Generator
对象、类与面向对象编程
Object
声明对象的方式:1. 构造函数 2. 字面量
// 声明对象 // 1. 构造函数 let obj1 = new Object({ name:'javascript' }) // 2. 字面量 let obj2 = { name: 'javascript' } 复制代码
常用的对象API
Object.defineProperty( obj, property, desc )
为某一个对象的某一个属性定义描述,包括configurable、writable、enumerable、value、get、set
Object.getOwnPropertyDescriptor( obj, property )
获取某一个对象的某一个属性的描述,返回描述对象
Object.create( obj )
以某个对象为原型创建新的对象
Object.freeze( obj )
冻结某个对象,即某个对象使用该方法后不可被修改
Object.keys( obj )
获取某个对象中所有可遍历( enumerable )的属性的键名,返回值为一个数组,所以可使用
for...of...
Object.values( obj )
获取某个对象中所有可遍历( enumerable )的属性的键值,返回值为一个数组,所以可使用
for...of...
Object.entries( obj )
获取某个对象中所有可遍历( enumerable )的属性的键值对,返回值为一个数组,所以可使用
for...of...
Object.is( obj1, obj2 )
判断对个对象或对个任意值是否相等。
Object.assign( targetObj, obj )
将一个对象合并到另一个对象,第一个参数为目标对象,其余参数为被合并的对象
obj.hasOwnProperty( property )
判断某个对象是否含有某个属性,返回布尔值
原型和原型链
类 Class
代理和反射
proxy
用于修改某些操作的默认行为。可以理解为在对象操作之前设置一层“拦截”,所有操作访问必须通过这层拦截,可以对外界的访问进行过滤和修改。
// proxy let obj = { name: 'javascript', age: 26 } const proxy = new Proxy(obj,{ get(target,property,receiver){ throw 'Sorry,this property can not be accessed.' }, set(target,property,value,receiver){ throw 'Sorry,this object can not be modified.' } }) proxy.name // 抛出错误 Uncaught Sorry,this property can not be accessed. proxy.grade = 'first' // 抛出错误 Uncaught Sorry,this object can not be modified. 复制代码
被代理的对象,当只有通过代理对象访问时才会被拦截,通过原对象访问不会被拦截。
上边代理对象设置访问拦截和赋值拦截,进行操作时会按照拦截的结果返回。
proxy支持多种拦截:
get(target, property, receiver)
拦截对象属性的读取,如:
proxy.name
或proxy[name]
。set(target, property, value, receiver)
拦截对象属性的赋值,如:
proxy.name = 'aa'
或proxy[name] = 'aa'
。has(target, propKey)
拦截
propKey in proxy
的操作。deleteProperty(target, propKey)
拦截
delete proxy[propKey]
的操作。ownKeys(target)
拦截
Object.keys(proxy)
、Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
的操作,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。denfineProperty(target, propKey, propDesc)
拦截
Object.defineProperty(proxy,propKey,propDesc)
和Object.defineProperties(proxy,propKey,propDesc)
的操作。getOwnPropertyDescriptor(target, propKey)
拦截
getOwnPropertyDescriptor(proxy,propKey)
,返回属性的描述对象getPrototypeOf(target)
拦截
Object.getPropertyOf(proxy)
,返回一个对象。preventExtensions(target)
拦截
Object.preventExtensions(proxy)
,返回一个布尔值。isExtensible(target)
拦截
Object.isExtensible(proxy)
,返回一个布尔值。setPrototypeOf(target, proto)
拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。apply(target, object, args)
拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。construct(target, args)
拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
。
Proxy.revocable( target, handler )
返回一个可取消代理的对象,对象有
proxy
和revoke()
,proxy
为代理对象,revoke()
为取消代理的函数
// Proxy.revocable() let target = {}; let handler = {}; let proxyObj = Proxy.revocable(target, handler); console.log(proxyObj) // {proxy: Proxy, revoke: ƒ} proxyObj.name = 'javascript' console.log(proxyObj.name) // javascript proxyObj.revoke() console.log(proxyObj.name) // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked 复制代码
取消代理后就不能再通过代理对象读取或进行其他操作。
Proxy.revocable( target, handler )
的应用场景为:目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
代理的应用场景是不可限量的。使用它可以创建出各种编码模式,比如(但远远不限于)跟踪属性访问、隐藏属性、阻止修改或删除属性、函数参数验证、构造函数参数验证、数据绑定,以及可观察对象。
Reflect
每种代理都对应一种反射
反射:
Reflect.get( ...arguments )
Reflect.set( ...arguments )
Reflect.has( ...arguments )
Reflect.deleteProperty( ...arguments )
Reflect.ownKeys( ..arguments )
Reflect.denfineProperty( ...arguments )
Reflect.getOwnPropertyDescriptor( ...arguments )
Reflect.getPrototypeOf( ...arguments )
Reflect.preventExtensions( ...arguments )
Reflect.isExtensible( ...arguments )
Reflect.setPrototypeOf( ...arguments )
Reflect.apply( ...arguments )
Reflect.construct( ...arguments )
// Reflect let obj = { name: 'javascript', age: 20 } const proxy = new Proxy(obj,{ get(target,property,receiver){ return Reflect.get(...arguments) }, has(target,propKey){ return Reflect.has(...arguments) }, getOwnPropertyDescriptor(target,propKey){ return Reflect.getOwnPropertyDescriptor(...arguments) } }) console.log(proxy.name) // javascript console.log(age in proxy) // true console.log(Object.getOwnPropertyDescriptor(obj,name)) // {value: "javascript", writable: true, enumerable: true, configurable: true} 复制代码
函数
call、apply、bind
改变函数调用时
this
的指向
// call window.name = 'windows'; let obj = { name: 'javascript', age: 26 } function test1(hobby){ if(hobby){ console.log(hobby); } console.log(this.name); } test1() // windows test1.call(obj) // javascript test1.call(obj,'football') // football javascript // apply function test2(val1,val2){ console.log(val1,val2); console.log(this,name); } test2() // windows test2.apply(obj) // javascript test2.apply(obj,['aa','bb']) // aa bb javascript //bind let exa = test1.bind(obj) exa() // javascript exa('ball') // ball javascript 复制代码
从上边的代码看出三个方法的区别:
call
和apply
都可以将函数的this
绑定到其他对象上,唯一区别在于call
方法传入的参数为单个参数,apply
可以以数组的形式传入,函数需要几个参数,apply
传入的数组元素就有几个
bind
方法也能改变this
指向,只不过在改变this
的同时又以当前的函数创建了一个新的函数,新的函数内部拥有和之前的函数相同的代码块,只是this
被改变
arguments
arguments
与this
一样都是函数内置的对象,arguments
是一个类数组对象,调用函数传参时,参数被存储在arguments
中
// arguments function test(){ console.log(arguments) } test() // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ] test(1,1,1,1) // Arguments(4) [1, 1, 1, 1, callee: ƒ, Symbol(Symbol.iterator): ƒ] 复制代码
上边的代码可以看出,当一个函数没有声明形参时,调用函数传参时,函数内部也是可以访问到实参
function test(){ console.log(arguments[0],arguments[1]) } test(1,2) // 1,2 复制代码
默认参数
ES5
实现默认参数的方式是通过检测某个参数是否为undefined
,是则赋值,或者通过短路算法
来实现:
// ES5实现 function test1(a){ a = (typeof a !== 'undefined') ? a : 1; console.log(a); } test1(); // 1 test1(2); // 2 function test2(a){ a = a || 1; console.log(a); } test2(); // 1 test2(2); // 2 复制代码
ES6
之后可以显式的定义默认参数,只需在参数处为参数赋值就可以了:
// ES6实现 function test1(a = 2){ console.log(a) } test1(); // 2 test1(3); // 3 复制代码
箭头函数
箭头函数
为ES6
新增的定义函数表达书的方式:() => {}
,
// ES5函数定义 let fn = function(a,b){ return a+b; } // 箭头函数 let arrowFn = (a,b) => { return a+b; } fn(1,2); // 3 arrowFn(1,2); // 3 复制代码
箭头函数
这种简洁的写法非常适合嵌入函数
的场景,例:
// 函数嵌入 let arr = [1,2,3,4,5,6]; let res = arr.map((val) => { return val + 1; }) console.log(res); // [2,3,4,5,6,7] 复制代码
当参数只有一个时,可以省略参数处的括号:
a => { return a+1 }
当只有一条返回语句时,可以省略
return
和{}
:(a) => a+1
闭包
闭包
指的是一个函数内部引用了另一个函数作用域中的变量,一般都在嵌套函数内。
// 闭包 复制代码
Promise、async 和 await
Promise
Promise
有三种状态:pending(待定)
、fulfilled(resolved,解决)
、rejected(拒绝)
,状态不可逆。自Promise
创建之后即为pending(待定)
状态 ????
// new Promise let promise = new Promise((resolve,reject) => {}) console.log(promise) // Promise {<pending>} // fulfilled let promise2 = new Promise((resolve,reject) => { resolve('success!') }) console.log(promise2) // Promise {<fulfilled>: "success!"} // rejected let promise3 = new Promise((resolve,reject) => { reject('sorry,you were rejected.') }) console.log(promise3) // Promise {<rejected>: "sorry,you were rejected."} 复制代码
如上为
promise
的三种状态,上边三个变量返回的都是promise 实例
,而要想得到实例中的值或捕获到实例中的错误 (或rejected后的理由或返回值),需要使用
Promise.prototype.then()
获取fulfilled
状态的值,Promise.prototype.catch()
捕获rejected
状态的错误。
then()
其实可以接受两个参数,分别为onResolved
和onRejected
两个函数,函数的参数为实例解决或拒绝后的值。onRejected
一般用catch()
来捕获错误,因此then()
方法一般只传一个参数函数用来获取resolved
的值并进行其他处理。
// 引用上边的promise实例 // then() promise2.then((res) => { console.log(res) }) // success! promise3.catch((err) => { console.log(err) }) // sorry,you were rejected. 复制代码
因为
then()
方法返回一个promise
实例,所以当一个实例中不确定是否解决,即通过判断来决定resolved
或rejected
时,获取实例的返回值或捕获错误,可通过链式调用 ????
// 链式调用 let flag = true; let newPromise = new Promise((resolve,reject) => { if(flag){ resolve('It`s true.') }else{ reject('It`s false.') } }) newPromise.then((res) => { console.log(res); }).catch((err) => { console.log(err); }) // flag = true ----> It`s true. // flag = false ---> It`s false. 复制代码
promise
还有一个实例方法finally()
,但该方法与状态无关,主要用于清理代码,所以一般不使用。
Promise.all()
参数为可迭代的对象( 传入数组 ),可同时处理多个 promise ,依据内部的
行为全部结束之后
,返回相应的对象。
// all() // 引用上述已定义的变量 let promise4 = new Promise((resolve,reject) => { resolve('promise4 was successed.') }) let promise5 = new Promise((resolve,reject) => { reject('promise5 was rejected.') }) let finalRes1 = Promise.all([promise2,promise4]); finalRes1.then(res => console.log(res)); // ["success!", "promise4 was successed."] let finalRes2 = Promise.all([promise2,promise3]); finalRes2.then(res => console.log(res)).catch(err => console.log(err)); // sorry,you were rejected. let finalRes3 = Promise.all([promise3,promise5]); finalRes3.then(res => console.log(res)).catch(err => console.log(err)); // sorry,you were rejected. let finalRes4 = Promise.all([promise5,promise3]); finalRes4.then(res => console.log(res)).catch(err => console.log(err)); // promise5 was rejected. 复制代码
从上边的实例可以得出:
使用
Promise.all()
,当数组中的promise
都为resolved
时,返回即为resolved
,返回值为数组,数组元素为各个promise
实例resolved
后的值。当数组中有一个
promise
实例为rejected
,那么返回即为rejected
,返回的实例通过catch()
方法可捕获rejected
的错误或理由。当数组中的实例都为
rejected
,返回为rejected
,捕获的错误为第一次rejected
的错误,之后的rejected
不会影响。
Promise.race()
同样接收的参数为数组,
race
,比赛、赛跑,顾名思义,数组中第一个落定状态的,整体就会包装第一个状态值并返回新实例。
// race() // 同样引用上边已定义的变量 let raceRes1 = Promise.race([promise2,promise3]) raceRes1.then(res => console.log(res)).catch(err => console.log(err)) // success! let raceRes2 = Promise.race([promise2,promise5]) raceRes2.then(res => console.log(res)).catch(err => console.log(err)) // success! let raceRes3 = Promise.race([promise3,promise2]) raceRes3.then(res => console.log(res)).catch(err => console.log(err)) // sorry,you were rejected. let raceRes4 = Promise.race([promise5,promise4]) raceRes4.then(res => console.log(res)).catch(err => console.log(err)) // promise5 was rejected. 复制代码
如上所示,
race()
方法的结果由数组中第一个落定状态决定,通过then()
或catch()
来获取或捕获状态值。
async、await
异步函数,
async
放在function
之前,return
返回的值为promise实例
。
// async await async function testAsync(){ return 1 } testAsync() // Promise {<fulfilled>: 1} 通过then()或catch()来获取或捕获状态值 复制代码
注意一点:当异步函数执行到
await
时,会暂停执行,直到await
后边的异步操作通过后,向异步队列中添加一个任务,然后退出async
函数,直到所有同步线程的任务执行结束后,再从异步对列中取出任务,再恢复异步执行。
// await async function testAwait(){ let flag = false ; await setTimeout(() => { falg = true; console.log(3); },3000) console.log(2) } console.log(1) testAwait() // 1 // 2 // (3s后...) 3 复制代码
在实际开发中,
async、await
一般在调用接口或写接口时访问后台数据使用,写接口时配合promise
使用。
Bom
BOM:Browser Object Model
,浏览器对象模型,即window
对象,代表浏览器窗口和页面可视的区域,也被ECMAScript
用来作为Global
对象,即全局对象。window
对象中几个重要的对象:location
、navigator
、history
、screen
等
window
该对象在浏览器中有两重身份,一是
ECMAScript
中的Global
对象,二是浏览器窗口的javascript
的接口。
window作为JS Global对象
首先作为全局对象,在js文件中,所有通过
var
声明的变量或函数都会成为window
的一员,在变量声明这部分已了解到。js中的
top
对象为最上层的窗口,即window
对象。window
对象有一self
对象,该对象始终指向window
即二者相同。parent
对象始终指向当前窗口的父窗口,如果当前窗口是最上层窗口,则parent
和top
相等。
属性:
innerWidth
innerHeight
outerWidth
outerHeight
screenLeft
screenTop
screenX
screenY
方法:
alert() 弹出一个警告框
confirm() 弹出一个确认框,可根据该方法的返回值进一步进行操作
prompt() 弹出一个提示框,并且可以输入内容
print() 弹出浏览器的打印页面
open() 打开一个新窗口,两个参数,("新地址",“打开方式(_blank 、 _self and so on)”)
close() 关闭当前窗口
scrollTo() 当页面可滚动时,滚动条滑动到参数指定位置,参数还可接收一个对象,除了偏移值还有
behavior
属性,告诉浏览器是否平滑滚动。scrollBy() 页面可滚动时,滚动到参数指定位置,参数为像素值,同样参数可接收一个对象。
//scrollTo() scrollBy window.scrollTo({ left:100, top:100, behavior:'auto' }); // 正常滚动 window.scrollTo({ left:100, top:100, behavior:'smooth' }); // 平滑滚动 复制代码
window.onload = () =>() 在页面完全加载完后立即执行
window.onscroll = () => () 监听滚动条,鼠标滑动滚轮触发
window.onresize = () => () 监听窗口,窗口大小发生改变触发
window.onfocus = () => () 监听页面焦点,页面获得焦点时即切换到本页面时触发
window.onblur = () => () 监听页面焦点,页面失去焦点时即切换到其他页面时触发
只列举了
window
作为JS Global
对象的部分常用的方法,更多的方法可自行打印window
.
location
location
也是window
对象的一员,使用时可省略window
直接使用。location
对象内包含当前页面URL的所有信息。
// location window.onload = () => { console.log(location); } // Location {ancestorOrigins: DOMStringList, href: 'https://cn.vuejs.org/', origin: 'https://cn.vuejs.org', protocol: 'https:', host: 'cn.vuejs.org', …} 复制代码
属性
location.hash 返回URL后边的哈希值,即
#xxx
,没有则为控制符串location.host 返回服务器名及端口号
location.hostname 返回服务器名
location.port 返回端口号,没有则为空字符串
location.pathname 返回当前页面的路径名
location.href 返回当前页面完整的URL,
location.toString()
方法也可返回location.protocol 返回当前页面使用的协议,
http:
或者https:
location.search 返回当前URL所查询的字符串,在
?
之后。如:https://www.baidu.com/s?wd=javascript
location.username 返回当前域名的用户名,一般在登录页面有
location.password 返回当前域名的密码,一般在登录页面有
location.origin 返回URL的源地址
查询字符串
已经知道,通过
location.search
可以获得当前URL所查询的字符串,返回的字符串为从?
开始一直到末尾,有时参数可能过多不方便查询或访问每个参数,因此,window
对象提供了一个助于我们访问每个参数的函数:URLSearchParams()
. 该函数是一个构造函数
,可通过该函数创建实例进行检查和修改查询到的字符串。该方法拥有get()
、set()
、delete()
等方法。
// URLSearchParams() // 假设该页面已经进行了搜索,关键字为'js' let queryStr = location.search; // 获取搜索的内容字符串 let UrlSearch = new URLSearchParams(queryStr); console.log(queryStr) // ?'ie=UTF-8&wd=js' console.log(UrlSearch.toString()); // 'ie=UTF-8&wd=js' // 两个唯一的不同 -> ‘?’ // 使用`URLSearchParams()`是为了更方便的查询和修改搜索字符串货关键字,如果必须使用 `location.search`,就只能通过字符串的各种方法来处理 console.log(UrlSearch.has('wd')); // true console.log(UrlSearch.get('wd')); // js UrlSearch.set('num',26); console.log(UrlSearch.toString()); // 'ie=UTF-8&wd=js&num=26' console.log(UrlSearch.get('num')); // 26 UrlSearch.delete('num'); console.log(UrlSearch.has('num')); // false // URLSearchParams() 还有类似于 Object 的可迭代的方法:keys()、values()、entries(),这些方法返回的 是一个 Iterator对象,不能直接打印,可通过 for...of... 语句访问其中的值 // entries() for(let params of UrlSearch.entries()){ console.log(params); } // ['ie', 'UTF-8'] // ['wd', 'js'] // values() for(let params of UrlSearch.values()){ console.log(params); } // UTF-8 // js // keys() for(let params of UrlSearch.keys()){ console.log(params); } // ie // wd 复制代码
通过
URLSearchParams()
创建的实例本身也是一个可迭代的对象,因此也可通过for..of...
进行操作。
navigator
该对象同样是
window
对象的一员,可省略window
直接使用。该对象记录了浏览器的各种信息,不太常用,可在控制台查看其属性和方法
history
也是
window
的属性,保存着当前窗口使用以来的所有导航历史记录,但不会暴露访问过的URL
,但是可以在不知道URL
的情况下前进或者后退。
history
对象常用的方法有go()
、back()
、forward()
等,不过在实际开发中基本用不到。实际开发中,用
Vue
框架时会用VueRouter
来管理或操作路由( 即URL ),用react
框架时会使用React Router
来管理路由。
window
对象较常用的对象已阐述完毕,更多的属性或方法可自行查看。
Dom
Dom,
Document Object Model
,文档对象模型。即包含整个页面所有节点的对象,开发者可根据此对象对页面各个部分进行获取、添加、删除和修改等任何操作。
Dom节点类型
元素节点(1) 可通过
dom.nodeType
查询到,返回值为()
内的整数属性节点(2) 同上
文本节点(3) 同上
注释节点(8) 同上
文档节点(9) 同上
文档片段节点(11) 同上
Dom节点属性
nodeName 元素节点名称,通过
Dom.nodeName
获得其值nodeValue 节点内容,
Dom.nodeValue
nodeType 节点类型,
Dom.nodeType
,返回值为上述某个节点类型的()
内的值attributes 元素节点的所有属性值,如:某元素
id
为div1
,返回一个NamedNodeMap
类型的类数组,该类数组中包含id
属性以及其他属性值,每个元素都为一个对象,对象中含有该元素的所有信息。
元素节点常用的方法
element.setAttribute('class' , ' ') 设置元素节点的
class
属性element.getAttribute("class") 获取元素节点的
class
属性element.removeAttribute("class") 删除元素节点的
class
属性
基于元素节点树的操作
parentElement 父元素节点
children 子元素节点集合
firstElementChild 第一个子节点元素
lastElementChild 最后一个子节点元素
nextElementSibling 下一个兄弟元素
previousElementChild 上一个兄弟元素
在实际开发中,这些方法基本不会用到,了解即可。
增、删、插、替换Dom节点
Dom节点也可进行增加、删除、插入以及替换等操作
创建节点
document.createElement() 创建元素节点
document.createTextNode() 创建文本节点
document.createComment() 创建注释节点
document.createDocumentFragment() 创建文档片段节点
增加节点
parentNode.appendChild(A) 在父节点的最后添加
A
节点parentNode.insertBefore(A,B) 在
A
节点插入到B
节点之前删除节点
parentNode.removeChild(A) 删除
A
节点child.remove()
节点替换
parentNode.replaceChild(A,B) 用节点
A
替换节点B
节点的操作实际开发中不是很常用,用到时候再复习即可。
element.hasChildNodes()
该方法判断一个元素节点有没有子节点,有则返回
true
,否则返回false
。
事件
javascript
和html
的交互是通过事件
实现的,事件代表文档或浏览器窗口中某个有意义的时刻。
事件流
事件冒泡
冒泡
,好比水底的的泡泡,会浮向水面。而事件冒泡
与其原理类似,一个元素定义事件
后,触发后从该元素开始触发,向上传播,直到最顶层元素。例:// 事件冒泡 <!DOCTYPE html> <html> <head> <title>Event Bubbling Example</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html> 复制代码
点击页面的
<div>
元素后,click
事件会以如下顺序发生:(1)
(2)
(3)
(4)document
即被点击的元素先触发
click
事件,随后click
事件沿dom树
一路向上。事件捕获
捕获
,该类型事件流事件顺序与事件冒泡
相反。
事件处理
事件绑定后,处理函数中
this
的指向:1)普通函数this
指向为当前DOM
2)箭头函数this
指向为window
DOM 0级事件处理
举个例子:
// DOM 0级 let btn = document.getELementById("btn"); btn.onclick = function(){ console.log(this.id); } // btn 复制代码
像
dom元素.onXXX = function
这样的事件处理为DOM 0级事件处理
。该方式只能监听冒泡
阶段,且同名的事件会被覆盖,如:// 同名事件 btn.onclick = function(){ console.log(111); } btn.onclick = function(){ console.log(222); } // 点击按钮后打印:222 复制代码
事件移除,如:
btn.onclick = null;
,即可移除该元素绑定的事件。DOM 2级事件处理
该方式事件处理和移除有两个方法:
addEventListener()
和removeEventListener()
。// DOM 2级 let btn = document.getElementById("btn"); btn.addEventListener("click",function(){ console.log(this.id); },false) // btn 复制代码
addEventLIstener()
接收三个参数,事件名、事件处理函数、布尔值,true
表示捕获阶段
调用事件处理程序,false
表示冒泡阶段
,默认为false
。若想移除事件,
removeEventListener()
的参数必须传入与addEventListener()
同样的参数,不能传入匿名函数。// removeEventListener() // 匿名函数 btn.removeEventListener('click',function(){ console.log(this.id); },false) // 无效果,控制台依旧打印 btn // 有名函数 function clickBtn(){ console.log(this.id); } btn.addEventListener("click",clickBtn,false); btn.removeEventListener("click",clickBtn,false); // 点击后不再打印任何值 复制代码
同名事件不会覆盖:????
let btn = document.getElementById("mybtn"); btn.addEventListener('click',function(){ console.log(111); },false) btn.addEventListener('click',function(){ console.log(222); },false) // 111 // 222 复制代码
同名事件会按照事件顺序执行。
事件对象
事件处理函数的第一个参数为事件对象
event
,不管哪种处理方式,DOM 0
或DOM 2
都会传入这个event
对象。
// event btn.removeEventListener('click',(event) => { console.log(event.type); },false) // click 复制代码
event
常用的属性与方法(其余属性和方法可在控制台查看)event.stopPropagation() 阻止事件传播,即阻止冒泡
event.preventDefault() 阻止默认事件
event.target 事件源,即触发事件的元素
event.currentTarget 当前绑定的元素
event.button 触发事件的树边按键,左键、中键、右键
event.clientX/clientY 鼠标距离可视窗口的水平距离/竖直距离,随滚动条改变
event.pageX/pageY 鼠标距离页面左上角的水平距离/竖直距离,不随滚动条改变
event.offsetX/offsetY 鼠标距离触发事件元素左上角的距离
event.layerX/layerY 鼠标距离最近定位父级左上角的距离
event.screenX/screenY 鼠标距离计算机屏幕左上角的距离
属性
方法
事件分类
click 点击事件,一次
click
=mousedown
+mouseup
contextmenu 鼠标右键菜单,不常用
dblclick 双击事件
mousedown 鼠标任意键按下触发
mouseup 鼠标任意键抬起触发
mouseenter 鼠标进入元素触发,该事件不会
冒泡
mouseleave 鼠标离开元素触发,该事件不会
冒泡
mouseover 鼠标进入元素触发,
冒泡
mouseout 鼠标离开元素触发,
冒泡
mousemove 移动事件,鼠标移动触发
mousewheel 滚轮事件,滚轮滚动触发(Chrome)
DOMMouseScroll 滚轮事件,滚轮滚动触发(Firefox)
keydown 按住键盘任意键触发,持续触发,即按住不放就一直触发
keyup 键盘任意键抬起触发
keypress 按住键盘部分键触发,持续触发
load 页面或者图片加载完成时触发
scroll 页面滚动触发
focus 得到焦点触发
blur 失去焦点触发
客户端存储
sessionStorage
本地存储会话数据,页面存在期间有效,即浏览器关闭就消失。本地存储数据的大小一般为
5M
。
sessionStorage.setItem(key,value) 存储会话数据
sessionStorage.getItem(key) 获取数据
sessionStorage.removeItem(key) 删除会话数据
被保存的值必须是字符串
localStorage
也是将数据存储在客户端,必须手动删除或清除浏览器缓存,否则
localStorage
中的数据一直存在,不会因浏览器关闭而消失。本地存储数据的大小一般为5M
。
localStorage.setItem(key,value) 存储会话数据
localStorage.setItem(key,value) 存储会话数据
localStorage.setItem(key,value) 存储会话数据
cookie
从
javascript
的角度来看,cookie
就是一些字符串信息,这些信息存放在客户端用于客户端与服务器端传递信息。每个
cookie
所存放的数据不能超过4k
,超过则将返回空字符串。
cookie
最终都是以文件形式存放在客户端计算机中,所以可以很方便的查看cookie
。
设置
cookie
document.cookie = 'key=value'
获取
cookie
console.log(document.cookie)
删除
cookie
js
中cookie
没有显式的方法删除cookie
,可以通过设置其过期时间为一个过去的时间,如:// 删除cookie // 获取当前时间 let date = new Date(); // 设置为过去的某个时间 date.setTime(date.getTime() - 1); // 将 userID 这个 cookie 删除 document.cookie = "userId=123; expire="+date.toGMTString(); 复制代码
设置
cookie
有效时间cookie
的默认有效时间为关闭浏览器之前,即关闭浏览器的时候会自动清除,但是我们可以通过expire
来设置有效期。如:// 设置 cookie 有效期 function setCookie(key, value, expiredays){ let exdate = new Date(); exdate.setDate(exdate.getDate() + expiredays); document.cookie = `${key}=${value}` + ((expiredays==null) ? "" : ";expires="+exdate.toGMTString()); } setCookie('userID','886',30); // 有效期设置为30天后 复制代码
大多数情况下还是用本地存储居多,即
sessionStorage
,当需要保存当前应用的一些组件状态等信息时,sessionStorage
是个不错的选择。
稍有瑕疵,望兄弟们提出宝贵意见,会及时更改。会继续更新......
作者:愤怒的小孩
链接:https://juejin.cn/post/7015192592576938020