Day14-JS进阶-

一、回调函数

二、IIFE

三、函数中的this

四、关于语句分号问题

五、原型与原型链

六、探索instanceof

七、执行上下文

八、作用域与作用域链

一、回调函数

 

 

 

 

二、IIFE(立即调用函数表达式)

 

 也可用于编写 js 模块

  var a = function (){
            console.log("a");
        }
        function b(){
            console.log("b");
        }
        (function c(){ //匿名函数自调用这个和IIFE其实是同一个概念的
            console.log("c");
        })()

最后打印出来的 只有 c

那就有一个疑惑了,为什么不能直接把这个匿名函数的内部代码直接在外面写了,而是要用这个“包”起来呢?

因为包起来的话,就是一个局部了,里面的变量就是一个局部变量,不会影响全局变量

(function d(){
            var a = 1;
            function test(){
                console.log(++a);
            }
             function test2(){
                console.log("test2");
            }
            window.$ = function(){
                return {
                    test: test
                }
            }
        })();

        $().test()

这种的话,就是把test向外暴露了,暴露为一个全局函数,但是test2没暴露出来,这也是匿名函数的一个功能了

其中不要被$里面了,在匿名函数d中,我们给这个$定义了一个函数,$其实就是一个函数,这个函数执行之后,就会返回一个对象

这个对象的名字叫做test,然后就可以通过test()来调用这个test函数了

 

三、函数中的this 

 

 

扩展:函数call

https://www.w3school.com.cn/js/js_function_call.asp

 

 

<script>
        //函数外面全局的this就是window
        function Person(color){
            console.log(this);//这个this不是Person,它代表的是谁来调用这个Person
            this.color = color;
            this.getColor = function(){
                console.log(this);
                return this.color;
            };
            this.setColor = function(color){
                console.log(this);
                this.color = color;
            };
        }
        Person("red"); //这个时候就输出语句一个,this是window,因为没人调用,就是用window直接调用了
        var p = new Person("yello");//这个时候的this 是 Person,也可以说是p,因为p指向这个对象

        p.getColor();//这个时候的this 是 p

        var obj = {};
        p.setColor.call(obj , "black");//这里的this是obj,把obj打印出来就是一个对象,有一个color属性值为black
        // console.log(obj);

        var test = p.setColor;
        test();//这个时候的this是 window,因为test是一个函数,没东西调用,就是window调用
        //全局调用的话this都是window

        function fun1(){
            function fun2(){
                console.log(this);
            }
            fun2();//this是window
        }
        fun1();
    </script>

 

四、关于语句分号问题

 

 

 一般在做项目的时候,如果用到很多js文件的话,都是在这个文件开始的时候添加上一个分号,防止代码合并的时候会出错的

 

五、原型与原型链

  1、原型prototype

下面是打印 Date.prototype 的结果

 

 可以看到是一个object类型的,然后里面有很多内置的方法,这些方法都是给实例对象使用的

 

 如果是这样打印的话,就是一个龙的object,但是我们可以人工的给这个函数原型添加方法

 

 

 

 

 

 

 

 ②

 

 返回了两个 true

 

 也就是说构造函数和它的原型对象是有一种相互引用的关系,也就是构造函数的一个叫做prototype的属性可以找到这个构造函数的原型

并且这个构造函数的原型里面也有一个属性 叫做 constructor 可以找到这个构造函数本身

 

 

2、显示原型和隐式原型

 

 

<script>
        function Fn(){

        };
        console.log(Fn.prototype);
        var fn = new Fn();
        console.log(fn.__proto__);

 

 

 

 原因就是第三句话,要验证的话,直接用===来判断即可,prototype和__proto__共同指向原型对象,都是引用值,保存 地址值的

函数的prototype这个属性是在函数创建的时候就写入了,而__proto__这个属性是在实例对象创建的时候生成的-new的时候(都是js引擎自动帮我们加的)

new Fn()也就是这个创建这个函数对象的时候,内部执行了一个赋值就是把这个函数的prototype属性的值赋值给了这个实例对象的__proto__属性的值

function Fn(){}的时候就是一个创建函数对象的时候,就是给了这个函数的prototype属性赋值为了一个空的object

 

 

这个图的左边是一个栈,右边是堆

1、function Fn(){} 定义了一个函数对象(放在堆里面),然后这个函数名Fn放在栈空间里面

 其中Fn:0x123,表示的是他存了一个值(地址值)是ox123,也就是说在堆中这个函数对象的地址就是0x123

prototype是一个引用变量属性,他的值是地址值,他指向的是一个原型对象 { }

===第一条语句 执行之后,绘制的图是 

 

 

 2、console.log(Fn.prototype)输出的就是这个对象了

 

 3、 var fn= new Fn()这个fn也是一个引用类型,在栈中fn的值就是一个地址值了

4、最后的fn.test其实是,以为fn.的话有一个点,就会找到Fn实例对象:

 

 

 

 但是这个对象里面没有这个test方法,但是最终还是找到了,所以说它的原理就是

如果在 fn 。也就是实例对象中找不到这个方法的话,就会根据这个实例对象的
__proto__属性来找到原型对象object,再在这个原型对象里面找这个test方法

 

 

 

 5、原型链

 

 即使是后面才给这个函数添加test2方法的,但是在那之前就打印出来了,因为我们拿到的是最总的函数)

这个空的Object也是object的一个原型

Object本身就是存在的,并且是全局的,一开始就有的,

先有了原型才有实例对象

1、第一个语句 

function Fn(){

this.test1 = function() {

console.log("test1()")

}

这个语句 就画了下面这个图片了

 

 上面的Object 那三个图,都是一上来就有的所以js引擎一开始的时候是把js内置的一些函数和方法加载进来了

 

 

 

 

补充:

 

 

 

 

4、原型链-属性问题

==前面都是给这个函数添加方法,1这里就是给这个函数添加属性

 

 这两个输出的都是 xxx ,但是打印fn2.a的时候就会显示yyy了

===为什么后面通过 fn2.a=‘yyy’没有把前面的xxx覆盖掉了呢

我们把修改之后的fn2打印可以看到

 

 自身多了一个a属性,然后原型也有一个a属性的

注意:原型链是在查找的时候用到的,但是在设置原型链的时候是不看的

 

 六、探索instanceof

 

 

 如果是B的话就走一部,也就是找prototype的显示原型属性,如果是a的话就可以走很多步,但是每一步都要走隐式原型属性的__proto__

 

 

 

 

面试题:

 

 它的思路是,先生成了一个 n==2,m==3的对象,之后再赋值给a的原型的,并不是直接修改原来a原型指向的东西

因为a的原型保存的是地址值,所以就不是一个东西了,因为地址改变了,指向就不同了4

 所以结果就是:b.n==1 b.m=undefined    c.n==2 c.m==3

 

第一个f.a可以和object本身就有的 toString 方法类比,因为直接对了object的原型添加了方法和属性,那么f就可以直接访问它们了

这个a属性和tostring是放在一个容器里面的

 

 

 

 

 这个就是打印出来的f,也就是原型链了,因为找到proto的时候是找到了这个F函数,但是里面没有a,所以再找下一个proto就找到了Object的原型,就可以找到a这个属性了

但是f.b是找不到的,就会报错了

 

 

 

 

 

 上面这个部分就比较重要了

 

 

 

 其中的除了 f.b()是会报错之外(因为找不到这个属性),其他的都是可以执行打印出来的

 

七、执行上下文

 

 

执行上下文栈

 

 这个代码其实是没问题的,

因为在bar里面的调用f00的时候,其实下面的foo函数定义已经执行了,因为函数定义执行并不待变前面的foo的调用已经执行了

 就是前面的bar和foo是两条赋值语句,并不代表函数就执行了,最后面才调用了bar函数的

在上面那个函数中 产生了三个执行上下文对象,一个是window作为全局执行上下文 两个函数对象function

(函数被调用的时候就会产生函数执行上下文对象,定义的时候是没有产生的)

也就是

 

 这两个语句执行的时候,就会产生函数的执行上下文了

所以执行上下文栈中的话,就是n+1层,其中n就是函数被执行的语句(调用函数的次数),1就是window产生的全局执行上希望了

同理:

 

 如果代码是这样的话,执行上下文栈中就有5层了,因为一个bar调用然后引发一个foo调用,两个bar的话,就有4词被调用了,再加上wondow全局调用

 

 

 

一个函数被调用,就会在执行上下文栈中添加,然后执行完了之后,就会出栈的了,就被释放出来了(正在执行的都是栈顶的对象的)

也就是说全部的代码都执行完了之后, 执行上下文栈中留下的就只有 window了

那么栈底有没有可能不是window呢?--不可能,因为第一个产生的就是window(全局执行上下文和当前执行上下文均只能由一个,其他都是要进行等待的了)

(小扩展)如果用f1里面调用f2和f3的话,执行上下文栈中最多由三个执行上下文,因为在f1中调用f2的时候,不会调用f3,当执行晚了f2之后才会调用f3,所以同时最多的话只能是3个了

 

面试题:

 

 

 

面试题:

1、

 

 输出的要么是undefined 要么就是 function==这里就涉及到了变量提升和函数提升顺序了

====先执行变量提升,再执行函数提升

所以最终打印出来的就是 function 了

2、

 

 在就涉及到,在if之前对b进行了声明定义,还是在先if再对b声明了

 

  实际上的代码实现是这样的

 

所以说就会报错了,因为c不是函数来的,(先是变量提升之后才是函数提升)

 也就是c是变量了,c(2)的话就会报错了,也就是说 定义的函数c里面的代码都是没有执行的

 

八、作用域与作用域链

1、块作用域就是在 大括号里面的作用域了

 

 这里有几个作用域呢?

外面的全局作用域+定义了几个函数

 

 注意:

 

 这里的b其实,会打印20的,因为b是全局的,在里面也是可以访问到的

因为b是先在这个函数里面找的,如果没有的话,就去外面找的

 

2、作用域 与 执行上下文

 

 

 作用域链 就是嵌套的作用域 由内向外的一个过程

相关面试题:

 

 打印出来的答案 是 10

因为相当于是直接调用了fn,也就是要在fn里面找有没有x,如果没有的话,就在外部作用域里面找了,(注意 并不是在show函数里面执行,而是会回到上面的fn作用域中执行的)

面试题二:

 

如果是输出fn的话,就会吧赋值给fn的这个函数打印出来了

在输出fn2的时候,开始在函数里面找不到,所以在外部作用域里面找,想要找到fn2的话,就是要找obj这个对象里面找才行,也就是this.fn2才行

所以在外面是找不到的

 

 就是在沿着作用域找的时候没找到

但是如果打印this的话就可以找到了

 

 

posted @ 2020-07-03 13:46  SCAU-gogocj  阅读(158)  评论(0编辑  收藏  举报