美文网首页
js学习笔记

js学习笔记

作者: 背着梦的幸存者 | 来源:发表于2022-08-15 14:12 被阅读0次

    js代码执行分为两个阶段:预编译和代码执行

    **预编译:**

    声明提升:加var的变量以及function声明的函数都会提升,提升到代码段的最前面。

    函数内部的局部变量,提升到函数体的最前面。

    注意:1、变量的提升仅仅提升声明,函数是提升整个函数体。

    代码执行:自上而下执行

    全局变量和局部变量的分界点是函数!

    没有加var的变量都是全局变量  不管是函数里面还是函数外面

    1.形参和变量声明

    2.实参值赋给形参

    3.寻找函数声明

    4.执行函数

    **全局执行上下文:**

            当全局代码执行时,就会产生一个全局的执行上下文,EC(G);数据保存在VO中(变量对象)

    **局部执行上下文:**

            当函数代码执行时,就会产生一个局部的执行上下文,EC(Fn)。只要调用一个函数,就会产生一个局部执行上下文。调用100个函数,就会产生100个执行上下文。执行上下文里会保存一些数据

    内存分为栈堆,基本数据类型存在栈 引用数据类型存在堆,栈里存的引用类型的地址

    形参就是函数内部加var的变量

    当执行上下文出栈后就被销毁(数据存储所占用的内存空间都要被释放)

    var a=b=2--->var a=2;b=2;

    ##加var的变量和不加var的变量区别

    1.加var的变量在预编译期间会提升,不加var的变量不会提升。

    2.不管有没有var,创建的变量都会放在GO中,都可以通过window.XX 拿到变量。

    3.加var的变量可能是全局变量也可能是局部变量,不加var的只能是全局变量。

    4.加var的局部变量不会作为window的属性

    let不允许重复声明 const不允许修改

    ##作用域

    局部:函数内部 AO

    全局:函数外部

    - GO 全局对象

    - ECS 执行上下文栈 先进后出(杯子)

    - ES(G)全局执行上下文,当执行全局代码时就会产生全局执行上下文(鸡蛋,放入杯子中)

    - EC(fn)当函数代码执行时,就会产生一个局部的执行上下文

    如果全局函数在if中,仅仅提升声明,不提升赋值。

    如果条件成立,进来的第一件事就是赋值

    ```

    //预编译:提升

    //首先var fn提升了 fn的值是undefined

    //下面的fn函数整体(函数名+函数体)也要提升

    //如果提升过了,就不在提升,函数名不在提升

    //函数体提升 fn的值由undefined变成函数体

    console.log(fn); //打印函数

    var fn=100;

    function fn(){

      console.log("fn...")

    }

    ```

    函数没有返回值,默认返回undefined

    形成闭包的条件:是一个不会释放栈内存空间的EC(局部的执行上下文)

    闭包指的是栈空间

    当内部函数被返回到外部并保存时,一定会产生闭包,闭包会产生原来的作用域链不释放,过度的闭包可能会导致内存泄漏或加载过慢.

    在一个函数里面嵌套一个函数,并且里面的函数引用了外部函数的变量,这样就形成了闭包

    闭包的前提是函数嵌套

    一个不会释放内存的栈空间就是一个闭包

    闭包好处

    1.保存变量的值(延长一个函数内部变量的生命周期)

    2.由于x是函数内部的变量,外部不能访问。对x有保护作用

    ```

    // 闭包实现累加器

    function test(){

        var num=0;

        function add(){

            console.log(++num);

        }

        return add;

    }

    var add=test();

    add();  //1

    add();  //2

    //闭包实现缓存机制

    function myClass(){

        var students=[];

        var operations={

            join:function(name){

                students.push(name);

                console.log(students);

            },

            leave:function(name){

                var idx=students.indexOf(name);

                if(idx>-1){

                    students.splice(idx,1);

                }

                console.log(students);

            }

        };

        return operations;

    }

    var obj=myClass();

    obj.join('1111');

    obj.join('2222');

    obj.leave('1111');

    ```

    作用域链:数据的查找机制

    IIFE:

    立即调用函数表达式

    (function(){})();

    (function(){}());

    ```

    var f=function(){};//普通函数表达式

    function g(){};//函数声明、定义

    ```

    一定是表达式才能被执行符号执行

    var num=(1,2);返回2 ()里面有, 只返回,后面的值

    (function b(){}) 带()代表是表达式,忽略函数名字(b)

    this:

    1.如果this出现在普通函数中,通过window.f调用函数,那么this就表示window,主要是看.前面是谁

    2.事件绑定中,监听器中的this表示事件源

    3.对象是属性的无序集合,在一个对象中也可以有函数,如果一个函数出现在了对象中(方法),那么this表示这个对象

    4.在IIFE中,this表示window

    5.只有代码执行时才知道this指向

    in:

    也是一个运算符,判断一个属性是否属于某个对象,不管是共有属性还是私有属性

    new的原理

    1.内部创建一个空对象

    2.内部的this指向这个空对象

    3.返回这个空对象

    原型和原型链

    1.每一个对象中必定有一个属性叫__proto__,指向构造器的原型函数

    2.每一个构造器上,都有一个属性叫prototype,也是指向原型对象上,原型对象上通常会放有公共属性

    3.每一个原型对象上,都有一个constructor属性,指向他所对应的构造器

    4.当我们在查找一个对象上的属性时,先在自己的私有属性中找,如果找不到,就沿着__proto__去原型对象中找,如果原型对象中找不到,就再沿着__proto__去它的原型对象的原型对象中找,直到null。如果找不到就报错undefined。(原型链)

    当我们修改了原型对象,需要手动修改constructor的指向

    不是所有的对象都继承于object.prototype    Object.create(null)

    原型链的顶端是object.prototype

    ==会自动调用toString方法

    .的优先级高于new

    call:改变函数中this的指向

    方法.call(obj)  1.让this指向obj 2.让方法执行

    apply:和call的作用是一摸一样的,只是穿参的形式不一样

    当函数的参数大于3个时,使用call性能更好

    callee在哪个函数里面值得是哪个函数本身,通过arguments.callee调用函数自身

    caller返回当前被调用函数的函数引用

    ```

      function say(a,b){

          console.log(a,b)

      };

      let obj1={name:"aaa"};

      let obj2={name:"bbb"};

      say.call(obj1,1,2);

      say.apply(obj2,[2,3]);

    ```

    typeof:

    object/string/number/boolean/function/undefined

    优点:对基本数据类型和函数判断很准确;是运算符 简单

    缺点:对引用数据类型判断不准确,结果全是object

    使用call优化

    ```

    function type(data){

      return Object.prototype.toString.call(data)

    }

    ```

    继承:

    原型继承 :

    1.继承父类的共有属性和私有属性作为子类的共有属性;

    2.核心代码:Son.prototype=new Father(); Son.prototype.constructor=Son;

    call继承:

    1.继承父类的私有属性作为子类的私有属性

    2.function Son(){

    Father.call(this)

    }

    组合继承:call+原型

    1.继承父类的共有和私有属性,作为子类的共有属性  原型

    2.继承父类的私有属性,作为子类的私有属性  call

    3.父类的私有属性被继承了两次,当我们去访问一个属性时,会先访问私有属性

    只继承父类的共有属性,作为子类的共有属性

    var Fn=function(){}; //空函数 中介作用

    fn.prototype=Father.prototype; //改变Fn的原型对象,和Father共享一个原型对象

    Son.prototype=new Fn();

    ```

    //圣杯模式继承

    function Teacher(){

        this.name='Mr.Li';

        this.tSkill='JAVA';

    }

    Teacher.prototype={

        pSkill:'JS'

    }

    var t=new Teacher();

    console.log(t);

    function Student(){

        this.name='Mr.Wang';

    }

    //中间缓冲构造函数,作用是原型继承的时候与原本的原型隔离开

    // function Buffer(){}

    // Buffer.prototype=Teacher.prototype;

    // var buffer=new Buffer();

    // Student.prototype=buffer;

    // //此处只改变student的原型,不影响teacher的原型

    // Student.prototype.age=18;

    inherit(Student,Teacher);

    var s=new Student();

    console.log(s);

    //函数封装

    function inherit(Target,Origin){

        function Buffer(){};

        Buffer.prototype=Origin.prototype;

        Target.prototype=new Buffer();

        Target.prototype.constructor=Target;

        Target.prototype.super_class=Origin;

    }

    ```

    类数组

    把{}变成[] 需要在对象中加入splice,并且继承数组原型的splice方法,还要有length属性

    Object.prototype.toString.call('') 增强版typeof

    ```

    //类数组面试题以及原理解析

    var obj={

        '2':3,

        '3':4,

        'length':2,

        'splice':Array.prototype.splice,

        'push':Array.prototype.push

    }

    obj.push(1);

    obj.push(2);

    console.log(obj);

    // {

    //    '2': 1,

    //    '3': 2,

    //    length: 4

    // }

    //push原理解析

    // Array.prototype.push=function(elem) {

    //    this[this.length]=elem;

    //    this.length++;

    // }

    ```

    Array.from:把伪数组变成真实数组

    数组的下标 从前面开始数是从0开始的 从后面开始是从-1开始的

    可迭代对象: 可以使用for of 进行遍历

    数组 字符串 Set Map

    不可迭代:

    object

    push/unshift(最后/最前面 添加元素) 返回值,是执行了方法以后的数组长度 修改原数组

    pop/shift(删除最后/最前面一位元素,没有参数)返回值 删除剪切的元素 修改原数组

    reverse (倒序) 返回值,改变后的数组 修改原数组

    splice(开始项的下标,剪切长度,剪切以后的最后一位添加的元素) 返回值 剪切的元素 修改原数组

    sort(按照ascii码排序)返回值 排序以后的结果 修改原数组

    concat(合并数组) 返回值改变后的数组 新建数组,不影响原数组(新建)

    slice(截取起始下标,截取结束下标之前一位)返回值是截取的数组,不影响原数组(新建)

    join(数组转成字符串)返回值是转换之后的字符串,不影响原数组(新建)

    split(按什么分隔的,分隔的长度) 返回值转换之后的数组,不影响原数组(新建)

    ```

    //1.参数a,b

    //2.返回值:正值,b排前面

    //              负值,a排前面

    //                  0,保持不动

    var arr=[27,39,5,7];

    arr.sort(function(a,b) {

        //return a>b?1:-1

         return a-b //升序 b-a降序

    })

    console.log(arr);

    //随机排序

    //Math.random()->0-1之间的数,不包含0和1 开区间

    var arr=[1,2,3,4,5,6,7,8];

    arr.sort(function(a,b){

       //var rand=Math.random();

        //return rand-0.5>0?1:-1;

          return Math.random()-0.5;

    })

    console.log(arr);

    ```

    find方法:

        在一个数组中,找出第一个符合条件的元素。

        返回值:返回找到的元素  如果说没有找到,就返回und

        let arr = [1,2,3,4,5]

        let res = arr.find(item=>{  //在arr中,找出大于2的元素

            return item>100

        })

        console.log(res)

    --------------------------------------------------------

    findIndex方法:

        在一个数组中,找出第一个符合条件的元素的索引。

        返回值:返回找到的元素的索引  如果说没有找到,就返回-1

        let arr = [1,2,3,4,5]

        let res = arr.find(item=>{  //在arr中,找出大于2的元素

            return item>100

        })

        console.log(res)

    --------------------------------------------------------

    includes方法:

        判断一个数组中是否包含某个值,如果包含,返回true。

    --------------------------------------------------------

    forEach方法:

        遍历数组    声明式编程

    --------------------------------------------------------

    map方法:

        对数组中的每一项元素进行加工,返回一个加工后的新数组。

        let arr = [1,2,3,4,5]

        let res = arr.map(item=>{

            return item*100

        })

        console.log(res)

    --------------------------------------------------------

    filter方法:

        filter本意是过滤的意思。过滤出满足我们条件的元素,形成一个新的数据。

    --------------------------------------------------------

    some方法:

        遍历数组中每一项,有一项返回true,那么就停止遍历,整体的结果就是ture

    --------------------------------------------------------

    every方法:

        遍历数组中每一项,所有项返回true,整体结果才为true

    每个函数作用域链上都有GO,函数自己的AO排在第一位,GO排第二位

    var i=1;

    var a=i++; //a=5  解析:var a=i;i=i+1;

    var b=++i; //b=2  解析:i=i+1;var b=i;

    window.a||(window.a='1');console.log(a);

    //结果是a 有()先看(),a=1,然后再去看或者window.a

    1.形参和变量声明

    2.实参值赋给形参

    3.寻找函数声明

    4.执行函数

    undefined=>false

    数组截断方法

    ```

    var arr=[1,2,3,4,5];

    arr.length=3;

    console.log(arr);// [1,2,3]

    ```

    原始值没有属性和方法

    ```

    ;(function(){

        var Compute=function(opt){

            this.x=opt.firstNum || 0;

            this.y=opt.secondNum || 0;

        }

        Compute.prototype={

            plus:function(){

                return this.x+this.y;

            },

            minus:function(){

                return this.x-this.y;

            },

            mul:function(){

                return this.x*this.y;

            },

            div:function(){

                return this.x/this.y;

            }

        }

        window.Compute=Compute;

    })();

    var compute=new Compute({

        firstNum:1,

        secondNum:2

    });

    var res=compute.plus();

    ```

    ```

    function Car(){};

    var car=new Car();

    function Person(){};

    var p=new Person();

    console.log(car instanceof Car);//true

    console.log(car instanceof Object);//true

    console.log([] instanceof Array);//true

    console.log([] instanceof Object);//true

    console.log({} instanceof Object);//true

    ```

    ```

    window.onload=function(){

        init();

    }

    function init(){

        console.log(initFb(10));

        console.log(initDiv(100));

    }

    var initFb=(function(){

        function fb(n){

            if(n<=0){

                return 0;

            }

            if(n<=2){

                return 1;

            }

            return fb(n-1)+fb(n-2);

        }

        return fb;

    })();

    var initDiv=(function(){

        function div(n){

            var arr=[];

            for (let i = 0; i < n; i++) {

                if(i%3==0||i%5==0||i%7==0){

                    arr.push(i);

                }

            }

            return arr;

        }

        return div;

    })();

    ```

    ```

    function deepClone(origin,target){

        var target=target||{},

            toStr=Object.prototype.toString,

            arrType='[object Array]';

        for (const key in origin) {

            if (origin.hasOwnProperty(key)) {

                if(typeof(origin[key])==='object'&&origin[key]!==null){

                    if(toStr.call(origin[key])===arrType){

                        target[key]=[];

                    }else{

                        target[key]={};

                    }

                    deepClone(origin[key],target[key]);

                }else{

                    target[key]=origin[key];

                }

            }

        }

        return target;

    }

    ```

    ```

    var name ='222';

    var a={

        name:'111',

        say:function(){

            console.log(this.name);

        }

    }

    var fun=a.say;

    //var fun=function(){

    //    console.log(this.name);

    // }

    fun();//this指向window 打印222

    a.say();//this指向a 打印111

    var b={

        name:'333',

        say:function(fun){

            fun();

            //此处是fun函数调用,fun函数是全局的,所以this还是指向window->222

            //+function(){

            //    console.log(this.name);

            // }()

        }

    }

    b.say(a.say);//222

    b.say=a.say;

    b.say(); //333

    ```

    ```

    function Foo(){

        getName=function(){

            console.log(1);

        }

        return this;

    }

    //相当于给函数对象增加属性

    Foo.getName=function(){

        console.log(2);

    }

    Foo.prototype.getName=function(){

        console.log(3);

    }

    var getName=function(){

        console.log(4);

    }

    //预编译的时候会变量提升

    function getName(){

        console.log(5);

    }

    Foo.getName(); //2

    getName();//4

    Foo().getName();//1

    getName();//1

    new Foo.getName();//.的优先级高于new 相当于Foo.getName() new2没有意义 还是2

    new Foo().getName();//()的优先级比.大 实例化了函数,函数本身没有getName属性,故去找原型 3

    new new Foo().getName(); //同上 new3没有意义 还是3

    ```

    相关文章

      网友评论

          本文标题:js学习笔记

          本文链接:https://www.haomeiwen.com/subject/agodoltx.html