二,函数高级

前面对JS基础进行了深入总结,可以通过https://blog.csdn.net/qq_33345511/article/details/106122978访问。今天我们来聊一聊函数。
一,原型与原型链
1)函数的prototype属性
每个函数都有prototype属性(该属性在函数创建时创建),它默认指向一个空的object空对象(即原型对象);原型对象中有一个constructor属性,它指向函数对象
2)给原型对象添加属性或方法,作用:函数所有的实例对象自动拥有原型中的属性或方法
3)显示原型prototype和隐式原型
    (1)每个函数都有一个显式原型prototype属性,默认指向一个空的Object对象(在prototype上添加属性或方法后不是空的),显示原型属性在函数创建时就产生了,
    (2)每个实例对象都有一个__proto__隐式原型,在创建实例对象时就创建了
    (3)对象的隐式原型的值为其对应构造函数的显示原型的值
程序员可以直接操作显示原型,不可以操作隐式原型,(ES6中也可操作隐式原型)


二,函数高级_第1张图片
            console.log(Fn.prototype);得到的一个空的Object对象,所谓空的Object对象是指我们开发人员没有在这个Object对象里面添加属性和方法,这个Object对象里面实际上是有属性的:
Object{
     constructor:function Fn(),//constructor属性指向函数对象
     :Object{
           __proto__:Object{
               null;
           }//原型对象的原型对象指向Object对象
      }// 指向空的Object原型对象
}
4)原型链
        原型链是用来查找实例上的属性值和方法的,实例对象调用方法或属性时是通过隐式原型来查找的,因此原型链又称为隐式原型链。
二,函数高级_第2张图片二,函数高级_第3张图片
当调用(访问)一个实例对象的属性或方法时,会先在自身中寻找,如果找到该属性或方法则使用;如果没有找到则沿着原型链(__proto__)属性去原型对象中寻找,如果找到则使用;如果没有找到则利用__proto__属性去原型对象Object的原型中寻找,如果找到则使用,如果没有找到则返回undefined。

in检查对象是否具有某个属性时,会沿着原型链查找;
hasOwnProperty()可以用来检查对象本身是否具有某个属性。hasOwnProperty属性是在实例对象原型对象的原型对象上

面试题目:
二,函数高级_第4张图片二,函数高级_第5张图片

总结:每个函数都有prototype属性,这是一个引用类型的变量,引用类型就对应对象,因此prototype属性指向一个对象,我们将这个对象称为原型对象Object。原型链的尽头是Object原型对象,原型对象Object原型对象的__proto__属性为null。原型链是用来帮助我们查找实例对象的属性和方法。
二,变量提升与函数提升

什么是变量提升?什么是函数提升?变量提升与函数提升是如何产生的?
变量提升是指用var关键字定义的变量在定义前就可以访问了,其值是undefined;
函数提升是指用function声明的函数,在函数声明前就可以直接调用,其值是函数对象;函数提升可以提高代码性能,函数声明提升解析器拿到代码只需预编译一次即可,在调用函数时无需多次解析函数。变量提升先于函数提升执行
在js中js引擎会优先解析var变量和function定义!在预解析完成后从上到下逐步进行!解析var变量时,不会赋值,添加为window对象!在解析function时会把函数整体定义,这也就解释了为什么在function定义函数时为什么可以先调用后声明了!其实表面上看是先调用了,其实在内部机制中第一步实行的是把以function方式定义的函数先声明了(预处理)。
二,函数高级_第6张图片
二,函数高级_第7张图片

三,执行上下文与执行上下文栈
执行的时候才产生
1,代码分为:全局代码,函数代码
2,全局执行上下文:
    1)在执行全局代码前将window作为全局执行上下文
    2)对全局数据进行处理
        var定义的全局变量赋值为undefined,添加为window的属性
        function定义的全局函数赋值为函数本身,添加为window的方法
        this赋值为window
 3)开始执行全局代码
3,函数执行上下文:
       1)在调用函数将要执行之前,创建对应的函数执行上下文对象
  2)对局部数据进行预处理
   形参变量赋值,添加为函数执行上下文的属性
   arguments赋值实参列表,添加为函数执行上下文的属性
           var定义的函数变量赋值为undefined,添加为函数执行上下文的属性
           function声明的函数赋值为函数本身, 添加为函数执行上下文的方法
          this赋值调用函数的对象
       3)开始执行函数代码  
在变量,函数等定义前我们就可以访问,说明它们已经存在了,也就是在执行全局代码之前就必须进行一些准备工作。
二,函数高级_第8张图片二,函数高级_第9张图片
执行上下文栈:
用来保存所有产生的执行上下文对象
    在全局代码执行前,JS引擎会创建一个栈来存储管理所有的执行上下文对象
    在全局执行上下文window确定后,将其添加到栈中(压栈)
    在函数执行上下文创建后,将其压栈
   在当前函数执行完毕后,将栈顶移出(出栈)
   当所有代码执行完毕后,栈中只剩下window
二,函数高级_第10张图片二,函数高级_第11张图片
二,函数高级_第12张图片二,函数高级_第13张图片

执行上下文在调用函数时创建是动态的
四,作用域和作用域链
作用域:一个代码段所在的区域,它是静态的(相对于执行上下文),在编写代码时就确定了,
分类:全局作用域,函数作用域;(ES5中没有块作用,ES6中有块作用域{})
作用:隔离变量,不同作用域下同名的变量不会有冲突

作用域是在编写代码时就确定了,定义了几个函数就有几个函数作用域与函数调用次数无关,因此整个JS文件的作用域个数为N(函数个数)+1(全局作用域)
二,函数高级_第14张图片二,函数高级_第15张图片
作用域与执行上下文的区别:
1)除全局作用域外,每个函数都会创建之间的作用域,作用域在函数创建时就已经创建;全局执行上下文在全局作用域确定后JS代码执行前创建(全局变量和全局函数预解析完成后产生全局上下文),函数执行上下文是在函数调用时,函数内部代码执行前产生的
2)作用域是静态的,只要函数定义了函数作用域就存在,不会再发生变化;上下文环境是动态的,在调用函数时创建,函数调用结束上下文环境被自动释放
作用域与执行上下文的联系:
上下文环境是从属于作用域的,全局上下文环境对应全局作用域,函数上下文环境对应函数作用域

五,闭包
1,如何产生闭包?闭包是什么?闭包产生的条件?
1)当函数的内部函数引用了嵌套的外部函数(父)函数中的变量/函数时,就产生了闭包
2)闭包是嵌套的内部函数(闭包存在于嵌套函数的内部函数中)
3)嵌套函数,内部函数引用了外部函数的数据(变量/函数)
当我们想要通过循环遍历来实现点击不同按钮输出对应的第几个按钮时,采用如下方法会发输出都是3,这是为什么呢?
for()中的变量i是全局变量,for()中的代码会立即执行完毕i=3,for()执行完毕再执行{}里面的内容,{}里面没有定义i就会去全局作用域中寻找i,此时i是3所以无论点击哪一个都输输出3
二,函数高级_第16张图片二,函数高级_第17张图片
 将btn所对应的下标保存在btn上,this指向点击的按钮对象btns[i]
二,函数高级_第18张图片

执行函数定义就会产生闭包,不用调用内部函数但是需要调用外部函数
2,常见的闭包
1)将函数作为另一个函数的返回值
2)将函数作为实参传递给另一个函数调用
二,函数高级_第19张图片二,函数高级_第20张图片
1)var f = fn();//创建内部函数对象产生闭包,此时产生闭包,但是没有输出,因为代码还没有执行。调用两次f()函数输出2和3,说明a变量在累加并没有消失,但是从代码结构上看a是fn的局部变量,不在函数fn2中,正常情况下  a在执行fn1时产生,fn1调用结束死亡,但在var f = fn();调用完fn时a明显没有死亡
外部函数调用几次创建几个内部函数就产生几个闭包。
上述代码在执行到var a =1 时闭包就产生了因为存在函数声明提升
3,闭包的作用
1)使函数的内部变量在函数执行完毕后仍存活在内存中(延长布局变量生命周期)
2)让函数外部可以读写(操作)函数内部的数据(变量/函数)
4,闭包的生命周期
1)产生:在嵌套内部函数定义执行完就产生了闭包,并不是在内部函数调用时产生,而是内部函数定义了就产生了闭包(浏览器预解析时会将函数声明提前,执行完函数声明就产生了闭包)
2)死亡:内部函数成为垃圾对象时死亡,(f=null)
问题:
1)函数执行完毕后,函数内部声明的局部变量是否存在?
一般不存在,被内部函数引用的变量在外部函数执行完毕后还存在,直到内部函数对象成为垃圾对象时才死亡;但是不是所有的在外部函数里声明的变量都会存在,而是只有被内部函数引用的变量才存在。
2)在函数外部能直接访问/操作函数内部的局部变量?
不能,但可以通过闭包让外部操作函数内部的局部变量。
5,闭包的应用
定义JS模块

JS模块:具有特定功能的JS文件,将所有的数据和功能都封装在一个函数内部(私有的),只向外暴露一个包含多个方法的对象或函数;模块的使用者只需通过模块暴露的对象调用方法来实现对应的功能。
二,函数高级_第21张图片二,函数高级_第22张图片
6,闭包的缺陷
1)函数执行完毕后,函数内的局部变量不会被释放,占据内存时间变长
2)容易造成内存泄露
解决方法:1)尽量不使用闭包;2)及时释放内存(让内部函数成为垃圾对象回收闭包)
7,内存溢出与内存泄露

1)内存溢出:出现运行时出现的错误,是会报错的。当程序运行需要的内存超过了剩余内存时就会抛出内存溢出的错误。如创建很多个超大的数组对象。出现运行空间有限,当超出了这个空间就会溢出。
2)内存泄露:程序仍可以运行,但是可用内存变小。闭包中局部变量没有及时释放就会占据内存。
    占用的内存没有及时释放;内存泄露积累过多会导致内存溢出;
3)常见的内存泄露:意外的全局变量;没有及时清理的计时器或回调函数;闭包

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(js高级)