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>

未完待续

原文地址:https://www.cnblogs.com/NightTiger/p/JavaScript.html