九、ES6(1)

1.面向对象编程介绍:

    1.1面向过程与面向对象:
        面向过程是以步骤来划分问题,面向对象是以对象来划分问题。
    1.2面向过程和面向对象的对比:

2.ES6中的类和对象:

    2.1对象:万物皆对象,特指某一个,通过类实例化一个具体的对象。
        对象是由属性(特征)和方法(行为)组成的。
    2.2类class:抽象了对象的公共部分,它泛指某一大类。
        面向对象的思维特点:
            (1)抽象对象公用的属性和方法封装成一个类。
            (2)对类进行实例化,获取类的对象。
    2.3类构造函数constructor:用于传参,返回实例对象,通过new命令生成对象时自动调用该函数。如果我们不写这个函数,类也会自动生成这个实例。

    <script>
        //1.创建一个类
        class Player {
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            // 方法放在类里面,不需要逗号
            play(basketball) {
                console.log(this.uname + basketball);
            }
        }
        //2.利用类创建对象
        var Kobe = new Player('科比', 41);
        // console.log(Kobe.uname, Kobe.age);
        Kobe.play('打篮球');
    </script>

3.继承extends和super:

    3.1继承:子类可以继承父类的一些属性和方法。

    <script>
        //报错,son可以调用父类sum方法,
        // 但是sum方法里面的this指向的是父类构造函数里面的数据
        //而new Son里面传递的数据是传给了son的构造函数
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
        }
        var son = new Son(3, 6);
        son.sum();
    </script>


    3.2super关键字:用于访问和调用父类的函数。

            <script>
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                super(x, y); //调用父类中的构造函数
            }
        }
        var son = new Son(3, 6);
        son.sum();
    </script>

  •         super.Play()就是调用父类中的普通函数Play。
  •         继承中的属性和方法的查找原则:就近。
  •         super必须放到子类this之前:
    <script>
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }
        //子类继承父类的方法,同时扩展新方法
        class Son extends Father {
            constructor(x, y) {
                //利用super调用父类的函数函数
                //super必须在子类this之前调用,先有爸爸才能有儿子
                super(x, y);
                this.x = x;
                this.y = y;
            }
            subtract() {
                console.log(this.x - this.y);
            }
        }
        var son = new Son(3, 6);
        son.subtract();
        son.sum();
    </script>

        3.3使用类的几个注意点:

<body>
    <button>点击</button>
    <script>
        // 1. ES6中类没有变量提升,必须先定义类,才能new
        // 2. 类里面的共有属性和方法一定要加this来使用
        // 3. this指向问题:构造函数里面的this指向实例化对象,方法主要是看谁调用就指向谁
        // var ldh = new Star('刘德华', 18); 错误
        var that; //验证this指向问题
        var _that;
        class Star {
            constructor(uname, age) {
                that = this;
                //构造函数里面的this指向的是实例化对象
                this.uname = uname;
                this.age = age;
                // this.sing(); //只要new就会自动执行构造函数,所以会执行sing();
                this.btn = document.querySelector('button');
                this.btn.onclick = this.sing; //不加括号点击完才调用,加了括号就立马调用了
            }
            sing() {
                // console.log(uname); 错误
                // 当实例化的对象调用sing时,this指向的还是实例化对象
                // 当你点击btn时,sing中的this指向btn
                console.log(this);
                console.log(that.uname + '暗里着迷'); //that里面存的是构造函数里面的this
            }
            dance() {
                //dance里面的this指向ldh,因为是ldh调用了这个函数
                _that = this;
                console.log(this);
            }
        }
        var ldh = new Star('刘德华', 18);
        console.log(that === ldh);
        ldh.dance();
        console.log(_that === ldh);
    </script>
</body>

 4.构造函数和原型:

    4.1概述:

        在典型的OOP语言中,都存在类的概念,类就是对象的模板,对象就是类的实例。但是ES6前JS并没有引入类的概念。
        在ES6之前,对象不是基于类创建的,而是用构造函数来定义对象和它们的特征。

    <script>
        // 创建对象的三种方式
        // 1.利用 new Object()创建对象
        var obj1 = new Object();
        // 2.利用对象字面量创建对象
        var obj2 = {};
        // 3.利用构造函数创建对象
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
            this.sing = function() {
                console.log('唱歌');
            }
        }
        var ldh = new Star('刘德华', 18);
        console.log(ldh);
    </script>

    4.2构造函数:

        用来初始化对象,与new一起使用。把对象的公共属性和方法抽取出来,封装到这个函数里面。
        new在执行时做的四件事情:

  •             1.在内存中创建一个新的空对象;
  •             2.让this指向这个新的对象;
  •             3.执行构造函数里面的代码,给这个新对象添加属性和方法;
  •             4.返回这个新对象。(不需要return)

        JS的构造函数中可以添加一些成员。

  •             静态成员:在构造函数上添加的成员,只能由构造函数来访问。
  •             实例成员:在构造函数内部创建的对象成员,只能由实例化对象来访问。
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
            this.sing = function() {
                console.log('唱歌');
            }
        }
        var ldh = new Star('刘德华', 18);
        ldh.uname;
        //1.实例成员:构造函数内部通过this添加的,如 uname age sing
        //只能通过实例化对象来访问
        // Star.uname; 不能通过构造函数来访问实例成员
        //2.静态成员:构造函数本身添加的成员 sex就是静态成员
        Star.sex = '男';
        // 静态成员只能通过构造函数来访问
        console.log(Star.sex);
        // ldh.sex; 错误,不能通过实例化对象来访问
    </script>

    4.3构造函数的问题:

        会存在浪费内存的问题。
        如创建了100个对象,就会开辟100块内存空间来存放函数。


    4.4构造函数原型prototype:

  •         构造函数通过原型分配的函数是所有对象所共享的。
  •         JS规定,每一个构造函数里面都有prototype属性,指向另一个对象。这个prototype是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
  •         我们可以把那些不变的方法,直接定义到prototype对象上,这样所有对象的实例就可以共享这些方法了。
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
            // this.sing = function() {
            //     console.log('唱歌');
            // }
        }
        Star.prototype.sing = function() {
            console.log('唱歌');
        }
        // console.log(ldh.sing === zxy.sing); 
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
        ldh.sing();
        zxy.sing();
    </script>

        原型的作用:共享方法。
        一般情况下,公共属性定义到构造函数里面,公共方法放到原型对象上(共享方法,节约内存)。


    4.5对象原型__proto__:


        对象身上自动添加__proto__ 指向构造函数的原型对象prototype。之所以我们对象可以使用prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。
                    对象身上的__proto__ 和 构造函数的prototype是等价的:

console.log(ldh.__proto__ === Star.prototype); //true

    4.6constructor构造函数:

  •         对象原型__proto__和构造函数原型对象prototype里面都有一个constructor属性。constructor我们称为构造函数,因为它指回构造函数本身。
  •         constructor主要用于记录该对象引用于那个构造函数,它可以让原型对象重新指回原来的构造函数。
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        //很多情况下,需要手动的利用constructor这个属性指回原来的构造函数
        // Star.prototype.sing = function() {
        //     console.log('唱歌');
        // }
        // Star.prototype.movie = function() {
        //     console.log('演电影');
        // } 
        Star.prototype = {
            // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,
            //则必须手动利用constructor指回原来的构造函数。
            constructor: Star,
            sing: function() {
                console.log('唱歌');
            },
            movie: function() {
                console.log('演电影');
            }
        }
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 19);
        console.log(Star.prototype);
        console.log(ldh.__proto__);
        console.log(Star.prototype.constructor);
        console.log(ldh.__proto__.constructor);
    </script>

    4.7构造函数、实例、原型对象三者之间的关系:

    4.8原型链:

    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
            console.log('唱歌');
        }
        var ldh = new Star('刘德华', 18);
        //1.只要是对象就有__proto__原型,指向原型对象
        console.log(Star.prototype);
        //2.Star原型对象里面的__proto__原型指向的是Object.prototype
        console.log(Star.prototype.__proto__ === Object.prototype); //true
        //3.Object.prototype里面的原型对象里面的 __proto__原型 指向为null
        console.log(Object.prototype.__proto__); //null
    </script>


    4.9JS的成员查找规则:

        就近原则,如果没有则去上一层查找,直到找到或者为空。

  •         (1)当访问一个对象的属性或者方法时,首先在这个对象自身查找;
  •         (2)如果没有找到,就查找它的原型(__proto__指向的原型对象prototype);
  •         (3)如果还没就查找原型对象的原型(Object的原型对象);
  •         (4)依次类推一直找到Objecct为止(null)。
  •         (5)__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        Star.prototype.sing = function() {
                console.log('唱歌');
            }
            // Star.prototype.sex = '男';
        Object.prototype.sex = '男';
        var ldh = new Star('刘德华', 18);
        // ldh.sex = '男';
        console.log(ldh.sex);
        console.log(Object.prototype);
        console.log(ldh.toString());
    </script>


    4.10原型对象this指向:

    <script>
        function Star(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        var that;
        Star.prototype.sing = function() {
                console.log('唱歌');
                that = this;
            }
        // 1.在构造函数中,this指向的是对象实例ldh
        var ldh = new Star('刘德华', 18);
        ldh.sing();
        // 2.原型对象函数里面的this指向的是实例对象ldh
        console.log(that === ldh); //true
    </script>


    4.11扩展内置对象:

    <script>
        //原型对象的应用 扩展内置对象方法
        Array.prototype.sum = function() {
                var sum = 0;
                for (var i = 0; i < this.length; i++) {
                    sum = sum + this[i];
                }
                return sum;
            }
            //下面这种添加方法是错误的,因为会覆盖掉原来的方法
            // Array.prototype = {
            //     sum: function() {
            //         var sum = 0;
            //         for (var i = 0; i < this.length; i++) {
            //             sum = sum + this[i];
            //         }
            //         return sum;
            //     }
            // }
        var arr = [1, 2, 3];
        console.log(arr.sum());
        console.log(Array.prototype);
    </script>

5.继承:

    ES6之前并没有extends继承方法。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承。


    5.1call():

        调用这个函数,并且修改函数运行时的this指向。

    <script>
        //call()
        function fn(x, y) {
            console.log('我想出去玩');
            console.log(this);
            console.log(x + y);
        }
        var o = {
            name: '杰尼龟'
        };
        //fn();以前的调用方式
        // 1.call()方法调用函数
        // fn.call();
        //2.call()可以改变这个函数的this指向
        fn.call(o, 3, 6); //此时this指向o这个对象,可以理解为fn呼叫o来调用它
    </script>


    5.2利用构造函数继承父类型属性:


        核心原理:通过call()把父类型的this指向子类型的this。

    <script>
        //借用父构造函数继承属性
        // 1.父构造函数
        function Father(uname, age) {
            //this指向父构造函数的对象实例
            this.uname = uname;
            this.age = age;
        }
        // 2.子构造函数
        function Son(uname, age, score) {
            //this指向子构造函数的对象实例
            Father.call(this, uname, age); //调用Father这个函数,并修改Father的指向为Son
            this.score = score;
        }
        var son = new Son('刘德华', 18, 100);
        console.log(son);
    </script>


    5.3利用原型对象继承父类中的方法:

    <script>
        //借用父构造函数继承属性
        // 1.父构造函数
        function Father(uname, age) {
            //this指向父构造函数的对象实例
            this.uname = uname;
            this.age = age;
        }
        Father.prototype.money = function() {
            console.log(1000000);
        };
        // 2.子构造函数
        function Son(uname, age, score) {
            //this指向子构造函数的对象实例
            Father.call(this, uname, age); //调用Father这个函数,并修改Father的指向为Son
            this.score = score;
        }
        // Son.prototype = Father.prototype; 错误,会同时修改父原型对象
        Son.prototype = new Father(); //重点,Father实例对象相当于一个中间量
        //如果利用对象的形式修改了原型对象,要用constructor指回原来的原型对象
        Son.prototype.constructor = Son;
        // 子构造函数专门的方法
        Son.prototype.exam = function() {
            console.log('孩子要考试');
        }
        var son = new Son('刘德华', 18, 100);
        console.log(son);
        console.log(Father.prototype);
        console.log(Son.prototype.constructor);
    </script>

6.类的本质:

  •         类的本质其实还是一个函数。
  •         也可以简单认为,类就是构造函数的另外一种写法。
  •         ES6的类其实就是语法糖。
    <script>
        // ES6之前通过 构造函数+原型 实现面向对象编程
        // (1)构造函数有原型对象prototype
        // (2)构造函数原型对象prototype里面有constructor指向构造函数本身
        // (3)构造函数可以通过原型对象添加方法
        // (4)构造函数创建的实例对象有__proto__原型 指向构造函数的原型对象
        // ES6通过类实现面向对象变成
        class Star {

        }
        console.log(typeof Star);
        //1.类的本质其实还是一个函数
        // (1)类有原型对象prototype
        console.log(Star.prototype);
        // (2)类原型对象prototype里面有constructor指向类本身
        console.log(Star.prototype.constructor);
        // (3)类可以通过原型对象添加方法
        Star.prototype.sing = function() {
            console.log('冰雨');
        };
        // (4)类创建的实例对象有__proto__原型 指向类的原型对象
        var ldh = new Star();
        console.log(ldh.__proto__ === Star.prototype); //true
    </script>

7.ES5中的新增方法:

    7.1数组方法:


        迭代(遍历)方法:forEach()、map()、filter()、some、every();
        forEach():

    <script>
        // forEach 迭代遍历数组
        var arr = [1, 2, 3];
        var sum = 0;
        arr.forEach(function(value, index, array) {
            console.log('每个数组元素' + value);
            console.log('每个数组元素的索引号' + index);
            console.log('数组本身' + array);
            sum = sum + value;
        })
        console.log(sum);
        // filter():筛选数组,它直接返回一个新数组。
        var arr = [12, 20, 4, 31];
        var newArr = arr.filter(function(value, index) {
            // return value > 20;
            return value % 2 != 0;
        });
        console.log(newArr);
    </script>

        some():查找数组中是否有满足条件的元素,它返回的是布尔值,true为查找到,false为没找到。如果找到第一个满足条件的元素,则终止循环,不再继续查找。

    <script>
        // some查找数组是否有满足条件的元素
        var arr = [20, 30, 15];
        var flag = arr.some(function(value, index) {
            return value > 25;
        });
        console.log(flag); //true
        var arr1 = ['Bill', 'Jack', 'Jay'];
        var flag1 = arr1.some(function(value) {
            return value === 'Eason';
        });
        console.log(flag1); //false
    </script>

        filter和some方法的区别:

  •             filter查找满足条件的元素,返回的是一个数组,而且返回的是所有满足条件的元素;
  •             some查找满足条件的元素是否存在,返回的是一个布尔值,如果找到第一个满足条件的元素,则终止循环。

        forEach和some方法的区别:
            如果查询数组中唯一的元素,用some更合适。

    <script>
        var arr = ['Bill', 'Jack', 'Jay'];
        // 1.forEach迭代遍历
        arr.forEach(function(value) {
            if (value == 'Jack') {
                console.log('找到了该元素');
                return true; //在forEach里面找到了也不会终止迭代
            }
            console.log(11);
        })

        // 2.some查找数组
        arr.some(function(value) {
            if (value == 'Jack') {
                console.log('找到了该元素');
                return true; //在some里面找到了会终止迭代,迭代效率更高
            }
            console.log(11);
        })
    </script>


    7.2字符串方法:

        trim():去除空格

    <script>
        // trim方法去除字符串两侧空格
        var str = '   Kobe   ';
        console.log(str);
        var str1 = str.trim();
        console.log(str1);
        var input = document.querySelector('input');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function() {
            var str = input.value.trim();
            if (str === '') {
                alert('请输入内容');
            } else {
                console.log(str);
                console.log(str.length);
                div.innerHTML = str;
            }
        }
    </script>

    7.3对象方法:        

    <script>
        var obj = {
            id: 1,
            pname: '小米',
            price: 1999
        };
        //1.以前给一个对象添加或修改属性的方式
        // obj.num = 300;
        // obj.price = 99;
        // console.log(obj);
        //2.Object.defineProperty() 定义新属性或者修改原有的属性
        Object.defineProperty(obj, 'num', {
            value: 1000
        });
        Object.defineProperty(obj, 'price', {
            value: 9.9
        });
        Object.defineProperty(obj, 'id', {
            writable: false //不允许修改这个属性值
        });
        obj.id = 2; //id还是=1
        Object.defineProperty(obj, 'address', {
            value: '中国山东找蓝翔',
            writable: false,
            // enumerable值为false,则不允许遍历,默认值是false
            enumerable: false,
            // configurable如果为false,则不允许删除和不允许修改第三个参数里面的特性,默认为false
            configurable: false
        });
        console.log(obj);
        console.log(Object.keys(obj));
        delete obj.address;
        console.log(obj);
    </script>

8.函数进阶:    

    8.1函数的定义和调用:


        (1)函数的定义方式:

    <script>
        // 函数的定义方式
        // 1.自定义函数(命名函数)
        function fn() {

        };
        // 2.函数表达式(匿名函数)
        var fun = function() {};

        // 3.利用new Function('参数1','参数2','函数体');
        var f = new Function('a', 'b', 'console.log(a + b)');
        f(3, 7);
        // 4.所有函数都是Function的实例化对象
        console.dir(f); //有__proto__ 属性
        // 5.函数也属于对象
        console.log(f instanceof Object); //true
    </script>

        (2)函数的调用方式: 

    <script>
        //函数的调用方式:

        //1.普通函数
        function fn() {
            console.log('普通函数');
        }
        // fn(); fn.call();
        // 2.对象的方法
        var o = {
            sayHi: function() {
                console.log('对象的方法');
            }
        }
        o.sayHi();
        // 3.构造函数
        function Star() {};
        var ldh = new Star();
        // 4.绑定事件函数
        btn.onclick = function() {}; //点击了按钮就调用这个函数
        // 5.定时器函数
        setInterval(function() {}, 1000); //定时器自动1秒钟调用1次
        // 6.立即执行函数
        (function() {
            console.log('立即执行函数'); //立即执行函数是自动调用
        })();
    </script>

    8.2this:


        (1)函数内this的指向:
        (2)改变函数内部this指向:
            call方法:调用函数的方式,但是它可以改变函数this的指向。

    <script>
        // 改变函数内this指向 js提供了三种方法 call() apply bind() 

        // 1.call() 可以调用函数,并且改变函数内部的this指向
        var o = {
            name: 'Kobe'
        }

        function fn(a, b) {
            console.log(this);
            console.log(a + b);
        };
        fn.call(o, 1, 2);
        // call的主要作用可以实现继承
        function Father(uname, age) {
            this.uname = uname;
            this.age = age;
        };

        function Son(uname, age) {
            Father.call(this, uname, age);
        }
        var son = new Son('ldh', 18);
        console.log(son);
    </script>

            apply方法:作用同上。不同之处在于传递的值是数组。

    <script>
        // apply()
        // 1.也是调用函数,并且第二个可以改变函数内部的this指向
        var o = {
            name: 'Kobe'
        };

        function fn(arr) {
            console.log(this);
            console.log(arr); //'yellow'
        };
        // 2.但是它的参数必须是数组
        fn.apply(o, ['yellow']);
        // 3.apply的主要应用 比如说我们可以利用apply借助于数学内置对象求最大值
        Math.max();
        var arr = [22, 55, 11, 6, 33];
        var max = Math.max.apply(Math, arr);
        var min = Math.min.apply(Math, arr);
        console.log(max, min);
    </script>

            bind方法:不会调用函数,但是可以改变this指向。
                返回的是原函数改变this之后产生的新函数。

<body>
    <button>点击</button>
    <button>点击</button>
    <button>点击</button>
    <script>
        // bind()
        // 1.不会调用原来的函数,但是可以改变this指向
        // var o = {
        //     name: 'Kobe'
        // };

        // function fn(a, b) {
        //     console.log(this);
        //     console.log(a + b);
        // };
        // // 2.bind()返回的是原函数改变this之后产生的新函数
        // var f = fn.bind(o, 3, 7);
        // f();
        // // 3.如果有的函数我们不需要立即调用,但是又想改变函数内部的this指向就用bind
        // // 如:我们有一个按钮,点击之后就禁用这个按钮,3秒钟之后再开启
        // var btn = document.querySelector('button');
        // btn.onclick = function() {
        //     this.disabled = true; //这个this指向btn按钮
        //     setTimeout(function() {
        //             // this.disabled = false; //定时器里面的this指向的是window
        //             this.disabled = false; //此时定时器里面的this指向的是btn
        //         }.bind(this), 3000) //这个this指向的是btn对象
        // }
        var btns = document.querySelectorAll('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                this.disabled = true;
                setTimeout(function() {
                    this.disabled = false;
                }.bind(this), 1000);
            }
        }
    </script>
</body>

            call apply bind总结:

  •                 相同点:都可以改变函数内部的this指向。
  •                 区别点:
  1.                     call和apply会调用函数,但是传递的参数不一样。call传递的是普通参数,apply传递的必须是数组形式。
  2.                     bind不会调用函数。
  •                 主要应用场景:
  1.                     call经常做继承。
  2.                     apply经常跟数组有关系。
  3.                     bind不调用函数且需要改变内部this指向。

    8.3严格模式:

        (1)什么是严格模式?            
        (2)开启严格模式:
            严格模式可以应用到整个脚本或个别函数中。因此我们可以将严格模式分为 为脚本开启严格模式和为函数开启严格模式两种情况。

  •             为脚本开启严格模式:

                在所有语句之前放一个特定语句"use strict";

<body>
    <!-- 为整个脚本-script标签开启严格模式 -->
    <script>
        // 下面的js代码就会按照严格模式执行代码
        "use strict"; //1.第一种方式
    </script>
    <script>
        (function() {
            'use strict'; //2.第二种方式
        })();
    </script>
</body>
  •             为函数开启严格模式:
<body>
    <!-- 为某个函数开启严格模式 -->
    <script>
        function fn() {
            // 只给函数fn开启严格模式
            'use script';
            // 下面的代码按照严格模式执行
        }

 

        function fun() {
            // 里面的还是按照普通函数执行
        }
    </script>
</body>

        (3)严格模式中的变化:严格模式对JS的语法和行为都做了一些改变。
            变量规定:

  •                 变量必须先声明才能使用。
  •                 不能随意删除已经声明好的变量。

            this指向问题:

  •                 以前在全局作用域函数中this指向window对象,严格模式下this是undefined。
  •                 构造函数不加new来调用,this会报错(因为它指向undefined)。
  •                 定时器里的this指向的还是window。
  •                 事件、对象还是指向调用者。
    <script>
        'use strict'
        //1.变量必须先声明才能使用
        // var num = 10;
        // console.log(num);
        //2.不能随意删除已经声明好的变量
        // delete num;
        // 3.严格模式下全局作用域中函数的 this是undefined
        // function fn() {
        //     console.log(this); //undefined
        // }
        // fn();
        // 4.严格模式下 构造函数不加new来调用,this会报错(因为它指向undefined)
        // function Star() {
        //     this.sex = '男';
        // }
        // // Star();
        // // 5. new 实例化后,构造函数指向创建对象的实例
        // var ldh = new Star();
        // console.log(ldh.sex);

        // 6. 定时器里的this指向的还是window
        setTimeout(function() {
            console.log(this);
        }, 1000)
    </script>

            函数变化:

  •                 函数不能有重名的参数。
  •                 不允许在非函数的代码块内声明函数。

    8.4高阶函数:

        参数或者返回值是函数。

    <script>
        // 高阶函数 -函数可以作为参数传递
        function fn(a, b, callback) {
            console.log(a + b);
            callback && callback();
        }
        fn(1, 2, function() {
            console.log('我是最后调用的');
        });
    </script>

    8.5闭包:

        (1)变量作用域:全局变量和局部变量。

  •             函数内部可以使用全局变量。
  •             函数外部不可以使用局部变量。
  •             当函数执行完毕,本作用域内的局部变量会销毁。

        (2)什么是闭包?

  •             闭包:指有权访问另一个函数作用域中变量的函数。(JavaScript高级程序设计)

                简单理解:一个作用域可以访问另一个函数内部的局部变量。

  •             闭包的主要作用:延伸了变量的作用范围。
    <script>
        // 闭包:有权访问另一个函数作用域中变量的函数
        // 闭包:我们fun这个函数作用域,访问了函数fn里面的局部变量num
        // fn外面的作用域可以访问 fn内部的局部变量
        // 闭包的主要作用:延伸了变量的作用范围
        function fn() {
            var num = 10;

            // function fun() {
            //     console.log(num);
            // }
            // return fun;
            return function() {
                console.log(num);
            }
        }
        var f = fn();
        f();
        // 类似于 var f =            
        // function fun() {
        //     console.log(num);
        // }
    </script>

            闭包案例:

  •                 点击li打印当前索引号。
<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>菠萝</li>
        <li>苹果</li>
        <li>梨</li>
    </ul>
    <script>
        //闭包应用-点击li输出当前li的索引号
        // 1.我们可以利用动态添加属性的方式
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            lis[i].index = i;
            lis[i].onclick = function() {
                console.log(this.index);
            }
        }
        // 2.利用闭包的方式得到当前小li的索引号
        for (var i = 0; i < lis.length; i++) {
            // 利用for循环创建了4个立即执行函数
            //立即执行函数也称为小闭包,因为立即执行函数里面的任何一个函数都可以使用它的i变量
            (function(a) { //这里的a是形参,相当于 a= i;
                lis[a].onclick = function() {
                    console.log(a);
                }
            })(i); //这里的i是实参 = for里面的i
        }
    </script>
</body>
  •                 3秒钟之后打印li内容。
<body>
    <ul class="nav">
        <li>榴莲</li>
        <li>菠萝</li>
        <li>苹果</li>
        <li>梨</li>
    </ul>
    <script>
        // 闭包应用-3秒钟之后,打印所有li元素的内容
        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            (function(a) {
                setTimeout(function() {
                    console.log(lis[a].innerHTML);
                }, 3000)
            })(i);
        }
    </script>
</body>

        (3)闭包总结:

  •             闭包是一个函数。
  •             闭包的作用:延伸变量的作用范围。

9.递归:

    9.1什么是递归?

  •         递归:函数内部自己调用自己。
  •         由于递归很容易发生栈溢出错误,所以必须加退出条件return。

    9.2利用递归求阶乘:

<body>
    <script>
        // 利用递归求1~n的阶乘
        // 详细思路 假如用户输入的是5
        // return 5*fn(4) -->return 4*fn(3) --> return 3*fn(2)
        // return 2*fn(1) -->fn(1)==1 从后往前开始返回(递归的性质)
        function fn(n) {
            if (n == 1) {
                return 1;
            }
            return n * fn(n - 1);
        }
        var num = fn(5);
        console.log(num);
    </script>
</body>


    9.3利用递归求斐波那契数列:

<body>
    <script>
        //利用递归函数求斐波那契数列(兔子序列)1、1、2、3、5、8、13、21
        //用户输入一个数字n就可以求出 这个数字对应的兔子序列值
        //知道用户输入的n的前两项 n-1 n-2 就可以计算n对应的序列值
        function fn(n) {
            if (n == 1 || n == 2) {
                return 1;
            }
            return fn(n - 1) + fn(n - 2);
        }
        var num = fn(8);
        console.log(num);
    </script>
</body>


    9.4浅拷贝和深拷贝:

  •         浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
    <script>
        // 浅拷贝只拷贝一层,更深层次对象级别的只拷贝引用
        var obj = {
            id: 1,
            name: 'Kobe',
            msg: {
                age: 18
            }
        };
        var o = {};
        // for (var k in obj) {
        //     // k是属性名 obj[k]属性值
        //     o[k] = obj[k];
        // }
        // console.log(o);
        // o.msg.age = 20;  //会一起变化
        // console.log(obj);
        console.log('-----');
        Object.assign(o, obj);//ES6的语法糖
        console.log(o);
    </script>

            o.msg和obj.msg指向同一个地址,如果修改了o.msg.age,obj.msg.age的值也会改变。

  •         深拷贝拷贝多层,每一级别的数据都会拷贝。
<body>
    <script>
        //深拷贝拷贝多层,每一级别的数据都会拷贝
        var obj = {
            id: 1,
            name: 'Kobe',
            msg: {
                age: 18
            },
            color: ['yellow', 'skyblue']
        };
        var o = {};
        // 封装函数
        function deepCopy(newobj, oldobj) {
            for (var k in oldobj) {
                // 判断我们的属性值属于那种数据类型
                // 1.获取属性值 oldobj[k]
                var item = oldobj[k];
                // 2.判断这个值是否是数组
                if (item instanceof Array) {
                    newobj[k] = [];
                    deepCopy(newobj[k], item);
                } else if (item instanceof Object) {
                    // 3.判断这个值是否是对象
                    newobj[k] = {};
                    deepCopy(newobj[k], item);
                } else {
                    // 4.属于简单数据类型
                    newobj[k] = item;
                }
            }
        }
        deepCopy(o, obj);
        console.log(o);
    </script>
</body>

10.正则表达式:

    10.1正则表达式概述:

  •         正则表达式是用于匹配字符串中字符组合的模式。在JS中,正则表达式也是对象。
  •         正则表达式通常被用来检索、替换、提取等。

    10.2正则表达式在JS中的使用:

  •         1.正则表达式的创建:

            (1)利用RegExp对象来创建。
            (2)通过字面量创建。

  •         2.测试正则表达式test:
<body>
    <script>
        //正则表达式在js中的使用
        // 1.利用RegExp对象来创建正则表达式
        var regexp = new RegExp(/123/);
        console.log(regexp);
        // 2.字面量创建
        var rg = /123/;
        // 3.test方法用来测试字符串是否符合正则表达式
        console.log(rg.test(123)); //true
        console.log(rg.test('ac')); //false
    </script>
</body>

    10.3正则表达式中的特殊字符:

<body>
    <script>
        // 边界符 ^ $
        var rg = /abc/; //正则表达式里面不需要加引号,不管是数字型还是字符串型
        // /abc/ 只要包含有abc(不断开)这个字符串,返回的都是true
        console.log(rg.test('aaabc')); //true
        console.log(rg.test('abcad')); //true
        console.log('--------------');
        var reg1 = /^abc/; //必须以abc开头
        console.log(reg1.test('aaabcd')); //false
        console.log(reg1.test('abcad')); //true
        console.log('--------------');
        var reg2 = /^abc$/; //精确匹配:要求必须是abc字符串才符合规范
        console.log(reg2.test('aaabcd')); //false
        console.log(reg2.test('abcad')); //false
    </script>
</body>

  •         3.字符类:

            (1)[] 表示有一系列字符可供选择,只要匹配其中一个就可以了
            (2)[-] -表示范围

<body>
    <script>
        // 字符类: [] 表示有一系列字符可供选择,只要匹配其中一个就可以了
        var rg = /[abc]/; // 只要包含有a,或者b,或者c就可以
        console.log(rg.test('kobe')); //true
        console.log(rg.test('kid')); //false
        var rg1 = /^[abc]$/; //三选一,只有是a/b/c 这三个字母才返回true
        console.log(rg1.test('a')); //true
        console.log(rg1.test('aa')); //false
        console.log('------------');
        //[-] -表示范围
        var rg2 = /^[a-z]$/; //26个英文字母返回的都是true
        //字符组合
        var rg3 = /^[a-zA-Z0-9_-]$/; //26个英文字母(大写和小写都可以)任何一个都返回true
        console.log(rg3.test('a3')); //false
        console.log(rg3.test('!')); //false
        console.log(rg3.test('_')); //true
        console.log('------------');
        //如果[]里面有^,表示取反,和边界符的结果不一样
        var rg4 = /^[^a-zA-Z0-9_-]$/;
        console.log(rg4.test('a3')); //false
        console.log(rg4.test('!')); //true
        console.log(rg4.test('_')); //false
    </script>
</body>
  •         4.量词符:用来设定某个模式出现的次数。
<body>
    <script>
        // 4.量词符:用来设定某个模式出现的次数。
        //简单理解:让下面的a这个字符重复多少次
        // var reg = /^a$/;

        // * 相当于 >=0 可以出现0次或多次
        // var reg = /^a*$/;
        // console.log(reg.test('')); //true
        // console.log(reg.test('a')); //true
        // console.log(reg.test('aaa')); //true

        // + 相当于 >=1 可以出现1次或多次
        // var reg = /^a+$/;
        // console.log(reg.test('')); //false
        // console.log(reg.test('a')); //true
        // console.log(reg.test('aaa')); //true

        // ? 相当于 1 || 0 出现1次或0次
        // var reg = /^a?$/;
        // console.log(reg.test('')); //true
        // console.log(reg.test('a')); //true
        // console.log(reg.test('aaa')); //false

        // // {3} 就是重复3次
        // var reg = /^a{3}$/;
        // console.log(reg.test('')); //false
        // console.log(reg.test('a')); //false
        // console.log(reg.test('aaa')); //true

        // // {3,} 大于等于3次
        // var reg = /^a{3,}$/;
        // console.log(reg.test('a')); //false
        // console.log(reg.test('aaa')); //true
        // console.log(reg.test('aaaa')); //true

        // {316} 大于等于3次 并且小于16
        var reg = /^a{3,16}$/;
        console.log(reg.test('a')); //false
        console.log(reg.test('aaa')); //true
        console.log(reg.test('aaaa')); //true
        console.log(reg.test('aaaaaaaaaaaaaaaaaaaaaaaa')); //false
    </script>
</body>
  •             量词重复某个模式的次数:
<body>
    <script>
        // 量词是设定某个模式出现的次数
        var reg = /^[a-zA-Z0-9_-]{3,16}$/; //这个模式用户只能输入英文字母 数字 下划线 短横线
        // 界符和[]就限定了只能多选1
        console.log(reg.test('a')); //false
        console.log(reg.test('ae86')); //true
        console.log(reg.test('ae!86')); //false
    </script>
</body>

  •         5.括号总结:
<body>
    <script>
        // 中括号 字符集合,匹配方括号中的任意字符
        // var reg = /^[abc]$/;
        // 大括号 量词符,里面表示重复次数
        var reg = /^abc{3}$/; // 此时{}只是让c重复3次 以a开头,c结尾且c重复3次
        console.log(reg.test('abcabcabc')); //false
        console.log(reg.test('abccc')); //true
        // 小括号 表示优先级
        var reg1 = /^(abc){3}$/;
        console.log(reg1.test('abcabcabc')); //true
        console.log(reg1.test('abccc')); //false
    </script>
</body>

            正则表达式在线测试:https://c.runoob.com/front-end/854/
        6.预定义类:某些常见模式的简写方式。
            
    10.4正则表达式中的替换:

  •         1.replace替换:
    <script>
        // //替换replace 第一个参数 被替换的字符串/正则表达式 第二个参数 替换的字符串
        // var str = 'Kobe和Plua';
        // // var newStr = str.replace('Plua', 'Gasol');
        // var newStr = str.replace(/Plua/, 'Gasol');
        // console.log(newStr);
        var text = document.querySelector('textarea');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        btn.onclick = function() {
            div.innerHTML = text.value.replace(/激情/g, '**');
        }
    </script>
  •         2.正则表达式参数: 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值