函数中的this this就是一个指针变量,动态指向当前函数的运行环境
全局环境下的this 全局作用域下,this永远指向window 。
普通函数中的this
谁调用this就指向谁,没有调用,this就指向window
严格模式下,必须写上函数的调用者,不能省略或简写。全局中this的调用者是window。因此要写window.fn()。
对象中的this 对象内部方法的this指向调用这些方法的对象,也就是谁调用就指向谁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var obj = { name :"张三" , age :18 , sayName :function ( ){ console .log ("my name is " +this .name ); }, obj :{ name :"李四" , age :20 , sayName :function ( ){ console .log ("my name is " +this .name ); } } } obj.sayName (); obj.obj .sayName ();
箭头函数中的this 箭头函数没有自己的this,它的this在函数被定义时就已经被绑定 ,指向函数所在作用域的外部作用域。
构造函数中的this 构造函数中的this永远指向被创建的实例对象。如果构造函数中设置了具有对象的返回值 ,那么该对象就是new表达式的结果;如果返回值不是对象,则new表达式的结果为构造函数的实例对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function Fun (name,age ){ this .name = name; this .age = age; this .sayName = function ( ){ console .log (this ) } } let fun = new Fun ("张三" ,18 );fun.sayName (); function Fun1 (a ){ this .a = a; return {a :18 ,name :"李四" }; } let fun1 = new Fun1 (17 );console .log (fun1);console .log (fun1.a );
特殊
定时器中的this
定时器中的回调为普通函数时,其中的this永远指向window,即全局环境。
若为箭头函数时,它的this指向上一层的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var obj={ name :"张三" , fun3 :function ( ){ setTimeout (function ( ){ console .log (this ) },100 ) }, fun4 :function ( ){ setTimeout (()=> { console .log (this ) },100 ) } } obj.fun3 (); obj.fun4 ()
原型和原型链 原型 当一个函数被创建时,JS会给他内部自动添加一个属性:prototype,prototype为一个对象,其中有一个constructor指向创建的函数。
可以看到fn的prototype属性中有constructor属性,可以将其看作指针,它指向的是fn这个函数。
构造函数和实例对象 一般来说,函数名首字母大写的函数称为构造函数。使用new关键字 可以通过构造函数定义一个它的实例对象。
1 2 3 4 5 6 7 8 9 10 11 function Fn ( ){ this .name = "张三" ; this .age = 18 ; this .info = function ( ){ console .log (this .name ,this .age ); } } let fn = new Fn ()console .log (fn.name );console .log (fn.age );fn.info ()
此时fn就是Fn的实例对象 。
fn会继承在Fn的所有属性,在new的过程中,Fn中的this会自动指向fn,所以在new的过程中,JS做了以下事情。
1 2 fn.__proto__ = Fn .prototype ; Fn .call (fn);
______proto______和prototype 在每个构造函数的实例对象中都有**____proto 属性,此时也可以把它当作一个 指针**,它所指向的就是构造函数的prototype对象。
1 2 3 4 5 function Fn ( ){} let fn = new Fn ()console .log (fn.__proto__ === Fn .prototype )
此时fn就可以通过**____proto 获取Fn的 prototype**原型上的所有属性。
原型链 刚刚说到,通过**____proto 获取Fn的 prototype原型上的所有属性,prototype也是一个实例对象,它是 Object**的实例对象,因此在prototype中也有______proto__属性,它指向的是Object函数的原型对象,即Object的prototype原型对象。
既然prototype是一个实例对象,那么只要是实例对象,都会有一个**____proto 指向它的构造函数的原型对象。而Object的prototype的______proto______所指向的为 null**。根据定义,null没有原型,因此原型链的尽头为Object.prototype.______proto__。
1 2 3 4 5 6 7 8 9 10 11 function Fn ( ){ this .name = "张三" ; this .age = 18 ; this .info = function ( ){ console .log (this .name ,this .age ); } } let fn = new Fn ();console .log (fn.__proto__ === Fn .prototype )console .log (Fn .prototype .__proto__ === Object .prototype )console .log (Object .prototype .__proto__ )
查找一个属性,实例对象会先在自身寻找,如果没有,会去它的______proto______也就是构造函数的prototype原型上找,如果也没有,则去构造函数的prototype的______proto__,也就是Object.prototype中去找,如果也没有,则返回undefined。
综上整个过程,即为原型链。
Function、Object、function 其实在构造函数中,也有一个______proto__,这是为什么呢?
因为构造函数是函数,而所有函数都是Function的实例(包括Function本身),所以就会有一个很有意思的现象。Function的______proto__指向Function的prototype原型对象。
而Object也是一个构造函数,它的______proto__指向Function的prototype。
1 2 3 console .log (Object .__proto__ === Function .prototype )console .log (Function .__proto__ === Function .prototype )console .log (Function .prototype .__proto__ === Object .prototype )
继承 原型链继承 将父类的实例 赋给子类的原型 ,这样子类就可以获得父类的属性和方法,以及原型上的属性和方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function Person ( ){ this .name = 'person' ; this .friends = ['a' ,'b' ,'c' ]; this .get = ()=> { console .log ("我是Person" ); } } Person .prototype .say = function ( ){ console .log ("我是Person的原型" ); } function Student ( ){}Student .prototype = new Person ();let stu1 = new Student ();console .log (stu1.name );stu1.get (); stu1.say (); console .log (stu1.friends );console .log (stu1);let stu2 = new Student ();stu2.friends .push ("d" ); console .log (stu2.friends );
优点:
可以继承父类及其原型的属性和方法
缺点:
所有子类共享 ,一旦一个子类修改了父类的属性或方法,其他子类也会改变
构造函数继承 在子类中通过call或apply改变父类的this指向,调用父类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function Person ( ){ this .name = 'person' ; this .friends = ['a' ,'b' ,'c' ]; this .get = ()=> { console .log ("我是Person" ); } } Person .prototype .say = function ( ){ console .log ("我是Person的原型" ); } function Student ( ){ Person .call (this ) } let stu = new Student ()console .log (stu.name );console .log (stu.friends );stu.get (); console .log (stu);stu.say ();
优点:
解决了原型链继承的缺点,每个子类继承的都是独一无二的父类,互不影响
缺点:
无法继承父类原型链上的属性和方法 ,相当于在子类里调用了父类函数 ,给子类添加了父类的属性和方法
无法复用
组合继承 使用原型链继承和构造函数继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function Person ( ){ this .name = 'person' ; this .friends = ['a' ,'b' ,'c' ]; this .get = ()=> { console .log ("我是Person" ); } } Person .prototype .say = function ( ){ console .log ("我是Person的原型" ); } function Student ( ){ Person .call (this ) } Student .prototype = new Person ();let stu = new Student ();console .log (stu.name );console .log (stu.friends );stu.get (); stu.say (); console .log (stu);
优点
解决了构造函数继承无法继承父类原型的问题,且子类继承的属性都是独立的,互不影响
缺点
会执行两次父类的构造函数 ,消耗较大内存
寄生组合式继承 利用一个新的函数Fn,将父类的实例赋给函数Fn的原型,然后子类中用call调用父类,并将Fn的实例赋给子类的原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function Person ( ){ this .name = 'person' ; this .friends = ['a' ,'b' ,'c' ]; this .get = ()=> { console .log ("我是Person" ); } } Person .prototype .say = function ( ){ console .log ("我是Person的原型" ); } function Fn ( ){};Fn .prototype = new Person ();function Student ( ){ Person .call (this ); } Student .prototype = new Fn ();let stu = new Student ();console .log (stu.name );console .log (stu.friends );stu.get (); stu.say (); console .log (stu);
闭包 本质就是内部函数引用的外部函数的变量或方法。
优点
可以实现变量私有化,可以让变量不受污染
缺点
可能造成内存泄漏
柯里化函数 每传递一个参数调用函数,就返回一个新函数处理剩余的参数
柯里化的概念:接收一个函数 fn,返回另一个函数 curried,当curried中的参数少于 fn时,它会返回另一个函数暂存之前的参数 ,直到参数个数等于fn接收的参数个数时返回结果 。
1 2 3 4 5 6 7 8 9 10 function curry (fn ){ return function curried (...args ){ if (args.length < fn.length ){ return function ( ){ return curried (...args.concat (Array .from (arguments ))); } } return fn (...args); } }
Promise then then函数是由返回值的,返回的是一个新的Promise对象实例化对象 。
如果p1实例化对象是成功的状态 ,则执行then的成功回调函数 ;如果是失败 的状态,则执行then的失败的回调函数 。
如果then中没有返回值
then函数返回的Promise是成功 的状态,并且结果值是undefined 。
1 2 3 4 5 6 7 8 9 10 11 let p1 = new Promise ((resolve,reject )=> { throw "err" }); const p2 = p1.then ( value => { console .log (value); },reason => { console .log (reason); }) console .log (p2);
如果then中有返回值
返回的为非Promise实例化对象 ,则状态仍是成功的Promise实例化对象 ,返回值就是结果值
返回的为Promise实例化对象 ,则p2的状态取决于Promise实例化对象的状态
如果直接抛出异常 ,则返回失败的Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 let p1 = new Promise ((resolve,reject )=> { resolve (1 ) }); const p2 = p1.then ( value => { console .log (value); return "okok" ; return new Promise ((_,reject )=> { reject (1 ) }) throw "err" }) console .log (p2);console .log (p1 === p2)
如果then中省略或者不是一个回调函数
默认成功的回调函数为value => value
,默认失败的回调函数为reason => {throw reason}
catch catch用于捕捉Promise里reject后的值,和then一样也返回一个新的Promise。只有一个回调函数,表示失败的回调。
如果catch中没有返回值
catch函数返回的Promise是成功 的状态,并且结果值是undefined 。
如果catch中有返回值
返回的为非Promise实例化对象 ,则状态仍是成功的Promise实例化对象 ,返回值就是结果值
返回的为Promise实例化对象 ,则p2的状态取决于Promise实例化对象的状态
如果直接抛出异常 ,则返回失败的Promise
Promise.resolve() Promise.resolve()
返回一个Promise。可以进行传参。
参数为非Promise或为空 : Promise状态为fulfilled,结果为传进的参数或undefined
参数为Promise的实例对象 ,则返回的Promise的状态取决于Promise实力对象的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const p = Promise .resolve ();const p1 = Promise .resolve (1 )const p2 = Promise .resolve (new Promise ((resolve,reject )=> { resolve (2 ) })) const p3 = Promise .resolve (new Promise ((resolve,reject )=> { reject ("error" ) })) const p4 = Promise .resolve (new Promise ((resolve,reject )=> { throw "异常" })) console .log (p);console .log (p1);console .log (p2);console .log (p3);console .log (p4);
Promise.reject() 不管参数如何,都返回一个失败的Promise。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const p = Promise .reject ();const p1 = Promise .reject (1 )const p2 = Promise .reject (new Promise ((resolve,reject )=> { resolve (2 ) })) const p3 = Promise .reject (new Promise ((resolve,reject )=> { reject ("error" ) })) const p4 = Promise .reject (new Promise ((resolve,reject )=> { throw "异常" })) console .log (p);console .log (p1);console .log (p2);console .log (p3);console .log (p4);
Promise.all() Promise.all()
需要传递一个数组 参数,数组里存的是Promise的实例化对象 ,返回值 也是Promise的实例化对象 。
返回值的Promise状态
当数组中所有的Promise实例化对象的状态都是成功 的话,则返回的Promise的状态也是成功 的,结果值为所有成功的Promise实例化对象的结果组成的数组
当数组中所有的Promise实例化对象的状态有一个失败 的话,直接返回失败的Promise实例化对象 ,结果值 就是第一个失败Promise实例化对象的结果值 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const p1 = new MyPromise ((resolve,reject )=> { resolve (1 ) }) const p2 = new MyPromise ((resolve,reject )=> { reject (1 ) }) const p3 = new MyPromise ((resolve,reject )=> { resolve (3 ) }) const p4 = new MyPromise ((resolve,reject )=> { resolve (4 ) }) const p = MyPromise .all ([p1,p2,p3]);const p = MyPromise .all ([p1,p2,p4]);console .log (p);
Promise.race() 参数为一个由多个Promise实例对象组成的数组 。
返回一个新的Promise实例对象,状态取决于数组中最先改变状态 的Promise实例对象的状态为准 。
链式调用 当new Promise
链式调用时,每调用一个then都会产生一个新的Promise,它的状态只要不是抛出异常throw error
以及在then的回调里返回一个失败的Promise,它都会是一个成功的Promise。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let p1 = new Promise ((resolve, reject ) => { resolve (1 ); }).then (value => { console .log (value); }).then (value => { console .log (value); return 2 ; }).then (value => { console .log (value); throw "error" }).then (null ,reason => { console .log (reason); }).catch (reason => { console .log (reason); })
异常穿透 当Promise的链式调用时出现异常,那么它会一直往下,直到遇到能处理异常的回调(then中的onrejected回调或catch ),在此期间,then返回的Promise状态都是rejected,直到异常处理后才变成fulfilled。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let p1 = new Promise ((resolve,reject ) => { throw '异常' }) console .log (p1);const p2 = p1.then (value => { console .log (value); }) console .log (p2);const p3 = p2.then (value => { console .log (value); },reason => { console .log (reason); return 1 ; }) console .log (p3);const p4 = p3.then (value => { console .log (value); }) console .log (p4);
中断Promise链 只要在链式调用的过程中返回一个pending状态的Promise ,Promise链就会中断。因为pending状态的Promise并没有处理,then处理成功或者失败的Promise,catch处理失败的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let p1 = new Promise ((resolve,reject ) => { resolve (1 ) }) const p2 = p1.then (value => { console .log (value); return 2 ; }) const p3 = p2.then (value => { console .log (value); return new Promise (()=> {}) },reason => { console .log (reason); }) const p4 = p3.then (value => { console .log (value); })
async函数和await表达式 $\textcolor{red}{注意点:}$
async函数中不一定要有await表达式,但有await的函数一定要有async
await相当于then,获取Promise实例对象成功的结果
async 在函数前面加一个async就可以把该函数变为异步函数。
在函数内部正常书写,$\textcolor{red}{async函数的返回值为一个Promise}$ ,状态根据返回的数据决定。
返回的是一个非Promise类型 ,那么状态为成功 的Promise,结果为返回的具体值 。
返回的是一个Promise实例对象 ,返回的Promise即为async函数返回的Promise。
抛出异常 ,那么async也会返回一个失败的Promise ,结果值为异常的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 async function fn1 ( ){ return 0 ; } let f1 = fn1 ();async function fn2 ( ){ return new Promise ((resolve, reject ) => { resolve (1 ) }) } let f2 = fn2 ();async function fn3 ( ){ return new Promise ((resolve, reject ) => { reject (2 ) }) } let f3 = fn3 ();async function fn4 ( ){ throw "异常" } let f4 = fn4 ();console .log (f1);console .log (f2);console .log (f3);console .log (f4);
await(异步)
如果await右侧为非Promise类型的数据 ,awiat后面是什么,得到的结果就是什么
如果await右侧为成功的Promise ,则得到的就是成功的结果
如果await右侧为失败的Promise ,需要用try…catch 去捕获失败的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 async function fn5 ( ){ let re = await null ; console .log (re); } fn5 ();async function fn6 ( ){ let re = await new Promise ((resolve, reject ) => { resolve (1 ) }); console .log (re); } fn6 ();async function fn7 ( ){ try { let re = await new Promise ((resolve, reject ) => { reject (2 ) }); console .log (re); }catch (err){ console .log (err); } } fn7 ();
await执行顺序 同步和异步同时存在时,会优先执行同步代码,随后再执行异步。await是异步的,它会等待后面的结果,哪怕是0秒也会等待。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function main ( ){ console .log (1 ); let re1 = await new Promise ((resolve, reject ) => { console .log (2 ); resolve ('ok' ); }) console .log (3 ); let re = await setTimeout (()=> { console .log (1 ); }); console .log ("re" + re); console .log (5 ); } main ();console .log (6 );