• JavaScript高级


    作者声明:本博客中所写的文章,都是博主自学过程的笔记,参考了很多的学习资料,学习资料和笔记会注明出处,所有的内容都以交流学习为主。有不正确的地方,欢迎批评指正

    本节课学习视频来源:https://www.bilibili.com/video/av26084949

    JavaScript高级

     课程内容介绍

    1.1值类型与引用类型及参数传递

    值类型与引用类型

    变量的内存的存储结构

     

     

     

    值类型和引用类型赋值与传递

    示例代码

    <script>
        
        var a = 9,b;
        var c = { age:9 },d;
    
        // 把a值类型复制给了b
        b = a;
        b = 19;//修改了b的值
        console.log( a );   //a => 9
        console.log( b );   //b => 19
    
        //如果是引用类型的赋值后操作
        d = c;  //把c引用类型赋值给了d
        d.age = 22;//留给了d变量的age属性为22
        
        //那么 c的age属性是多少呢?
        console.log( c.age );   //c.age => 22
        console.log( d.age );   //d.age => 22
        
        </script>

    示意图: 

     函数参数的值传递和引用传递

    示例代码

    <script>
        var a = 9;      //  简单类型,值类型
        var b = { 
            name:"laoma",
            age:18
         };             //引用类型
        //把  a 和 b 分别传给 c1 c2
        //函数参数:
        //如果是简单类型,会作为一个值类型的数值副本传到函数内部
        //如果是一个参数是引用类型,会将引用类型的地址值赋值给传入函数的参数
        function demo( c1,c2 ){
            c1 = 29;    //对c1做了更改
            c2.name = "6666";   //更改了c2引用类型的属性name
        }
        //  调用函数执行
        demo( a,b );
    
        console.log( a );   //a => 9
        console.log( b );   //b.name => "6666"
        
        </script>

     

     示例代码

    <script>
        function max( a,b ){
            console.log( arguments );
            //每个函数内部都可以直接访问arguments,里面就是存放着我们传递给函数的参数
            //arguments不是一个数组  但是跟数组类似
            //arguments有一个length属性,属性值就是传递 实参 数的个数
            for( var i=0;i < arguments.length;i++ ){
                console.log( arguments[i] );    //打印所有的参数
            }
            if( a > b ){
                return a;
            }else{
                return b;
            }
        }
        console.log( max.length );  //函数的length属性是指 形参 的个数
    
        var t = max( 9,10 );
        console.log( t );
    
        var m = max( 9,10,20,30 );
        
        </script>

     示例代码:

     //实现max方法,可以接受任意多个参数,返回这些参数中最大的那个值
        function myMax(){
            // 如果调用函数的方法没有传递参数,那么直接返回NaN
            if( arguments.length <= 0 ){
                return NaN;
            }
            
            var max = arguments[0];
            for( var i = 0;i < arguments.length;i++ ){
                if( arguments[i] > max ){
                    max = arguments[i];
                }
    
            }
            return max;
        }
    
        var m = myMax( 10,9,2,33,22,18 );
        console.log( m );   // m => 33

    示例代码:

    <script>
        //函数参数的封装
        //一个函数:封装一个矩形
        //矩形:x y坐标   width  height 背景色 文字信息  文字坐标 文字字体 颜色
        //全部用新参来处理矩形的参数
        function rect( x,y,width,height,bgColor,text,text_color,text_x,text_y ){
    
        }
        // 如果函数的形参非常多  会有如下问题
        // 1 开发人员很难记忆形参的具体参数
        // 2 传递参数的时候,如果顺序不小心写错了,那么会导致函数内部出现错误
        // 3 编写 代码不方便
    
    
        //解决办法:把这些参数封装成一个对象进行传递
    
    
        function rect2( obj ){
            //拿到矩形的 x  y 坐标
            console.log( obj.x + " " + obj.y );
        }
    
        //rectObj
        var rectObj = {
            x:19,
            y:20,
            200,
            height:300,
            bgColor:"#ccc",
            text:"laoma"
        }
    
        rect2( rectObj );
    
        </script>

     1.2函数高级内幕

     

     JavaScript时间循环机制

     

     

     执行上下文相关的概念

     执行上下文的执行栈

     示例代码

     <script>
            //代码执行之前的时候,就会立即创建一个全局的执行上下文 Global Excution Context
            //创建完了全局的执行上下文之后,把全局执行上下文压如  执行环境栈中
            function f1(){
                console.log( "f1" );
            }
    
            function f2(){
                console.log( "f2" );
                f3();
            }
            function f3(){
                console.log( "f3" );
                f4();
            }
            function f4(){
                console.log( "f4" );
            }
            
            
            f1();
            f2();
    
            </script>

    分析

     示意图

     

     执行上下文的声明周期

     

     示例代码:

     <script>
        //变量声明
        var a1 = 19,
            a2 = 20,
            a3 = "sss",
            b1 = { name:'laoma' };
        //函数调用
            a1 = f1( a1,a2 );
        //函数声明 
            function f1( a,b ){
            //f1函数的执行上下文
            //第一步:扫描参数  a = 19  b = 20
            //第二步:扫描函数声明   f2 = function(){}  
            //第三步:扫描变量声明   t = undefined  m = undefined   i = undefined
                var t = 0,
                    m = 10;
                for( var i = 0;i < a;i++ ){
                    console.log( i );
                }
    
                function f2(){
                    console.log( f2 );
                }
                return a + b;
            }
        
        </script>

    示意图

     JavaScript的解释和执行阶段

     函数变量的作用域

     示例代码

    <script>
    
            var t = 9;          //全局作用域,全部都可以以访问   
            function f1() {      //f1函数 全局作用域
                var t2 = 10;    //t2 是f1函数内部可访问
                console.log(t);
    
                function f2() {  //f2函数式 f1函数的做哟用于
                    var t3 = 200;   //t3 只能在f2函数内部访问
                    console.log(t2);
    
                    return t2 * t2;//f2函数可以访问f1函数的作用域的变化量  及 f2 自己内部的变量
                }
                return f2();
            }
    
            var m = f1();
            console.log(m);
    
        </script>

    示意图

     没有块级作用域

     示例代码

    <script>
        //没有块级作用域
        function f1(){
            var t1 = 9; //只能在f1函数内部访问到
    
            for( var i = 0;i < 10;i++ ){//i 变量在 for  循环中定义中的
                console.log( i );       
            }
            console.log( i );    //因为js没哟块级作用域  所以可以直接访问i变量
        }
    
        f1();
    
        console.log( t1 );//全局是访问不到f1函数内部的变量的
        
        </script>

    变量提升

     

    示例代码:

    <script>
        var a = 10;     //全局变量,全局都可以访问
    
        //先执行f1函数,然后再定义f1函数的内容
        f1();
    
        function f1(){
            //函数的变量提升:因为在函数执行之前,先创建了函数的EC,在创建EC的时候
            //已经把函数里面成名的变量都已经初始化成了undefined
    
            //所以:hositing存在
            //很多人有这样的习惯,把函数内部所有的变量声明都放在函数的头部
            console.log( a );   //a => undefined
    
            var a = 19;         //给局部变量a赋值19
    
            console.log( a );   //a => 19
    
    
            //特殊情况:变量声明和函数声明 同时拥有一个名字的时候,函数优先级高
            console.log( b );   // b => function
    
            var b = 9;          //因为js是动态语言,把b重新赋值9,把之前的function覆盖
            function b(){
    
            }
    
            console.log( b );   // b => 9
    
    
        }
    
        console.log( a );       //a => 10
        
        </script>

     window

    示例代码

     <script>
        //在浏览器中 全局对象就是window
        //在全局作用域中声明 的变量和函数声明都会作为window的属性存在
    
        var a = 9;
        function b(){
            console.log( a );
        }
    
        console.log( "a" in window );
        console.log( window.a );
        console.log( window["a"] );
        console.log( window.b() );
        console.log( window.b );
        
        </script>

     变量提升案例

     示例代码案例1

    <script>
        //案例1
        var a = 18;     //全局变量
        function d(){
            console.log( a );
            var a = { age:19 };
    
            console.log( a );
        }
        d();        //输出undefined   输出对象
        console.log( a );   //a => 18
        </script>

     示例代码案例2

     <script>
        
        //案例2
        //比较
        if( !("a" in window) ){ //"a" in window => true
            var a = 1;
        }
        console.log( a );//a => undefined
        //比较
        if( !!("a" in window) ){
            var a = 1;
        }
        console.log( a );//a => 1
    
    
        </script>

     示例代码案例3

     <script>
        //案例3
        console.log( a );//a => function
    
        var a = 20;     //对a进行重新赋值
        console.log( a );//a => 20
    
        function a(){   //创建 GEC 函数优先级高于变量优先级
    
        }
        </script>

     示例代码案例4

      <script>
        
        f();
        console.log(a);     //a未声明  异常
        console.log(b);     //b => 9
        console.log(c);     //c => 9
    
        function f(){
            var a = b = c = 9;  //a 局部  bc 全局
        //var a = 9,b = 9 ,c =9;//定义局部变量
            console.log( a );   //a => 9
            console.log( b );   //b => 9
            console.log( c );   //c => 9
        }
      
        </script>

     示例代码案例5

    <script>
            //案例 5
            f();
            function f(){
                for( var k = 0;k < 10;k++  ){
                    console.log( k );//0-9
                }
                console.log( k );    //10  js没有块级作用域
            }
            //结果  0-10
        </script>

     作用域链

     

    示例代码

     

     函数四种调用模式与this

     示例代码 方法调用模式

     <script>
        //定义构造函数
        //定义一个Dog类的构造函数
    
        function Dog( dogName ){
            //如果函数当做构造函数来用
            //第一步:创建一个空对象(新对象),函数上下文===this
            //第二步:把空对象赋值给函数上下文,this = 新对象
    
            this.name = dogName;
            this.age = 0;
    
            this.run = function(){
                console.log( this.name + " is running..." );
            };
            //如果函数当做构造函数调用,并没有返回任何数据的时候,默认就会返回新对象  this
        }
        //使用构造函数创建一个Dog类型的实例
        var d = new Dog( "lddd" );
        //调用d对象实例的run()方法,这就是对象的方法调用模式
        //在方法调用模式中,方法内部的this指向当前调用者的对象 => d
        //this === d//true
        d.run();
        
        
        </script>

    示例代码 构造器调用模式

    <script>
        //构造器调用模式  构造函数
        //关键字new
        function Cat(){
            //第一步:创建一个空对象(新对象)
            //第二步:给函数上下文赋值  新对象  this = 新对象
            this.age = 19;
            this.name = "cat";//在构造函数内部定义的this属性
            this.run = function(){
                console.log( this.name + "run..." );
            }
           
            //如果构造函数没有返回值,那么久返回this(新对象)
            // return 3;//即使有返回值,如果返回值类型是简单类型,那么会被忽略
            // return null;
            //如果返回值是一个引用类型(去掉null)那么新对象就会被抛弃,把这个引用类型返回
            return {
                name:9999,
                run:function(){
                    console.log( "自己返回的{}" );
                }
            };//return 了一个对象的字面量  return 了一个因信用类型
        }
        
        var cat = new Cat();//构造函数调用模式
        //如果使用new关键字+构造函数执行的话  触发了构造函数执行模式
        cat.age = 20;
        cat.name = "2222";
        cat.run();//方法调用模式
        </script>

     示例代码 函数调用模式

    <script>
        //函数调用模式
        function f( a,b ){
            console.log( a + " " + b );
            this.a = 19;    //this === window  true
            console.log( "a" in window );
            console.log( this );    //window 全队对象 严格模式undefined
        }
        f( 2,3 );//直接调用函数:f() 函数调用模式
        
        
        function Dog(){
            this.age =19;
            console.log( this );
        }
    
        Dog();//函数执行模式  => window
        var d = new Dog();//构造函数执行模式  => d对象
        
        </script>

     示例代码 apply  call

    <script>
        //函数调用模式
        function f( a,b ){
            console.log( a + " " + b );
            this.a = 19;    //this === window  true
            console.log( "a" in window );
            console.log( this );    //window 全队对象 严格模式undefined
        }
        f( 2,3 );//直接调用函数:f() 函数调用模式
        
        
        function Dog(){
            this.age =19;
            console.log( this );
        }
    
        Dog();//函数执行模式  => window
        var d = new Dog();//构造函数执行模式  => d对象
        
        </script>

    函数四种调用模式的案例

     

     示例代码:

     <script>
        //1   定义按钮类 ,要求按钮类的构造函数可以接受参数初始化按钮的宽度 高度坐标xy
        function Btn( width,height,x,y ){
            //构造函数内部初始化值
            this.width = width; //给this对象上的width属性赋值  width参数的参数值
            this.height = height;
            this.px = x;
            this.py = y;
        }
    
        var  b = new Btn( 100,100,30,30 );
    
        //2   借用Math的min方法实现求数组[2,9,33]中的最小值
         
        //  var m = Math.min( 2,9,33 );
        //  console.log( m );
    
        //  var m = Math.min.apply( null,[2,9,33] );
        //  console.log( m );
    
         var arr = [1,4,7,8,6,454,44,5];
         var m = Math.min.apply( null,arr );
         console.log( m );
    
        //3 类数组转换成真正的数组
        var t = {};
        t[0] = 1;
        t[1] = true;
        t[2] = "laoma";
        t.length = 3;
    
        console.log( t );
    
        //var m = [ 1,2,3 ];
        //m.slice();//如果设么都不穿,默认从0索引开始截取到数组最后
        //第一个参数是   截取开始的位置    startIndex
        //第二个参数是   截取结束的位置+1  endIndex
    
        //如果我借用数组的slice方法,然后把this指向到t对象
        //那么slice方法就会返回t对象对应的数组
        var k = Array.prototype.slice.call( t,0 );
        //通过一个slice方法,就可以吧类数组转化成真正的数组
        console.log( k );
        console.log("==========");
        //4  判断代码输出的内容
        function Dog(){
            console.log( this );
        }
        Dog();  //函数调用模式
        var d = new Dog();//构造函数调用模式
        Dog.call( null );//借用调用模式
    
        </script>

     JavaScript中函数没有重载

     

    示例代码

     <script>
        //arguments模拟函数重载
        //创建一个矩形的类型,构造函数接受一个参数,返回一个正方形。
        //接收两个参数,返回一个矩形
        function React(){
            //如果一个参数,返回一个正方形
            if( arguments.length == 1 ){
                this.width = arguments[0];
                this.height = arguments[0];
            }
    
            //如果两个参数,返回一个矩形
            if( arguments.length > 1 ){
                this.width = arguments[0];
                this.height = arguments[1];
            }
    
            //由于跟原型上的toString方法重名,那么会覆盖Object原型上的toString方法
            this.toString = function(){
                return "" + this.width + "  height:" + this.height;
            }
        }
    
    
        var r1 = new React( 10 );
        console.log( r1.toString() );
    
        var r2 = new React( 10,9 );
        console.log( r2.toString() );
        
        </script>

     函数的递归调用

     

     示例代码:

     <script>
        //求1-100的和
        //  一般方法
        var sum = 0,
            n = 100;
            for( var i = 0;i <= n;i++ ){
                sum += i;
            }
        console.log( sum );
    
        //  递归的方法
        function sumNum( num ){
            if( num == 0 ){
                //递归 一定要有个结束 自己调用自己 的出口
                return num;
            }else{
                //实现自己调用自己
                return num + sumNum( num-1 );
            }
        }
    
        console.log( sumNum(100) );

     示例代码:函数递归调用案例

    <script>
        //函数递归案例
        var f = function(){
            console.log( "sss" );
        };
    
        f();//调用函数表达式执行
    
        //函数命名表达式,fun相当于在函数内部添加一变量fun,fun指向函数自身
        //fun === arguments.callee
        //fun 的作用域在函数内部,不会影响函数外面的声明的参数或者函数
        var m = function fun(){
            console.log( "fun" );
        };
        
        m();
    
        //求num的阶乘,用递归实现
        function factorial( num ){
            //定义一个出口
            if( num == 0 ){
                return 1;
            }
            return num * factorial( num - 1 );
        }
    
        console.log( factorial(4) );
    
    
        //求斐波那切数列
        //f(0) = 0   f(1) = 1
        //f(n) = f(n-1) + f(n-2)
        function fibonacii(n){
            if( n == 0 ){
                return 0;
            }
            if( n == 1 ){
                return 1;
            }
            return fibonacii( n-1 )+ f( n-2 );
        }
    
        console.log( fibonacii(10) );
        </script>

     函数式编程

    示例代码:

    <script>
        //函数式编程
        //复习  Array.prototype.sort可以对数组中的元素进行排序
        //排序的算法是根据字符串比较大小的算法进行排序
        //不适合于数字的排序
    
        var t = [ 48,2,33,4,55,9,8 ];
        console.log( t );
        console.log( t.sort() );
        
    
        //按照数值的大小进行排序
        t.sort(function( a,b ){//函数式编程  一个函数作为另一个函数的输入
        //如果 a > b return 大于0 的值
        //如果 a === b return 0
        //如果 a < b return 小于0的值
            return a - b;
        });
        console.log( t );
        </script>

     示例代码

    <script>
        //map方法
        var t = [ 1,3,9,10,20 ];
        
        for( var i in t ){
            console.log( t[i] );
        }
    
        //对数组中的每个元素都进行*2,打印结果数组
        //map方法:返回一个新数组,每个项处理完成后的结果组成的新数组
        //对原数组没哟印象
        var m = t.map(function( item,index,array ){
            //item: 就是当前的选项
            //index:当前选项的索引
            //array:当前数组
            //return:返回每个项处理的结果
            return item * 2;
        });
    
        console.log( t );
        console.log( m );
        
        </script>

     示例代码

    <script>
        //数组的forEach方法
        var m = [ "22",true,1,99,98 ];
        
        //打印m中的每个元素
        m.forEach( function( item,index ){
            //item是遍历的每个项
            //index项对应数组的索引
            console.log( "index:" + index + "  " + item );
        } );
    
        // console.log( m );
        </script>

     函数的属性和方法

     示例代码:

    <script>
        //函数自有属性
        function fun1( a,b ){
            console.log( a + b );
        }
        console.log( fun1.length );//函数都有默认的length属性  = 形参的个数
    
        //fun1是函数也是一个独享   就可以当做对象使用
    
        fun1.age = 19;
        console.log( fun1 );
        </script>

     1.3垃圾回收

     示例代码

    <script>
        // JavaScript 内存管理
        
        var m = 0,n = 19;
    
       var t = add( m,n ); //把 a  b  c 标记为进入环境
    
        console.log( t );//a  b  c标记为离开环境  等待垃圾回收
    
        function add( a,b ){
            a++;
            var c = a + b;
            return c;
        }
        
        </script>

     垃圾回收的应用

     示例代码

    <script>
        //垃圾回收处理
    
        //1 数组清零
        var a = [ 1,9,20,333,4,43 ];
        
        a.forEach(function( item ){
            console.log( item );
        });
        //数组清零,让gc尽快的回收a数组的内存的空间
        //a = null; //首先 把a转化成null的类型
        //a = [];   //确实让a变量成一个空数组,但是在堆上重新申请了一个空数组对象
    
        a.length = 0;//可以直接让数字清空,而且数组类型不变
    
        //2 对象复用
        var t = {};     //每次循环都会创建一个新的对象
        for( var i = 0; i < 10;i++ ){
            //var t = {};//每次循环都会创建一个新对象
            t.age = 19;
            t.name = "123";
            t.index = i;
            console.log( t );
        }
    
        t = null;//对象如果已经不用了,那就立即设置为null:等垃圾回收
    
        //3 在循环中最好也别使用函数表达式
        // for( var k = 0;k < 10;k++ ){
        //     var t1 = function( a ){  //创建了10册函数对象
        //         console.log( a );
        //     };
        //     t1( k );
        // }
    
        function t1( a ){
            console.log( a );
        }
        for( var k = 0;k < 10;k++ ){
            t1( k );
        }
    
        t1 = null;
        </script>

     1.4 原型链和闭包

     

     原型链

     

     构造函数的原型对象

     

     

     

     

     

     

     闭包

     

     闭包的应用

     示例代码:

     <script>
        //定义了一个 外层的函数
        function foo( x ){
            var tmp = 3;//定义了一个 局部的变量tmp
            return function( y ){
                console.log( x + y + (++tmp) );//这个函数可以访问的变量 y  tmp  x  bar
            };
        }
        //调用foo方法执行,并把返回的函数给了  bar   变量
        var bar = foo( 2 );
        bar( 10 );
        bar( 20 );
    
    
        function d(){
            var a = 10;
            console.log( a );
        }
    
        d();
        </script>

    闭包的应用

    匿名自执行函数模拟块级作用域示例

     <script>
        
        // var t = function( a ){
        //     console.log( a );
        // };
    
        // t(9);
        //尽量少的定义全局变量,尽量少污染全局变量
    
        //匿名自执行函数
        ;(function( a ){
            console.log( a );
        })(9);
    
        // ;(function(a){
        //     console.log( a );
        // }(8));
        
        
        </script>

    循环注册dom时间中index示例代码

    <body>
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
        </ul>
    
        <script>
        //循环注册dom事件典型错误
        //从文档结构中获取所有的li标签
        var lis = document.querySelectorAll( "li" );
        console.log( lis );
    
        //----循环注册事件的index的典型错误------
        for( var i = 0;i < lis.length; i++ ){
            lis[i].onclick = function( e ){//事情的方法执行是:当事件触发的时候执行
            //变量i是福函数里面的变量
                console.log( i );//把当前的索引i的值打印出来
            };
        }
        // i == 5
    
        //面试题目
        //----改正循环注册事件的index的典型错误------
        for( var i = 0;i < lis.length; i++ ){
            
            ;(function(a){
                lis[a].onclick = function( e ){
                    console.log( a );
                }
            })(i);//传参数:是做的值的复制,做的副本
        }
    
        </script>
    </body>

    setTimeOut中的闭包应用

    <script>
            //setTimeOut的闭包应用
            //经过多少好秒后,执行回调函数
            //bom的一个方法,接受两个参数:第一个参数是回调函数
            //第二个参数是经过的毫秒数
            setTimeout (function () {
                //当1000毫秒后,执行当前的函数体
                console.log("sss");
            }, 1000);
    
            for (var i = 0; i < 10; i++) {
                setTimeout(function () {  //注册1秒后的代码执行很快
                    console.log(i);   //函数体在1秒后才执行
                }, 1000);
            }
    
    
            //用匿名自执行函数解决
            for (var i = 0; i < 10; i++) {
                ; (function (a) {
                    setTimeout(function () {  //注册1秒后的代码执行很快
                        console.log(a);   //函数体在1秒后才执行
                    }, 1000);
                })(i);
            }
    
        </script>

     闭包的缺点

     1.5面向对象

     

     面向对象的概念

     对象属性和行为复用

     

    工厂方式创建对象

     示例代码

     <script>
        //E31-工厂模式创建对象.html
        var a = {};//不能重复利用设置公共属性的代码
    
        //工厂模式
        //我需要建立10个猫对象  每个对象都有年龄 星星的属性  包括run方法
        function createCat( age,name ){
            var o = new Object();
            o.age = age;
            o.name = age;
            o.run = function(){
              console.log( o.name + "  run...." );  
            };
            return 0;
        }
        
        var c = createCat( 19,"dddgg" );
        //优点
        //可以进行批量的创建  都有公共默认值和属性的对象
        //缺点
        //1  对象的方法不能跟其他对象共享   多占内存
        //2  不能识别对象的原型及构造函数
        // c instanceof createCat //false
        </script>

    示例图

     

     构造函数创建对象

     

    示例代码

    <script>
        //E32-构造函数创建对象的模式.html
        function Cat( age,name ){
            this.age = age;
            this.name = name;
            this.run = function(){
                console.log( this.name + "  running..." );
            };
        }
        //当使用new来调用构造函数的时候
        //1 创建一个空对象
        //2 把空对象赋值给this
        //3 执行构造函数里面代码,并给this的属性赋值初始化
        //4 把新创建的对象返回(如果有返回值,返回值是简单类型会直接忽略,还返回this)
        //  如果直接返回引用了类型,直接返回引用对象
    
        //通过构造函数创建一个对象
        var c1 = new Cat( 19,"dd" );
        c1.age = 20;    //修改对象的属性值
        c1.run();
        var c2 = new Cat( 22,"ss" );
    
        //优点
        //1  创建对象的时候默默人初始化一些属性
        //2  可以进行使用instance追溯对象的原型及构造函数
        //   c1 instanceof Cat;//true
        //   c1.constructor === Cat
    
        // 缺点
        // 对象的方法不能进行重用,每个对象里面都要存储一份方法对象,浪费内存
    
    </script>  

    原型构建对象

     示例代码

    <script>
        function Cat(){
            this.age = 19;
    
            //如果需要共享的方法和属性,一般放到原型中定义
            // this.run = function(){
            //     console.log( "run" );
            // };
        }
    
        //原型中定义属性和方法
        Cat.prototype.run = function(){
            console.log( this.name,this.age );
        };
        //私有的属性希望每个对象私自拥有
        Cat.prototype.name = "black cat!";  //所有的新对象都共享这个属性
        
        var c1 = new Cat(); //c1 有自己的run方法
        var c2 = new Cat(); //c2 有自己的fun方法
    
        console.log( c1.name ); //black cat!
        console.log( c2.name ); //black cat!
        c1.run();
    
        console.log( c1.run === c2.run );   //true
    
        c1.name = "good cat";   //对象的属性分为读取和设置两种模式
        //如果是读取:自己没有这个属性,那么去原型上找,直到找到为止,如果找不到,返回undefined
        //如果是吸入:那么自己没有这个属性,那么直接添加一个自己属性
        console.log( c1.name,c2.name );
    
        </script>

     示意图

     组合构造函数模式与原型模式构建对象

     示例代码:

      <script>
            //组合创建模式
            function Cat( age,name ){   //一般构造函数的首字母大写,我们也成为Cat类
                this.age = age; //每个对象都有自己私有的属性值的属性,放到构造函数中
                this.name = name;
            }
            //一把类型的方法,都放在原型上,让所有的对象都共享方法的内存
            Cat.prototype.run = function(){
                console.log( this.nam + " running.." );
            }
            
            var c1 = new Cat(10,"jimi");
            var c2 = new Cat(19,"kimi");
    
            console.log( c1.run === c2.run );
            c1.age = 20;
        </script>

    示意图

     稳妥构造函数模式

     示例代码:

     <script>
        //稳妥构造函数模式
        function Cat(){
            var o = {};
            o.age = 19;
            o.name = "laoma";
            o.run = function(){
                console.log( o.name + "  running...." );
            };
            return o;   //如果是构造函数执行模式,如果返回的是一个引用类型,就把引用类型返回
                        //如果是简单类型,那么就返回this
        }
        //  稳妥构造函数模式,要实现使用new构造一个对象和不使用new构造一个对象效果一样
        var c1 = new Cat(); //构造函数调用模式
        var c2 = new Cat(); //函数调用模式
        //缺点  不能溯源 原型 、构造函数   对象的方法内存不能共享  浪费内存
        </script>

     对象的继承

     示例代码

     <script>
        //原型继承模式
    
        //动物基类
        function Animal( age,name ){
            this.age = age;
            this.name = name;
        }
    
        //在动物基类的原型上添加方法run
        Animal.prototype.run = function(){
            console.log( this.name + "  running..." );
        }
        
        function Cat( age,name ){
            this.age = age;
            this.name = name;
        }
    
        //原型的继承方式
        //Cat.prototype.constructor === Cat
        Cat.prototype = new Animal();
        Cat.prototype.constructor = Cat;    //因为上面的代码把Cat的prototype指向了Animal
        // console.log( Cat.prototype.constructor );
    
        var c = new Cat( 19,"sss" );    //希望 cat 继承animal的属性和方法
        c.run();    //从animal原型上继承的方法
    
        //问题:
        //1 子类构造函数的参数,没法传递给父类的构造函数
        //2 子类的原型的constructor 会被改变 须要自己改回来
    
    
        </script>

     

     示例代码

     <script>
        //原型继承模式
    
        //动物基类
        function Animal( age,name ){
            this.age = age;
            this.name = name;
        }
    
        //在动物基类的原型上添加方法run
        Animal.prototype.run = function(){
            console.log( this.name + "  running..." );
        }
        
        function Cat( age,name ){
            this.age = age;
            this.name = name;
        }
    
        //原型的继承方式
        //Cat.prototype.constructor === Cat
        Cat.prototype = new Animal();       //父类的构造函数:执行第一次   
        Cat.prototype.constructor = Cat;    //因为上面的代码把Cat的prototype指向了Animal
        // console.log( Cat.prototype.constructor );
    
        var c = new Cat( 19,"sss" );    //希望 cat 继承animal的属性和方法
        c.run();    //从animal原型上继承的方法
    
        //问题:
        //1 子类构造函数的参数,没法传递给父类的构造函数
        //2 子类的原型的constructor 会被改变 须要自己改回来
        //3 如果父类里有引用类型的属性,那么所有的子类会共享这个引用类型
        
    
        </script>

     组合借用构造函数模式与原型继承模式

    示例代码

      <script>
        //组合继承模式
        //组合的原型继承和借用构造函数继承
    
        //父类
        function Animal( age,name ){
            this.age = age;
            this.name = name;
            this.food = ["water","fruit","fish"];
        }
        
        //在父类的原型上创建一个run方法
        Animal.prototype.run = function(){
            console.log( this.name + " running ..." );
        }
    
        //定义子类
        function Cat( age,name ){
            // Animal( age,name );  //this ===window  函数执行模式  this === window
            
            //this == c
            //第一次执行父类的构造函数
            Animal.call( this,age,name );   //借用父类的构造函数,给子类创建实例属性
        }
        //第二次执行父类的构造函数
        Cat.prototype = new Animal();
        Cat.prototype.constructor = Cat;
    
    
        var c = new Cat( 19,"bosicat" );    //组合原型继承模式,给子类继承原型的方法和属性
        </script>

     原型式继承 

     示例代码

    <script>
        //原型式继承
        //o就是要借用的对象
        function object( o ){
            function F(){}
            F.prototype = o;    //让空函数的原型指向 o 对象
            return new F(); //创建一个F实例,f的内部原型指向 o 对象
        }
        
        var m = {
            age:19,
            name:"laoma", 
            friends:[
                "laoma1","laoma2"
            ]
        };
    
        var m1 = object( m );
    
        console.log( m1.friends );
        m1.age = 20;
    
        //优点  不需要使用new构造函数就可以直接构造另外其他对象
        //缺点  所有构造出来的实例会共享  原型对象上的引用类型的属性
      
        </script>

     寄生继承模式

     示例代码

     <script>
        //寄生继承模式
        //原型式继承的方法  传一个对象o
        //内部新构造一个对象    新对象的原型指向o
    
        function object( o ){
            function F() {}
            F.prototype = o;
            return new F();
        }
        
        var p = {
            age:19,
            name:"laoma"
        }
        //寄生继承方式:其实就是传一个对象到一个方法(工厂方法)
        //根据传来的对象构造一个新对象  并对新对象进行扩展增强
        function createPerson( p ){
            var o = object( p );    //用p对象构造一个新对象o
            o.say = function(){     //对新构造出来的对象o进行扩展
                console.log( "hhh" );
            };
            return o;
        }
    
        </script>

     寄生组合继承模式

     

     示例代码

    <script>
        //  寄生组合继承模式
        //  组合了:寄生继承模式和借用构造函数继承模式
    
        //父类
        function Animal( age,name ){
            this.age = age;
            this.name =name;
            this.foods = [ "水果","肉" ];
        }
        //父类原型上的方法:通过寄生继承方式进行继承
        Animal.prototype.run = function(){
            console.log( this.name + "  runniing...." );
        };
        
        function Cat( age,name ){
            //使用借用构造函数继承模式来构建对象的实例属性
            Animal.call( this,age,name );
        }
    
        //Cat.prototype = new Animal(); //多执行了一次父类的构造函数
    
        //寄生继承的方法
        Cat.prototype = inheritFrom( Animal.prototype );
    
        var c1 = new Cat( 19,"laoma" );
        var c2 = new Cat( 29,"xiaoma" );
    
        c1.run();
        c2.run();
    
        //寄生继承模式
        function inheritFrom( o ){
            var t = object( o );
            t.constructor = Cat;    //把Cat原型的构造函数指回Cat构造函数
            return t;
        }
    
        //原型式继承的方法
        function object( o ){
            function F(){}
            F.prototype = o;
            return new F();
        }
    
        </script>

     私有变量

    示例代码

      <script>
            //第一种模拟私有变量的方式
            function Person() {
                var age = 0;//私有变量只能通过getAge和setAge来操作age变量
                this.getAge = function () {
                    return age;
                };
                this.setAge = function (a) {
                    age = a;
                };
            }
    
            var p = new Person();
            p.setAge(90);
            //访问p的年龄
            console.log(p.getAge());
    
            //第二种模拟私有变量
            function Per() {
                var age = 0;
                return {
                    getAge: function () {
                        return age;
                    },
                    setAge: function (num) {
                        age = num;
                    }
                }
            }
    
            var p1 = Per();  //创建一个Per对象
            p1.setAge(20);
            console.log(p1.getAge());
    
        </script>

     1.6模块化演变

    示例代码

    <script>
        // 问题
        function demo(){
            var a = b = c = 9;  //a 享受了var关键字,b和 c不享受
            //如果变量没有声明直接拿过来用,那么就相当于直接声明了全局变量
        }
        demo();
        // console.log( a );   //未声明  报错
        console.log( b );   //9
        console.log( c );   //9
        
        //  a.js    a开发的
        var m = 0;
        console.log( m );
    
        //  b.js    b开发的
        var m = "sss";
        console.log( m );
    
        //团队合作噩梦  变量冲突
    
        //尝试解决命名冲突的问题
        //第一个尝试:命名空间
        //  a.js    a开发的
        var Shop = {};  //顶层命名空间
        Shop.User = {}; //电商的用户模块
        Shop.User.UserList = {};    //用户列表页面模块
        Shop.User.UserList.length = 19; //用户一共有19个
    
        //  b.js    b开发的
        Shop.User.UserDetial = {};
        Shop.User.UserDetial.length = 20;
        console.log( Shop.User.UserDetial.length );
        console.log( Shop.User.UserList.length );
    
    
    
        //=>    给单个文件里面定义的局部变量 编程 局部作用域里面的变量
        //第二个尝试
        //  a.js
        ;(function(){
            var a = 9;
        })();
    
        //  b.js
        ;(function(){
            var a = "sss";
        })();
    
        //局部作用域和命名空间的用法减少了变量冲突的可能性
    
    
    
        //第三种尝试:希望能把自己积累的的很多工具封装一个整体的框架
        //btn   form    animate
        //  laoma.js
        ;( function(w){
            //判断老马框架是否存在,如果不存在就初始化创建已给啊
            if( !w.laoma ){
                w.laoma = {};
            }
            // var laoma = {};
    
            w.laoma.Btn = {
                getVal:function(){
                    console.log( "getval" );
                },
                setVal:function( str ){
                    console.log( "setval" );
                }
            };
    
            // window.laoma = laoma;   //把老马对象传递给window全局变量
        } )(window||{});
    
        //  laoma.animate.js
        //  动画组件
        ;(function(w){
            if( !w.laoma ){
                w.laoma = {};
            }
            w.laoma.animate = {};
        })(window||{});
        //  laoma.form.js 
        ;(function(w){
            if( !w.laoma ){
                w.laoma = {};
            }
            w.laoma.form = {};
        })(window||{});
        </script>

     1.7 正则表达式

     正则表达式学习工具:https://c.runoob.com/front-end/854

              https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp

     元字符

     限定符

    括号相关

     贪婪模式

    常见验证案例

     

     JavaScript中使用正则表达式

    示例代码

      <script>
        //  创建一个正则表达式对象  d+
        var exp1 = new  RegExp( "\d+","gi" );
    
        //  创建表达式对象可以用  //g 创建
        var exp2 = /d+/g;
        // console.dier( exp1 );
        // console.dier( exp2 );
        
        //正则对象的test方法,接受一个字符串,然后进行匹配,如果匹配上了,返回true,否则false
        console.log( exp1.test("24234jljl") );
        console.log( exp2.test("jljl") );
    
    
        </script>

     正则对象的属性和方法

     示例代码

     <script>
        // E44-正则的exec方法.html
        var str = "12,34,56";
        var exp = /d{2}/g;
        // console.dir( exp.exec(str) );
        var temp;
    
        //exec方法:如果没有匹配项,那么就会返回null
        //如果有匹配的返回一个数组
        //0 p匹配的字符串
        //index 匹配开始的索引
        //input 要匹配的字符串   str 原始的字符串
        while( (temp = exp.exec(str)) != null ){
            console.log( exp.lastIndex );   //当前匹配完了之后,下一次匹配开始的位置
            console.log( temp[0] );
        }
    
        //如果有分组
        var temp1;
        var str2 = "12abc,34,fde,45asf";
        var exp2 = /d{2}(w)(w+)/g;
        while( (temp1 = exp2.exec(str2)) != null ){
            console.log( exp2.lastIndex );   //当前匹配完了之后,下一次匹配开始的位置
            console.log( temp1[0] );
        }
        
        </script>

    字符串中支持正则的方法

     正则案例

    示例代码

    <script>
        //将字符串  1392945632000,mss,Date(1392945632000) 其中绿色部分的数字转化成日期对象
    
        var str = "1392945632000,mss,Date(1392945632000)";
        
        var t = eval( str.replace(/.*(Date(d+)).*/g,"new $1" ) );
        console.log( t.toString() );
        </script>

     1.7 JavaScript的异常处理

     

     错误信息对象Error

     示例代码

    <script>
        try{
            console.log( 1 );
            var t = new jflsj();//报错
            console.log( 2 );
        }catch(e){
            console.log( e.message );
        }finally{
            console.log( "finally" );
        }
    
        alert( "232" );
    
    
        //throw
        try{
            add( "w","4" );
        }catch( e ){
            console.dir( e );
        }finally{
            console.log("结束");
        }
    
    
        function add( a,b ){
            if( typeof(a) != "number" ){
                throw "传入参数不是整数类型";
                // throw new Error( "传入参数不是整数类型" );    //ff  chrome浏览器
    
                // IE浏览器   第一个参数是错误行号  第二个参数是 错误信息
                // throw new Error(26,'传入参数不是整数类型');  //ie edge
                // var e = new Error;
                // e.message = "传入参数不是整数类型";
                // e.name = "parame type error";
                // throw e;
    
                //  只要遇到throw  程序就停止了
                //  代码跳转到catch语句  如果没有遇到catch语句当前js程序会结束
            }
            return a + b;
        }
    
        
        </script>

    未完待续

  • 相关阅读:
    JavaScript排序算法——快速排序
    JavaScript排序算法——归并排序
    JavaScript排序算法——堆排序
    JavaScript排序算法——插入排序
    ubuntu sublime text3 lisence
    jQuery学习笔记——弹出对话框
    CSS学习笔记——响应式布局
    CSS学习笔记——选择器
    JavaScript学习笔记——事件
    JavaScript学习笔记——节点
  • 原文地址:https://www.cnblogs.com/NightTiger/p/JavaScript.html
Copyright © 2020-2023  润新知