js-函数及面向对象

一.数据类型的操作原理

  • 数据类型

    • 基本数据类型(值类型)

      • number
      • string
      • boolean
      • null
      • undefined
    • 引用数据类型

      • 对象

        • {} 普通对象
        • /^$/正则
        • 一般类的实例都是对象数据类型的
        • 函数的prototype属性
        • 实例的proto属性
      • 函数

        • function 普通函数
        • 。。。

      console.log(typeof Object);//function

    • 值类型

      直接按值操作,例如:var a=12;直接把12这个赋值给变量a(让a变量和12这个值建立了链接关系)

    • 对象数据类型

      在js中遇到对象,会严格按照如下的步骤操作

      1.浏览器为其开辟一个新的内存空间,为了方便后期可以找到这个空间,浏览器给空间分配一个16进 制的地址

      2.按照一定顺序,分别的把对象键值对存储到内存空间中

      3.把开辟内存的地址赋值给变量(或者其他的东西),以后变量就可以通过地址找到内存空间,然后进行一些操作

    • 分析例子

    js代码运行在浏览器中,是因为浏览器给我们提供了一个供js代码执行的环境->全局作用域(window/global)

    var a=12;
    var b=a;
    b=13;
    console.log(a);
    
    var o={name:"珠峰培训"};
    var p=o;
    p.name="周啸天"
    console.log(o.name);
    
    var m={name:"珠峰培训"};
    var n=m;
    n={name:"中国最权威的前端培训机构"};
    console.log(m.name);
    
    
    image.png
  • 函数的操作

    • 创建函数

      1.先开辟一个新的内存空间(为其分配了一个16进制的地址)

      2.把函数体中编写的js代码当做字符串存储到空间中(函数只创建不执行没有意义)

      3.把分配的地址赋值给声明的函数名(function fn和var fn操作原理其实相同,都是在当前作用域声明了一个名字)

    • 执行函数

      目的:执行函数体中的代码

      1.函数执行的时候,浏览器会形成一个新的私有作用域(只能执行函数体 中的代码)供函数体中的代 码执行

      2.执行代码之前,先把创建函数存储的那些字符串变为真正的js表达式, 按照从上到下的顺序在私有作用域中执行,一函数可以被执行N次,
      每一次的执行相互之间互不干扰

      形成的私有作用域把函数体中的私有变量都包裹起来了(保护起来了),在私有作用域中操作私有 变量和外界没关系,外界也无法直接的操作私有变量

      3.函数执行具体步骤

      • 形参赋值
      • 变量提升
      • 代码自上而下执行
      • 当前栈内存(私有作用域)销毁 (也有例外:不销毁的时候,往下看闭包)

      案例分析

      var a=12;
      var b=a;
      b=13;
      console.log(a);
      
      var o={name:"珠峰培训"};
      var p=o;
      p.name="周啸天"
      console.log(o.name);
      
      var m={name:"珠峰培训"};
      var n=m;
      n={name:"中国最权威的前端培训机构"};
      console.log(m.name);
      
      function fn(){
       var ary=Array.prototype.slice.call(arguments);
       return eval(ary.join('+'));
      }
      fn(12);
      fn(13);
      
      image.png

二.堆栈内存

  • 栈内存

    俗称叫做作用域(全局作用域/私有作用域)

    • 为js代码提供执行环境(执行js代码的地方)
    • 基本数据类型值是直接存放在栈内存中的
  • 堆内存

    存储引用数据类型值的(相当于一个存储的仓库)

    • 对象存储的是键值对
    • 函数存储的是代码字符串
  • 内存释放

    在项目中,我们使用的内存越少性能越好,我们需要把一些没用的内存处理掉

    • 堆内存

      var o={};当前对象对应的堆内存被变量o占用着呢,堆内存是无法销毁的

      o=null;null空对象指针(不指向任何的堆内存),此时上一次的堆内存就没有被占用了,谷歌浏览器会在空闲时间把没有被占用的堆内存自动释放(销毁/回收)

    • 栈内存

      一般情况下,函数执行形成的栈内存,函数执行完,浏览器会把形成的栈内存自动释放;

      有的时候执行完成,栈内存不能被释放?

      全局作用域在加载页面的时候执行,在关掉页面的时候销毁

三.变量提升

  • 定义及案例分析

    • 定义

    当前作用域中,js代码自上而下执行之前,浏览器首先会把所有带var/funciton关键字的进行提前的声明或者定义,

    也就是该变量不管是在当前作用域的哪个地方声明的,都会提升到所属作用域的最顶上去

    声明(declare):var num ;在当前作用域中吼一嗓子我有num这个名了

    定义(defined): num=12;把声明的名字赋一个值

    带var关键字的只是提前的声明一下

    带funciton关键字的在变量提升阶段把声明和定义都完成了

    同一个变量只会声明一次,其他的会被忽略掉

    函数声明的优先级高于变量申明的优先级

    • 分析这一段代码
    console.log(num);
    console.log(fn);
    var num=13;
    function fn(){
     console.log(a);
     var a=10;
     console.log(a);
    }
    fn();
    console.log(num);
    
    image.png
  • 不管条件是否成立都要进行变量提升

    不管条件是否成立,判断体中出现的var/function,都会进行变量提升;

    但是在最新版浏览器中,function声明的变量只能提前声明不能定义了(前提:函数是在判断体中)

    console.log(num);//undefined
    console.log(fn);//undefined
    if(1==1){
     console.log(num);//undefined
     console.log(fn);//fn函数本身
     var num =12;
     function fn(){
    
     }
    }
    
    console.log(fn);//fn函数本身
    

    代码执行到条件判断的地方

    • 条件不成立

      进入不到判断体中,赋值的代码执行不了,此时之前的声明的变量或者函数以然是undefined

    • 条件成立

      进入到条件判断体中的第一件事情不是代码执行,而是把之前的变量提升没有定义的函数首先定义了(进入到判断体中的函数就有定义了)

      老版本浏览器不是这样处理的:不管条件是否成立,function都要进行变量提升(声明+定义)

      新版浏览器function只是声明

  • 关于重名的处理

            //重名的只会声明一次,函数在提升阶段就可以定义了就是赋值,可以多次赋值    
       //=>变量提升:fn=aaafff111 (=aaafff222) (=aaafff333) (=aaafff444)
       fn();//4
       function fn(){console.log(1);}
       fn();//4
       function fn(){console.log(2);}
       fn();//4
       var fn=13;   
       fn();//Uncaught TypeError: fn is not a function
    
       function fn(){console.log(3);}
       fn();
       function fn(){console.log(4);}
    
  • 变量提升,函数优先

    //函数声明和变量声明都会被提升,但是需要注意的是函数会先被提升,然后才是变量
    var fn1; //只定义没有赋值,意味着原有栈中fn1的结果没有改变
    function fn1(){
    
    }
    console.log(typeof fn1);//function
    理由:var fn1;尽管出现在function fn1()之前,但它是重复的声明,会被忽略,因为函数声明会被提升到普通变量之前
    
    (
     其实也可以从另外一个角度去理解,提升变量声明fn1,
     但是函数声明和定义都被提升上去的,最总fn1赋值是函数体
     )
    
    
    
    var fn1=4;//定义赋值,覆盖函数
    function fn1(){
    
    }
    console.log(typeof fn1);//number
    

四.作用域/作用域链

  • 定义

    • 作用域
      【栈内存】

      • 全局作用域:window
      • 私有作用域:函数的执行
      • 块级作用域:使用let创建变量存在块级作用域
    • 作用域链

      当前作用域代码执行的时候遇到一个变量,首先看一下它是否属于私有变量,

      如果不是私有变量,向其上级作用域查找,也不是上级作用域私有变量,继续向上查找,

      一直到window全局作用域为止,

      我们把这种向上一级级查找机制叫做作用域链

  • 查找私有变量

    js中的私有变量有且只有两种

    • 在私有作用域变量提升阶段,声明过的变量(或者函数)

    • 形参也是私有变量

  • 案例分析

    • 案例1
    image.png
    • 案例2
    function fn(b){
      //=>私有作用域
      //=>形参赋值:b=1(私有变量)
      //=>变量提升:b=aaaff111 (此处赋值操作替换了形参赋值的内容)
      console.log(b);//=>函数
      function b(){
        //=>私有作用域
        //=>形参赋值和变量提升都没有
        console.log(b);//=>函数,不是私有变量,去上一级作用域查找,
      }
      b();
    }
    
    fn(1);
    
  • 如何查找上级作用域

    函数执行形成的一个私有的作用域(A),A的上级作用域是谁,和它在哪执行的没有关系,主要看他在哪定义的,在哪个作用域下定义的,当前A的上级作用域就是谁;

    • 例1
    var n=10;
    function sum(){
     console.log(n);//10
    }
    
    (function(){
     var n=100;
     sum();
    })();
    
    • 例2
    var n=10;
    var obj={
     n:20,
     fn:(function(){
       var n=30;
       return function(){
         console.log(n);//30
       }
     })(),
    };
    obj.fn();
    
    image.png
    • 例3
    var n=10;
    var obj={
     n:20,
     fn:(function(){
       //上级作用域:全局作用域
       return function(){
         //上级作用域:自执行函数
         console.log(n);//10
       }
     })(),
    };
    obj.fn();
    
    • 例4
    var n=10;
    var obj={
     n:20,
     fn:(function(n){
         return function(){
             console.log(n);//10
       }
     })(obj.n) //Uncaught TypeError: Cannot read property 'n' of undefined
     //因为这时候obj还没有呢
    
    };
    obj.fn();
    

五.闭包

  • 闭包作用

    • 保护作用

      形成私有作用域,保护里面的私有变量不受外界的干扰

      例如jqery代码

      (function(){
       var jQuery=function(){
         。。。
       }
       window.jQuery=window.$=jQuery;
      })();
      
      jQuery();
      
    • 保存作用

      函数执行形成一个私有作用域,函数执行完成,形成的这个栈内存一般情况下都会自动释放

      但是还有二般情况:函数执行完成,当前私有作用域(栈内存)中的某一部分内容被栈内存以外的其他东西占用了,当前的栈内存就不能释放掉,也就形成了不销毁的私有作用域(里面得到私有变量也不会销毁)

      【形式】

      ​ 函数执行返回了一个引用数据类型堆内存的地址,在外面有一个变量接收了这个返回值,此时 当前作用域就不能销毁,(想要销毁,只需要让外面的变量赋值为null,也就是不占用当前 作用域的内容了)

       function fn(){
              var i=1;
              return function(n){
                  console.log(n+ i++);
              }
          }
        var f=fn(); //只要f不销毁,fn函数形成的私有作用域不会销毁
        f(10);//11
        fn()(10);//11 //这种执行完,fn函数形成的私有作用域就销毁了
        f(20);//22
        fn()(20);//21
      
      image.png

六.原型

  • 1.原理

    • 所有的函数都天生自带一个属性:prototype(原型),它是一个对象数据类型的值,在当前prototype对象中,存储了类需要给其实例使用的公有的属性和方法

    • prototype这个对象,浏览器会默认为其开一个堆内存,这个堆内存中天生自带一个属性:constructor(构造函数),这个属性存储的的值就是当前函数本身

    • 每一个类的实例(每一个对象)都天生自带一个属性:proto,属性值是当前对象所属类的原型(prototype)

    分析个例子

    function Fn(name,age){
     //这里面的属性都是私有的
      this.name=name;
      this.age=age;
      this.say=function(){
          console.log(this.name);
      }
    }
    
    //原型上的都是公有的
    Fn.prototype.say=function(){
      console.log("hello world");
    }
    Fn.prototype.eat=function(){
      console.log("I like food");
    }
    
    var f1=new Fn("王燕燕",19);
    var f2=new Fn("王雪超",69);
    
    f1.eat();
    
    image.png
  • 2.内置类的原型分析

    image.png
    /*
    判断是否是对象的对象属性
    hasOwnProperty
    
    私有和公有是一个相对论,我们需要看相对于哪个对象而言:
    1.相对于实例ary1来说push是公有的
    2.相对于Array.prototype来说,push就是自己的私有的
    3.凡是通过__proto__找到的属性都是公有的,反之都是私有的
    */ 
    var ary1=[12,23];
    console.log(ary1.hasOwnProperty("push"));//false
    console.log(Array.prototype.hasOwnProperty("push"));
    
  • 3重新构造原型

    function Fn(name){
     this.name=name;
     this.say=function(){
       console.log(this.name);
     }
    }
    
    Fn.prototype={
            /*让原型指向自己开辟的堆内存有一个问题:自己开辟的堆内存中没有constructor这个属性,
           所以实例在调取constructor的时候找到的是Object,这样不好,
           此时我们应该重新设置一下constructor,保证机制的完整性
           */
           constructor:Fn,
           aa:function(){},
           bb:function(){}
    }
     var f =new Fn("wanglei");
     console.log(f.constructor)//Fn函数
    

7.继承

  • 原型继承

    让父类中的属性和方法在子类实例的原型链上

    • 特点

      • 它是把父类的原型放到子类实例的原型链上,实例想调取这些方法,是基于__proto__ 原型链查找机制完成的

      • 子类可以重写父类上的方法(这样会导致父类其它的实例也会收到影响)

      • 父类中私有或者公有的属性方法,最后都会变为子类中公有的属性和方法

    • 代码分析

     function A(x){
                this.x=x;
            }
      A.prototype.getX=function(){
        console.log(this.x);
      }
    
      function B(y){
        this.y=y;
      }
    
       //这2行代码就实现了原型继承
      B.prototype =new A(200);
      B.prototype.constructor=B;
    
      B.prototype.getY=function(){
        console.log(this.y);
      }
    
      var b=new B(100);
      console.log(b);
    
    image.png
  • call继承

    • 特点

      • child方法中把parent当做普通函数执行,让parent中的this指向child的实例,相当于给child的实例设置了很多私有的属性或者方法

      • 只能继承父类私有的属性或者方法(因为是把parent当做普通函数执行,和其原型上的属性和方法没有关系

      • 父类私有的变为子类私有的

    • 代码

     function A(x){
                this.x=x;
            }
      A.prototype.getX=function(){
        console.log(this.x);
      }
    
      function B(y){
        //这行代码实现了call继承
        A.call(this,200);//=>b.x=200
       
        this.y=y;
      }
      B.prototype.getY=function(){
        console.log(this.y);
      }
    
      var b=new B(100);
      console.log(b);
    
  • 寄生组合继承:call继承+类似原型继承 (推荐)

    • 特点
      父类私有和公有的分别是子类实例的私有和公有属性方法

    • 代码分析

    function A(){
        this.x=10;
    }
    A.prototype.getX=function(){
        console.log(this.x);
    }
    
    function B(){
        //这行代码继承父类私有属性
        A.call(this);
        this.y=10;
    }
    
    //这2行代码继承父类原型上的属性(公有属性)
    B.prototype = Object.create(A.prototype);
    B.prototype.constructor=B;
    
    B.prototype.getY=function(){
        console.log(this.y);
    }
    
    var b=new B();
    console.log(b);
    
    image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容