js高级

1、js的数据类型: 

      分类: 基本类型(5种),引用类型 

基本(值)类型 ,保存在栈中:

     String:任意字符串

      Number:任意数字

      boolean:true/false

      undefined:undefined

       null: null  可以用来判断undefined和null

引用类型,保存在堆中:

      Object: 任意对象

       Function:一种特殊的对象(可以执行)

        Array: 一种特殊的对象(数值下表,内部数据是有序的)

判断:

          typeof: 返回数据类型的字符串表达,首字母都是小写。 比如var a; console.log(typeof a)                  //'undefined ',typeof可以用来判断undefined('undefined')、字符串(‘string’)、bool、函数('function'),但是他不能够准确区分null和object类型还有arrya和object(返回的都是‘object’)

          instanceof : 判断对象的具体类型,格式console.log(a instabceof b)用来判断a是否是b                              的一个实例,即a是一个实例,b是一个构造函数。比如 var a =[]  ;console.log(a instabceof Array)返回true,j即用来判断数组这个对象是否是 数组 === : 通常是通过判断语句的返回值来判断,可以判断null和undefined

2、undefined 和null的区别

undefined: 指的是定义了但是没有赋值,此时为undefined

null: 指的是定义了并且进行了赋值只是赋值为null

*什么时候会用到null类型

       *初始赋值时,当你想要定义一个变量赋值为对象时,因为刚开始不知道对象内容可以包含什么时就可以给变量赋值为null,如var a = null 用来告诉别人a是一个对象。

       *当不在使用时为了释放内存,让对象变成一个垃圾对象(垃圾对象是指:没有变量引用/指向该对象),可以被垃圾回收机制回收,那么就给变量赋值为null

3、数据、内存、变量

*数据:存储在内存中代表特定信息的东西,本质上是二进制  如 var  age = 18

*内存:内存条通电后产生的可存储数据的空间(临时的)

*变量: 可变化的量,用来存储数据。每一个变量都对应一个小内存,变量名用来查找对应的内存,变量值就是内存的数据。

3.1、关于引用变量赋值赋值的问题

*两个引用变量指向同一个对象,通过一个变量修改对象内部的数据,其他的所有变量看到的是修改之后的数据

var obj = {name :‘tom’}

var   obj1 = obj2

function fn(funobj)     //  此处形参指向 {name :‘tom’},然后又将该对象进行修改

{

funobj.name  = 'A'

}

fn(obj1)  //此处传入实参,相当于funobj=obj1 ,所以funobj1指向和obj相同的对象

console.log(obj2.name)   //A

*2个引用变量指向通过一个对象,让其中一个变量指向另一个变量,另一个引用变量依然指向前一个对象

var  a = {age : 12}

var b=a 

a = {name : 'BOB', age : '13'}  // 此处a被赋值新的对象,那么a中存储的地址也将会发生变化,而b中地址不发生改变。

b.age = 14

console.log(b.age, a.name , a.age)  // 14  BOB  13

function fn2 (obj)

{

obj = {age : 15}

}

fn2(a)  //此处传入实参,相当于obj=a ,但是又因为obj又被赋值为新的对象那么此时obj和a指向的地址不在是同一个,所以a.age还是13,由于函数运行完函数中的变量会被释放,此时不在有变量指向{age : 15},因此该对象会被垃圾回收机制回收

console.log(a.age)  // 13

3.2 、js在调用函数传递变量参数时,是值传递还是引用传递

         *理解1:都是值传递,都是传递一个变量中存储的内容,但是该内容分为基本类型的数据,或者是地址(如传递一个变量时)

         *理解2: 可能是值传递,也有可能是引用传递(即传递的内容是地址)

3.3、js的内存管理

      1、内存的生命周期

           *分配内存,得到该位置的使用权

           *存储数据,可以进行操作

           *释放内存

     2、释放内存

       *局部变量: 函数执行完自动释放

        *对象: 成为垃圾对象然后被垃圾回收机制回收

总结

4、对象

   4.1、什么是对象

     *多个数据的封装体

      *用来保存数据的容器 

       *一个对象代表一个现实中的一个事物

    4.2、为什么要使用对象

        *统一管理多个数据

     4.3、对象的组成

        *属性 :属性名(字符串)和属性值(任意)组成

        *方法:一种特别的属性(属性值是函数)

     4.4、如何访问对象内部数据

        *通过点方法,.属性名:编码简单,但有时不能用

        *['属性名']: 编码麻烦,能通用

     4.5、什么情况只能使用【'属性名'】

        *当属性的属性名中包含特殊字符时: -  空格

        *当属性名不确定是,如

         var  propName = 'name'

         var value = 'Tom'

         此时如果想在对象中添加{name : Tom}属性并且propName是可变的,就只能通过                       obj[propName] = value而不能 通过obj.propName = value来设置此时将会设置为                         {propName:Tom}

5、函数

     5.1、什么是函数

       *函数是实现特定功能的封装体,可以提高代码的复用,更便于阅读

      5.2、函数调用方式

        *test(): 直接调用

        *obj.test(): 通过对象调用

        *new test() : new调用

        *test.call/apply(obj): 临时让test成为obj的方法进行调用

                  var fun = function()

                   {

                    this.a = 'name'

                    }

                   var b = {}

                   fun.call(b)

                   console.log(b.a)

      5.3、回调函数

         *什么是回调函数

             自己定义的函数

             自己没有调用

             但是函数执行了 (在一定条件或者一定或者某个时刻)

         *常见的回调函数

             dom事件回调函数   ===》 this代表dom元素

             定时器回调函数    ===》 this代表window

              ajax回调函数

              生命周期回调函数(React中)

        *同步回调

             理解:立即执行,完全执行完才结束,不放入回调队列中

            例子  数组遍历,promise的excutor

        *异步回调

            理解:不会立即执行,会放入回调队列中将来执行

            例子:定时器回调/ajax回调/promise成功回调|失败回调

      5.4、IIFE (匿名函数自调用)

         *作用

             隐藏实现、不会污染全局命名空间,用他来编写模块

      5.5、this

         *什么是this

                .任何函数本质上都是通过某一个对象来调用的,如果没有直接指定就是window

                 所有函数内部都有一个变量this

                 他的值是调用函数的对象

         *如何确定this的指向

                 test():  window          

                 p.test() : p

                 new test() : 新建的对象

                  p.call(obj)  :obj

      5.6、函数的prototype属性

          *每一个函数都有一个prototype属性,他默认指向一个object对象(原型对象),原型对象添加的属性/方法格式为:fun.prototype.test() = function(){},该方法可以通过实例对象访问到

          *原型对象中有一个属性为constructor,他指向函数对象


 Type为函数对象,Type Prototype为原型对象

          *显示原型和隐式原型

                1、每一个函数function都有一个prototype,即显式原型

                2、每一个实例都有一个__proto__,可称为隐式原型

                3、对象的隐式原型的值为对应构造函数的显式原型的值 :fn.__proto__=Fn.prototype

                4、函数的prototype属性:在定义函数是自动添加的,默认值为一个空对象

                 5、对象的__proto__属性:实例化对象时添加的,默认值为构造函数的prototype

                  var Fn = function()   //默认执行了,this.prototype = {}
                  { 
                  }

                  var fn = new Fn()   内部语句 this.__proto__ = Fn.prototype


      5.6、原型链

             *原型链

                  *访问一个对象属性时,先在自己的属性中查找,找到返回,如果没有,再沿着__proto__这条链向上查找,找到返回。如果始终没有找到那么返回undefined

                   *别名:隐式原型链

                   *作用:查找对象的属性

图片讲解: 

左边矩形:栈空间,右边矩形:堆空间

Fn是一个构造函数,fn是Fn的一个实例。

Fn变量中存储的是函数对象的地址,又因为Fn是一个函数对象所以有一个prototype属性指向一个空的对象即Object空对象

fn变量中存储的是一个Fn的实例对象的地址,Fn实例对象的有一个__proto__属性他指向构造函数的prototype即Object空对象 ,上面语句可以总结为:fn.__proto__=Fn.prototype。

其中Object空对象也是一个实例,他的构造函数为Object函数对象,因此也符合上面的等式即:Object空对象.__proto__=Object函数对象.prototype(Object的原型对象)。所有的对象都是Object函数对象的一个实例



图片讲解:

       Object.prototype中添加的属性所有的实例对象,以及构造函数对象都可以访问到

       Function.prototype中添加的属性只有构造函数对象才可以访问到

        其实每一个函数都是一个实例,相当于var Fn = new Function(),他们的构造函数是       Function , 由于Fn是一个实例因此每一个函数除了有prototype属性外还有__proto__属性,因此有Fn.__proto__=Function.prototype成立,并且所有函数的__proto__都是一样的都是Function.prototype。对于Funtion来说有一个特殊的等式即Function.__proto__=Function.prototype即有Function = new Function()

          上面两张图的对应关系:

                fn<===>f1;

                Fn<===>Foo;

                Object空对象(Object函数对象的实例)<===>Foo.prototype(Objetc()的实例);

                Obejct函数对象<===>Object()

                Object的原型对象<===>Object.prototype

所有的原型对象都是Object的一个实例   函数对象. prototype.__proto__=Object.prototype。但是Object.prototype.__proto__=null

所有的函数对象都是Function的一个实例  函数对象.__proto__=Function.prototype,即Function本身也是本身的实例

      5.7、原型链补充

                1、函数的显示原型指向的对象默认是空的,但是Object实例对象除了Object之外

                 console.log(Fn.prototype instanceof Object)  //true

                 console.log(Object.prototype instanceof Object)  //false  因为                                                        Object.prototype.__proto__为null

                  console.log(Function.prototype instanceof Object)  //true

                  2、所有函数都是Function的实例(包含Function)

                  console.log(Function.__proto__===Function.prototype)

                   3、Object的原型对象是原型对象链的尽头

                   console.log(Object.prototype.__proto__)  //null


      5.8、instanceof是如何判断的

               *表达式: A instanceof B

               *如果B函数的显示原型对象在A对象的原型链上,返回true,否则返回false

自定义的prototype

      5.9、面试题


解析:function A(){}是一个构造函数,因此具有prototype属性指向一个空对象(假设地址为0x123),A.prototype.n=1添加一个属性,n:1. var b = new A()中b是一个实例具有__proto__属性,指向构造函数prototype(地址也为0x123)。下面一条语句又对A.prototype重新赋值,此时A.prototype不在指向地址0x123,此时假设指向0x234,此对象中包含n:2,m:3。对于原来对象即地址0x123由于实例b的__proto__属性指向他所以没有被回收。然后下一条语句又重新建立一个实例对象c,c的__proto__属性指向现在的A.prototype属性所指的对象即地址0x234,因此最后输出{1,undefined,2,3}


可以通过原型链图进行获取答案,结果为a(),报错,a(),b()

      5.10、变量声明提升和函数声明提升

                1、变量声明提升

                    *通过var定义的变量,在定义语句之前就可以访问到

                    *值:undefined

                 2、函数声明提升

                     *通过function声明的函数,在之前就可以直接调用


                     *值:函数定义的对象


                 例如:

                        console.log('------')  //当代码运行到此处时,window就包含了b值为undefied,fn2函数,fn3值为undefined

                         console.log(b)  //undefined  变量提升

                         fn2()  // ‘fn2函数’  此处可以调用函数

                         fn3()  //报错 ,此处不可以调用函数

                         var b =3 

                          function fn2()
                          { console.log('fn2函数')}

                          var fn3= function ()

                           { console.log('fn3函数')}

      5.11、执行上下文

                1、全局执行上下文

                   *在执行全局代码之前先将window确定为全局执行上下文

                   *对全局数据进行预处理(在执行代码前的准备工作)

                         *var定义的全局变量==>undefined,添加为window的属性

                         *function声明的全局函数==>赋值(fun),添加为window的方法

                         *this==>赋值(window)

                     *开始执行全局代码

                 2、函数执行上下文

                      *在调用函数时,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟对象,存在于栈中)

                      *对局部数据进行预处理

                              *形参变量==>赋值(实参)==>添加为执行上下文的属性

                              *arguements==>赋值(实参列表)==>添加为执行上下文的属性

                              *var定义的局部变量==>undefined,添加为执行上下文的属性

                              *function声明的函数==>赋值(fun),添加为执行上下文的属性

                              *this==>赋值(调用函数的对象)

                        *开始执行函数体代码

                  3、确定有几个执行上下文方法

                       *n+1,其中n为调用了几次函数,1为window执行上下文

      5.12、执行上下文栈

                 1、在全局代码执行前,js引擎会创建一个栈用来存储管理所有的执行上下文对象

                 2、在全局执行上下文(window)确定后,将其添加到栈 中(压栈)

                 3、在函数执行上下文创建后,将其添加到栈中

                 4、在当前函执行完毕之后,将栈顶的对象移除

                 5、当所有代码执行完毕之后,栈中只剩下window


      5.13、执行上下文,执行上下文栈面试题

              1、

            2、

              输出‘function’,该问题涉及到变量声明提升和函数声明提升的顺序,首先需要知道变量声明提升在函数声明提升之前。因此该代码相当于先声明变量然后在声明函数,又因为变量名相同,所以函数声明会将变量声明覆盖,因此输出function

 3、


该代码会运行时,会报错。解析:首先声明变量c,即var c ; 然后在声明函数,即function(){}.

然后在对c进行赋值 c = 1,然后执行c(2),此时的c相当于变量c而非函数c,因此会报错

     5.14、作用域链

             1、理解

                 *多个上下级关系的作用域形成的链,它的方向是从内到外

                 *查找变量时就是沿着作用域链来查找的

                2、查找一个变量的查找规则

                  *在当前的作用域下的执行上下文中查找对应的属性,如果有直接返回,否则进入2

                  *在上一级作用域的执行上下文中查找对应的属性,如果有直接返回,否则进入3

                  *再次执行2的相同操作,直到全局作用域,如果还不能找到就抛出异常

               3、作用域的作用:隔离变量使不同的作用域内可以使用相同的变量名而不冲突

               4、作用域链的作用:查找变量如果找到那么返回,如果找不到那么报错


     5.15、作用域和执行上下文的区别和联系 

                 1、区别1,产生的时间          

                       *全局作用域之外,每一个函数会创建自己的作用域,作用域在函数定义时就已经确定了。而不是在函数调用时。

                       *全局执行上下文是在全局作用域确定后,js代码马上执行之前创建的

                       *函数执行上下文是在调用函数时,函数体代码执行之前创建的

                   2、区别

                        *作用域是静态的,只要函数定义好了就一直存在,且不会再发生改变

                        *执行上下文是动态的,调用函数时会创建,函数调用结束时就会自动释放

                    3、联系

                        *执行上下文是从属于所在的作用域

                        *全局执行上下文===>全局作用域

                        *函数执行上下文===>对应的函数作用域

       5.16、作用域面试题

                  1、

10

解析:show()函数中执行fn()函数,执行fn()函数时,输出x,首先在fn()函数体中查找x,结果在函数体中未发现x,于是就会跑到上一层中寻找,fn()函数的上一层为全局作用域,在该作用域中查找到x=10,于是会输出10

                    2、


第一个正常输出,第二个报错

解析:第一个输出比较好理解,对于第二个,执行obj.fn2()时,首先会在function(){}函数中查找fn2结果为找到,然后会返回上一层作用域,也就是全局作用域中,注意一个对象不是一个作用域,有因为在全局中未找到,所以会报错。如果想要正确输出fn2,可以console.log(this.fn2)

6、闭包

     6.1、了解闭包

       1、如何产生闭包

          *当一个嵌套函数内部(子)函数引用了嵌套函数外部(父)函数的变量/函数时,就会产生闭包

       2、闭包到底是什么

           *闭包可以通过chrome调试查看 

           *理解:闭包一个类似于对象且保存在内部函数中的东西,此内部函数引用了外部函数的变量或者函数

           *注意:闭包存在于嵌套的函数内部,只要内部函数定义代码执行了(注意不是内部函数执行)就会产生闭包,内部函数定义代码执行几次就会产生几个闭包===外部函数调用几次

        3、产生闭包的条件

           *函数嵌套

           *内部函数引用了外部函数的数据(变量/函数)

           *调用了外部函数,内部函数不一定调用

      6.2、常见的闭包

         1、function fn1 ()
               {

             / /此时闭包已经产生了,函数提升,内部函数对象已经创建 

               var a =2

               function fn2 ()

               {

                a++

                 console.log(a)
                 }
                  return fn2

                   }

                   var f = fn1()

                   f()  //3

                    f() //4

         该代码产生了,一次闭包,因为外部函数只执行了一次。

          2、function showDalay(msg,time)

           setTimeout (function(){
           alter(msg)},time)  //内部函数引用了外部函数的变量

             }

           showDelay('wang',2000)

      6.3、闭包的作用

             1、使用函数内部的变量执行完后,仍然存活在内存中(延长了局部变量的生命周期)

             2、让函数外部可以操作(读写)函数内部的数据(变量或者函数)

             3、闭包的应用:定义js模块

                       *具有特定功能的js文件

                       *讲所有的数据和功能都封装到一个函数内部

                       *只向外暴露一份包含n个方法的对象或者函数

                       *模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能

                          参考:  https://www.bilibili.com/video/BV14s411E7qf?p=34

      6.4、闭包的生命周期

             1、产生:嵌套内部函数定义执行完时就产生了

             2、死亡:在嵌套的内部函数成为垃圾对象时,即fn2=null时

      6.5、闭包的缺点

             1、函数执行后,函数内的局部变量没有释放,占用内存时间变长

              2、容易造成内存泄漏,因此闭包需要及时释放

              3、内存溢出和内存泄漏

                    *内存溢出:

                          *一种程序运行出现的错误

                          *当程序运行需要的内存超过剩余的内存是,就会抛出内存溢出的错误

                    *内存泄漏

                          *占用的内存没有得到及时的释放

                          *内存泄漏积累多了就会导致内存溢出

                    *常见的内存泄漏问题

                          *意外的将函数中的局部变量设置为全局变量

                           *没有及时清理的计数器或者回调函数

                           *闭包

7、继承

       7.1、原型链继承

              1、方法

                  *定义父类型的构造函数

                  *给父类型的原型添加方法

                  *定义子类型的构造函数

                  *创建父类型的对象赋值给子类型的原型  Sub.prototype = new Supper()即子类型的prototype属性是父类型的一个实例

                  *将子类型的原型的够咱函数设置为子类型 Sub.prototype.constructor=Sub

                  *给子类型添加方法

                  *创建子类型的对象;可以调用父类型的方法

        为什么需要Sub.prototype.constructor=Sub语句?

            如果不添加该语句的话,sub.constructor = Supper。按照本来的理解,实例对象的构             造器应该为构造函数即Sub,但是该例子中却指向了 Supper,这是因为Sub.prototype = new Supper()修改了sub的prototype的指向,但是新的prototype对象(即地址0x567)没有constructor属性,于是他就会按照原型链向上找,最终Supper的实例对象的原型对象上具有constructor属性于是sub.constructor为Supper

       7.2、new一个对象所做的事

               *创建一个空的{}

                *给对象设置__proto__,值为构造函数的prototype属性  this.__proto__ = Fn.prototype

                *执行构造函数体(给对象添加方法)

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