原型与原型链

函数的prototype

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.函数的prototype属性
        每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)
        原型对象中有一个属性constructor,它指向函数对象
    2.给原型对象添加属性(一般都是方法)
        作用:函数的所有实例对象自动拥有原型中的属性(方法)
-->
<script type="text/javascript">
    //每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为:原型对象)
    console.log(Date.prototype,typeof Date.prototype)
    function Fun(){//alt+shift+r(重命名rename)

    }
    Fun.prototype.test=function (){
        console.log('test()')
    }
    console.log(Fun.prototype)//默认指向一个Object空对象(没有我们的属性)
    //原型对象中有一个属性constructor,它指向函数对象
    console.log(Date.prototype.constructor===Date)
    console.log(Fun.prototype.constructor===Fun)
    //给原型对象添加属性(一般是方法)==>实例对象可以访问
    Fun.prototype.test=function (){
        console.log('test()')
    }
    var fun=new Fun()
    fun.test()
</script>
</body>
</html>

显式原型与隐式原型

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.每个函数function都有一个prototype,即显式原型(属性)
    2.每个实例对象都有一个_proto_,可称为隐式原型(属性)
    3.对象的隐式原型的值为其对应构造函数的显式原型的值
    4.内存结构
    5.总结:
        -函数的prototype属性:在定义函数时自动添加的,默认值是一个object对象
        -对象的_proto_属性:创建对象时自动添加的,默认值为构造函数的prototype属性值
        -程序员能直接操作显式原型,但不能直接操作隐式原型(ES6之前)
-->
<script type="text/javascript">
    function Fn(){//内部语句:this.prototype={}

    }
    //1.每个函数function都有一个prototype,即显式原型(属性)
    console.log(Fn.prototype)
    //2.每个实例对象都有一个_proto_,可称为隐式原型(属性)
    var fn=new Fn()//内部语句:this._proto_=Fn.prototype
    console.log(fn.__proto__)
    //3.对象的隐式原型的值为其对应构造函数的显式原型的值
    console.log(Fn.prototype===fn.__proto__)//true
    //给原型添加方法
    Fn.prototype.test=function (){
        console.log('test()')
    }
    //通过实例对象调用原型的方法
    fn.test()
</script>

</body>
</html>

原型链

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.原型链
        -访问一个对象的属性时,
            先在自身属性中查找,找到返回
            如果没有,再沿着__proto__这条链向上查找,找到返回
            如果最终没找到,返回undefined
        -别名:隐式原型链
        -作用:查找对象的属性(方法)
    2.构造函数/原型/实体对象的关系
    3.构造函数/原型/实体对象的关系2
-->
<script type="text/javascript">
    function Fn(){
        this.test1=function (){
            console.log('test1()')
        }
    }
    Fn.prototype.test2=function (){
        console.log('test2()')
    }
    var fn=new Fn()

    fn.test1()
    fn.test2()
    console.log(fn.toString())
    console.log('test3()')
    fn.test3()
    /*
        1.函数的显式原型指向的对象默认是空的object实例对象(但Object不满足)

     */
    console.log(Fn.prototype instanceof Object)//true
    console.log(Object.prototype instanceof Object)//false
    console.log(Function.prototype instanceof Object)//true
    /*
        2.所有函数都是Function的实例(包括它本身)
     */
    console.log(Function.__proto__===Function.prototype)
    /*
        3.Object的原型对象是原型链的尽头
     */
    console.log(Object.prototype.__proto__)//null
</script>
</body>
</html>

原型链的属性问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.读取对象的属性值时:会自动到原型链中查找
    2.设置对象的属性值时:不会查找原型链,如果当前对象中没有此属性,直接添加此属性并设置其值
    3.方法一般定义在原型中,属性一般通过构造函数定义在对象本身上
-->
<script type="text/javascript">
    function Fn(){

    }
    Fn.prototype.a='xxx'
    var fn1=new Fn()
    console.log(fn1.a,fn1)

    var fn2=new Fn()
    fn2.a='yyy'
    console.log(fn1.a,fn2.a,fn2)

    function Person(name,age){
        this.name=name
        this.age=age
    }
    Person.prototype.setName=function (name){
        this.name=name
    }
    var p1=new Person('Tom',12)
    p1.setName('Bob')
    console.log(p1)

    var p2=new Person('Jack',12)
    p1.setName('Cat')
    console.log(p2)
    console.log(p1.__proto__===p2.__proto__)//true

</script>
</body>
</html>

探索instanceof

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
  1.instanceof是如何判断的?
    -表达式:A instanceof B
    -如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false
  2.Function是通过new自己产生的实例
-->
<script type="text/javascript">
  /*
    案例1
   */
  function Foo(){}
  var f1=new Foo()
  console.log(f1 instanceof Foo);//true
  console.log(f1 instanceof Object);//true

  /*
    案例2
   */
  console.log(Object instanceof Function)//true
  console.log(Object instanceof Object)//true
  console.log(Function instanceof Function)//true
  console.log(Function instanceof Object)//true

  function Foo(){}
  console.log(Object instanceof Foo)//false
</script>
</body>
</html>

面试题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    //测试题1
    function A(){

    }
    A.prototype.n=1
    var b=new A()
    A.prototype={
        n:2,
        m:3
    }
    var c=new A()
    console.log(b.n,b.m,c.n,c.m)

    //测试题2
    function F(){}
    Object.prototype.a=function (){
        console.log('a()')
    }
    Function.prototype.b=function (){
        console.log('b()')
    }
    var f=new F()
    f.a()
    //f.b()
    F.a()
    F.b()
    console.log(f)
    console.log(Object.prototype)
    console.log(Function.prototype)


</script>
</body>
</html>

执行上下文与执行上下文栈

变量提升与函数提升

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.变量声明提升
        -通过var定义(声明)的变量,在定义语句之前就可以访问到
        -值:undefined
    2.函数声明提升
        -通过function声明的函数,在之前就可以直接调用
        -值:函数定义(对象)
    3.问题:变量提升和函数提升是如何产生的?
-->
<script type="text/javascript">
    /*
        面试题:输出undefined
     */
    var a=3
    function fn(){
        console.log(a)
        var a=4

    }
    fn()

    console.log(b)//undefined  变量提升
    fn2()//可以调用 函数提升
    fn3()//不能调用 变量提升

    var b=3
    function  fn2(){
        console.log('fn2()')
    }
    var fn3=function (){
        console.log('fn3()')
    }

</script>
</body>
</html>

执行上下文

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.代码分类(位置)
        -全局代码
        -函数(局部)代码
    2.全局执行上下文
        -在执行全局代码前将window确定为全局执行上下文
        -对全局数据进行预处理
            -var定义的全局变量==>undefined,添加为window的属性
            -function声明的全局函数==>赋值(fun),添加为window的方法
            -this==>赋值(window)
        -开始执行全局代码
    3.函数执行上下文
        -在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
        -对局部数据进行处理
            -形参变量==>赋值(实参)==>添加为执行上下文的属性
            -arguments==>赋值(实参列表),添加为执行上下文的属性
            -var定义的局部变量==>undefined,添加为执行上下文的属性
            -function声明的函数==>赋值(fun),添加为执行上下文的方法
            this==>赋值(调用函数的对象)
        -开始执行函数体代码
-->
<script type="text/javascript">
    console.log(a1,window.a1)
    window.a2()
    console.log(this)

    var a1=3
    function a2(){
        console.log('a2()')
    }
    console.log(a1)
    console.log('----------')
    //函数执行上下文
    function fn(a1){
        console.log(a1)//2
        console.log(a2)//undefined
        a3()//a3()
        console.log(this)//window
        console.log(arguments)//伪数组(2,3)

        var a2=3
        function a3(){
            console.log('a3()')
        }
    }
    fn(2,3)
</script>
</body>
</html>

执行上下文栈

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
  1.在全局代码执行前,JS引擎就会创建一个栈来存储管理所有的执行上下文对象
  2.在全局执行上下文(window)确定后,将其添加到栈中(压栈)
  3.在函数执行上下文创建后,将其添加到栈中(压栈)
  4.在当前函数执行完后,将栈顶的对象移除(出栈)
  5.当所有的代码执行完后,栈中只剩下window
-->
<script type="text/javascript">
                                //1.进入全局执行上下文
    var a=10
    var bar=function (x){
        var b=5
        foo(x+b)                //3.进入foo执行上下文
    }
    var foo=function (y){
        var c=5
        console.log(a+c+y)
    }
                                //2.进入bar执行上下文
    bar(10)
</script>
</body>
</html>

执行上下文栈2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.依次输出什么?
        -gb:undefined
        -fb:1
        -fb:2
        -fb:3
        -fe:3
        -fe:2
        -ge:1
    2.整个过程中产生了几个执行上下文?  5
-->
<script type="text/javascript">
    console.log('gb:'+i)
    var i=1
    foo(1)
    function foo(i){
        if(i==4){
            return
        }
        console.log('fb:'+i)
        foo(i+1)//递归调用:在函数内部调用自己
        console.log('fe:'+i)
    }

    console.log('ge:'+i)
</script>
</body>
</html>

面试题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    /*
        测试题1:先执行变量提升,再执行函数提升
     */
    function a(){}
    var a;
    console.log(typeof a)//‘function'

    /*
        测试题2:
     */
    if(!(b in window)){
        var b=1;
    }
    console.log(b)//'undefined'

    /*
        测试3:
     */
    var c=1
    function c(c){
        console.log(c)
    }

    c(2)//报错
</script>
</body>
</html>

作用域与作用域链

作用域

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.理解
        -就是一块"地盘",一个代码段所在的区域
        -它是静态的(相对于上下文对象),在编写代码时就确定了
    2.分类
        -全局作用域
        -函数作用域
        -没有块作用域(ES6有)
    3.作用
        -隔离变量,不同作用域下同名变量不会有冲突
    4.区别1
        -全局作用域之外,每个函数都会创建自己的作用域,作用域在函数定义时就已经确定了,而不是在函数调用时
        -全局执行上下文环境是在全局作用域确定之后,js代码马上执行之前创建
        -函数执行上下文是在调用函数时,函数体代码执行之前创建
    5.区别2
        -作用域是静态的,只要函数定义好了就一直存在,且不会再变化
        -执行上下文是动态的,调用函数时创建,函数调用结束时就会自动释放
    6.联系
        -执行上下文(对象)是从属于所在的作用域
        -全局上下文环境==>全局作用域
        -函数上下文环境==>对应的函数使用域
-->
<script type="text/javascript">
    var a=10,
        b=20
    function fn(x){
        var a=100,
            c=300;
        console.log('fn()',a,b,c,x)
        function bar(x){
            var a=1000,
                d=400
            console.log('bar()',a,b,c,d,x)

        }
        bar(100)
        bar(200)
    }
    fn(10)
</script>
</body>
</html>

作用域链

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.理解
        -多个上下级关系的作用域形成的链,它的方向是从下向上的(从内到外)
        -查找变量时就是沿着作用域链来查找到
    2.查找一个变量的查找规则
        -在当前作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2
         -在上一级作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入3
         -再次执行2的相同操作,直到全局作用域,如果还找不到就抛出找不到的异常

-->
<script type="text/javascript">
    var a=1
    function fn1(){
        var b=2
        function fn2(){
            var c=3
            console.log(c)
            console.log(b)
            console.log(a)
            console.log(d)
        }
        fn(2)
    }
    fn1(0)
</script>
</body>
</html>

面试题1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
  var x=10;
  function fn(){
    console.log(x);
  }
  function show(f){
    var x=20;
    f()
  }
  show(fn);
</script>
</body>
</html>

面试题2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    var fn=function (){
        console.log(fn)
    }
    fn()

    var obj={
        fn2:function (){
            console.log(fn2)
        }
    }
    obj.fn2()
</script>
</body>
</html>

闭包

引入

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button>测试1</button>
<button>测试2</button>
<button>测试3</button>
<!--
    需求:点击某个按钮,提示”点击的是第n个按钮“
-->
<script type="text/javascript">
    var btns=document.getElementsByTagName('button')
    //遍历加监听
    for(var i=0,length=btns.length;i<length;i++){
        var obj=btns[i]
        //将btn所对应的下标保存在btn上
        btn.index=i
        btn.onclick=function (){
            alert('第'+(this.index+1)+'个')
        }
    }
</script>
</body>
</html>

闭包的理解

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.如何产生闭包?
        -当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
    2.闭包到底是什么?
        -使用chrome调试查看
        -理解一:闭包是嵌套的内部函数(绝大部分人)
        -理解二:包含被引用变量(函数)的对象(极少数人)
        -注意:闭包存在于嵌套的内部函数中
    3.产生闭包的条件?
        -函数嵌套
        -内部函数引用了外部函数的数据(变量/函数)
-->
<script type="text/javascript">
    function fn1(){
        var a=2
        var b='abc'
        function fn2(){//执行函数定义就会产生闭包(不用调用内部函数)
            console.log(a)
        }
    }
    fn1()
</script>
</body>
</html>

常见的闭包

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.将函数作为另一个函数的返回值
    2.将函数作为实参传递给另一个函数调用
-->
<script type="text/javascript">
    //1.将函数作为另一个函数的返回值
    function fn1(){
        var a=2
        function fn2(){
            a++
            console.log(a)
        }
        return fn2
    }
    var f=fn1()
    f()//3
    f()//4
    //2.将函数作为实参传递给另一个函数调用
    function showDelay(msg,time){
        setTimeout(function (){
            alert(msg)
        },time)
    }
    showDelay('atguigu',2000)
</script>
</body>
</html>

闭包的作用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
    2.让函数外部可以操作(读写)到函数内部的数据(变量/函数)

    问题:
        -函数执行完后,函数内部生命的局部变量是否还存在? 一般情况下不存在,存在于闭包中的变量才可能存在
        -在函数外部能直接访问函数内部的局部变量吗? 不能,但我们可以通过闭包让外部操作它
-->
<script type="text/javascript">
    function fn1(){
        var a=2
        function fn2(){
            a++
            console.log(a)
        }
        function fn3(){
            a--
            console.log(a)
        }
        return fn3
    }
    var f=fn1()
    f()//1
    f()//0
</script>
</body>
</html>

闭包的生命周期

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.产生:在嵌套内部函数定义执行完时就产生了(不是在调用)
    2.死亡:在嵌套的内部函数成为垃圾对象时
-->
<script type="text/javascript">
    function fn1(){
        //此时已经产生闭包(函数提升,内部函数对象已经创建了)
        var a=2
        function fn2(){
            a++
            console.log(a)
        }
        return fn2
    }
    var f=fn1()
    f()//3
    f()//4
    f=null//闭包死亡(包含闭包的函数对象成为垃圾对象)
</script>
</body>
</html>

闭包应用-自定义JS模块

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>闭包</title>
</head>
<body>
<!--
    闭包的应用:定义JS模块
        -具有特定功能的js文件
        -将所有的数据和功能都封装在一个函数内部(私有的)
        -只向外暴露一个包含n个方法的对象或函数
        -模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
-->
<script type="text/javascript" src="myModule.js">

</script>
<script type="text/javascript">
   var module= myModule()
   module.doSomething()
   module.doOtherthing()

</script>
</body>
</html>

闭包的缺点及解决

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.缺点
        -函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
        -容易造成内存泄漏
    2.解决
        -能不用闭包就不用
        -及时释放
-->
<script type="text/javascript">
    function fn1(){
        var arr=new Array[100000]
        function fn2(){
            console.log(arr.length)
        }
        return fn2
    }
    var f=fn1()
    f()
    f=null//让内部函数成为垃圾对象-->回收闭包
</script>
</body>
</html>

内存溢出与内存泄漏

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    1.内存溢出
        -一种程序运行出现的错误
        -当程序运行需要的内存超过了剩余的内存时,就会抛出内存溢出的错误
    2.内存泄漏
        -占用的内存没有及时释放
        -内存泄漏积累多了就容易导致内存溢出
        -常见的内存泄露
            -意外的全局变量
            -没有及时清理的计时器或回调函数
            -闭包

-->
<script type="text/javascript">
    //1.内存溢出
    var obj={}
    for(var i=0;i<10000;i++){
        obj[i]=new Array(1000000000)
        console.log('-----')
    }
    //2.内存泄露
    //意外的全局变量
    function fn(){
        a=3
        console.log(a)
    }
    fn()

    var intervalId=setInterval(function (){//启动循环定时器后不清理
        console.log('----')
    },1000)

    //clearInterval(intervalId)

    //闭包
    function fn1(){
        var a=4
        function fn2(){
            console.log(++a)
        }
        return fn2
    }
    var f=fn1()
    f()
    //f=null
</script>
</body>
</html>

面试题1

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    //代码片段1
    var name="The window";
    var object={
        name:"My Object",
        getNameFunc:function (){
            return function (){
                return this.name;
            };
        }
    };
    alert(object.getNameFunc()()); //? the window

    //代码片段2
    var name2="The window";
    var object2={
        name:"My Object",
        getNameFunc:function (){
            var that=this;
            return function (){
                return that.name;
            };
        }
    };
    alert(object2.getNameFunc()());//My Object

</script>
</body>
</html>

面试题2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
  function fun(n,o){
      console.log(o)
      return{
          fun:function (m){
              return fun(m,n);
          }
      };
  }
  var a=fun(0);a.fun(1);a.fun(2);a.fun(3);//undefined,0,0,0
  var b=fun(0);fun(1);fun(2);fun(3);//undefined,0,1,2
  var c=fun(0);fun(1);c.fun(2);c.fun(3);//undefined,0,1,1

</script>
</body>
</html>

Q.E.D.