阅读 92

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

    letvar差不多,最主要的区别在于: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对象的属性和方法,而letconst不会。

TDZ(暂时性死区)

上边说到,var声明的变量在作用域中会有提升,即在声明之前可以引用,而用let声明的变量没有提升,在声明之前引用会报错,所以在let声明之前

的执行瞬间被称为暂时性死区(Temporal Dead Zone)

数据类型

Javascript6种基本类型和3种引用类型

基本类型

  • Number

  • String

  • Boolean

  • Undefined

  • Null

  • Symbol(ES6新增)

引用类型

  • Array

  • Function

  • Object

流程语句

  • if语句

  • do-while语句

  • while语句

  • for语句

  • for-in语句

  • for-of语句

    可迭代对象可使用,即iterator

  • switch语句

  • continuebreak

变量、作用域和内存

数据分为原始值(基本类型)引用值(引用类型),原始值存储在中,引用值引用变量名 存储在中,实际对象存储在中。

因此,当两类值进行赋值和复制时有所不同

// 原始值(基本类型) 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不能直接操作内存,所以在操作对象时实际上操作的是该对象的引用而非其本身,obj1obj2实际上为真实对象的地址,二者都指向

存储在堆内存的真实对象。 ????

引用值

当通过方法改变obj1obj2任意一个的值时,改变的是堆内存中的原始对象,所以另一个也会改变。

类型判断

  • 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 复制代码

当代码嵌套比较深得时候,最内层的嵌套访问变量时会按照作用域链依次向上寻找,直到找到为止。如图:

作用域链.png

垃圾回收

  • 标记清理

  • 引用计数(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 的作用有三个:

  1. 为各种数据结构提供一个统一的、简便的访问接口;

  2. 使得数据结构的成员能够按某种次序排列;

  3. 主要供for...of消费。

Iterator本质上,就是一个指针对象。

  • 过程

    1. 创建一个指针对象,指向当前数据结构的起始位置。

    2. 第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

    3. 第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

    4. 不断调用指针对象的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.nameproxy[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 )

返回一个可取消代理的对象,对象有proxyrevoke()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 复制代码

从上边的代码看出三个方法的区别:

  1. callapply都可以将函数的this绑定到其他对象上,唯一区别在于call方法传入的参数为单个参数,apply可以以数组的形式传入,函数需要几个参数,apply传入的数组元素就有几个

  2. bind方法也能改变this指向,只不过在改变this的同时又以当前的函数创建了一个新的函数,新的函数内部拥有和之前的函数相同的代码块,只是this被改变

arguments

argumentsthis一样都是函数内置的对象,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()其实可以接受两个参数,分别为onResolvedonRejected两个函数,函数的参数为实例解决或拒绝后的值。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实例,所以当一个实例中不确定是否解决,即通过判断来决定resolvedrejected时,获取实例的返回值或捕获错误,可通过链式调用 ????

// 链式调用 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. 复制代码

从上边的实例可以得出:

  1. 使用Promise.all(),当数组中的promise都为resolved时,返回即为resolved,返回值为数组,数组元素为各个promise实例resolved后的值。

  2. 当数组中有一个promise实例为rejected,那么返回即为rejected,返回的实例通过catch()方法可捕获rejected的错误或理由。

  3. 当数组中的实例都为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对象中几个重要的对象:locationnavigatorhistoryscreen

window

该对象在浏览器中有两重身份,一是ECMAScript中的Global对象,二是浏览器窗口的javascript的接口。

window作为JS Global对象

首先作为全局对象,在js文件中,所有通过var声明的变量或函数都会成为window的一员,在变量声明这部分已了解到。

js中的top对象为最上层的窗口,即window对象。window对象有一self对象,该对象始终指向window即二者相同。parent对象始终指向当前窗口的父窗口,如果当前窗口是最上层窗口,则parenttop相等。

属性:

  • 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

DomDocument Object Model,文档对象模型。即包含整个页面所有节点的对象,开发者可根据此对象对页面各个部分进行获取、添加、删除和修改等任何操作。

Dom节点类型

  • 元素节点(1) 可通过dom.nodeType查询到,返回值为()内的整数

  • 属性节点(2)    同上

  • 文本节点(3)    同上

  • 注释节点(8)    同上

  • 文档节点(9)    同上

  • 文档片段节点(11)    同上

Dom节点属性

  • nodeName   元素节点名称,通过Dom.nodeName获得其值

  • nodeValue    节点内容,Dom.nodeValue

  • nodeType      节点类型,Dom.nodeType,返回值为上述某个节点类型的()内的值

  • attributes      元素节点的所有属性值,如:某元素iddiv1,返回一个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

事件

javascripthtml的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意义的时刻。

事件流

  • 事件冒泡

    • 冒泡,好比水底的的泡泡,会浮向水面。而事件冒泡与其原理类似,一个元素定义事件后,触发后从该元素开始触发,向上传播,直到最顶层元素。例:

      // 事件冒泡 <!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 0DOM 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

    jscookie没有显式的方法删除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


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐