学习笔记 -----ES6

一、面向对象编程介绍
1.2 面向对象的特性:
  • 封装性
  • 继承性
  • 多态性
1.1 面向过程和面向对象的对比

在这里插入图片描述

二、ES6中的类和对象
2.1 对象

在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的:

  • 属性:事物的特征,在对象中用属性来表示(常用名词)
  • 方法:事物的行为,在对象中用方法来表示(常用动词)
2.2 类 class

类抽象了对象的公共部分,他泛指某一大类(class).
对象特指某一个,通过类实例化一个具体的对象

2.3 创建类和对象

类 constructor 构造函数:
constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过 new 命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()
示例代码:

//1.创建类 class 创建一个 明星类
        class Start {
            constructor(uname, age) {
                this.uanme = uname;
                this.age = age;
            }


        }

        //2. 
        var ldh = new Start('刘德华', 18);
        var zxy = new Start('张学友', 20);
        console.log(ldh);
        console.log(zxy);
2.4 类中添加方法

(1)我们类里所有的方法函数都不需要加 function
(2) 多个函数方法之间不需要添加逗号分隔

//1.创建类 class 创建一个 明星类
        class Start {
            //类的共有属性放到 constructor 里面
            constructor(uname, age) {
                this.uanme = uname;
                this.age = age;
            }

            sing(song) {
                // console.log('唱歌');
                console.log(this.uanme + song);
            }


        }

        //2. 利用类创建对象 new
        var ldh = new Start('刘德华', 18);
        var zxy = new Start('张学友', 20);
        console.log(ldh);
        console.log(zxy);
        //(1)我们类里所有的方法函数都不需要加 function
        //(2) 多个函数方法之间不需要添加逗号分隔
        ldh.sing('冰雨');
        zxy.sing('彩虹');
三、类的继承
3.1 关键字 extends
//1.类的继承
        class Father {
            constructor() {

            }
            money() {
                console.log(100);
            }
        }

        class Son extends Father {

        }

        var son = new Son();
        son.money();
3.2 关键字 super

super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

调用父类构造函数:

 class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;

            }
            money() {
                // console.log(100);
                console.log(this.x + this.y);
            }
        }

        class Son extends Father {
            constructor(x, y) {
                // this.x = x;
                // this.y = y;
                super(x, y);
            }

        }

        var son = new Son(1, 2);
        son.money();

调用父类中普通函数:

class Father {
            say() {
                return '我是爸爸';
            }
        }
        class Son extends Father {
            say() {
                // console.log('我是儿子');
                console.log(super.say());
                //susper.say()就是调用父类的普通函数say()
            }
        }

        var son = new Son();
        son.say();
 
  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
    // 2. 子类中没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
3.3 子类继承父类方法同时扩展自己方法
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;
            }
            sub() {
                console.log(this.x - this.y);
            }
        }

        var son = new Son(5, 3);
        son.sub();
        son.sum();

super 必须在子类this之前调用。

3.4 使用类的三个注意点:
  1. 在ES6中没有变量提升,所以必须先定义类,才能通过类实例化对象
  2. 类里面的共有属性和方法一定要加this使用
  3. 类里面的this指向。
    constructor 里面的this 指向的是创建的实例化对象。
    方法里面的this指向的是方法的调用者。
四、构造函数和原型
4.1 构造函数

在Es6之前,对象不是基于类创建的,而是通过构造函数来定义对象和他们的特征。
创建对象的三种方式:

  1. 对象字面量
  2. new Object()
  3. 自定义构造函数
	// 1. 对象字面量
        var obj1 = {};

       // 2. new Object()
        var obj2 = new Object();

        // 3. 自定义构造函数
        function Start(uname, age) {
            this.uanme = uname;
            this.age = age;
            this.sing = function () {
                console.log('我在唱歌');
            }

        }

        var ldh = new Start('刘德华', 20);
        ldh.sing();
        console.log(ldh);
4.2 实例成员和静态成员
  • 实例成员:在构造函数内部通过this添加的成员。实例成员只能通过实例化对象进行访问
  • 静态成员:在构造函数本身添加的成员。静态成员只能通过构造函数进行访问。
function Start(uname, age) {
            this.uname = uname;
            this.age = age;
            this.sing = function () {
                console.log('我在唱歌');
            }

        }

        var ldh = new Start('刘德华', 20);
        //1.实例成员:在构造函数内部通过this添加的成员。uname age sing
        //实例成员只能通过实例化对象进行访问
        ldh.sing();
        console.log(ldh.uname);
        // console.log(Start.unmae); //不能通过构造函数来访问实例成员


        // 静态成员:在构造函数本身添加的成员。
        // 静态成员只能通过构造函数进行访问。
        Start.sex = '男';
        console.log(Start.sex);
        console.log(ldh.sex); //不能通过实例化对象来访问静态成员

4.3 构造函数的问题
  • 存在浪费内存的问题
4.4 构造函数原型 prototype
  • 构造函数通过原型分配的函数是所有对象所共享的。
    每一个构造函数都有一个prototype属性。
  • 我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
  • 原型? 一个对象,我们也称为prototype为原型对象。
  • 原型的作用? 共享方法。
function Start(uname, age) {
            this.uname = uname;
            this.age = age;
            // this.sing = function () {
            //     console.log('我在唱歌');
            // }

        }
        Start.prototype.sing = function () {
            console.log('我在唱歌')
        }

        var ldh = new Start('刘德华', 20);
        ldh.sing();
        var zxy = new Start('张学友', 21);
        zxy.sing();

一般情况下,我们将公共属性定义到构造函数里面,将公共的方法定义到prototype原型对象身上。

4.5 对象原型_proto_

对象都会有一个属性_proto_,指向构造函数里的prototype原型对象,之所以我们能使用构造函数里prototype原型对象里的属性和方法,就是因为对象有_proto_原型的存在。

  • proto 对象原型和原型对象prototype是等价的
  • _proto_对象原型的意义就是在于为对象的查找机制提供一个方向,或者说是一条线路,但是他是一个非标准属性,因此实际开发中,不可以使用这个这个属性,他只是内部指向原型对象prototype.
function Start(uname, age) {
            this.uname = uname;
            this.age = age;
            // this.sing = function () {
            //     console.log('我在唱歌');
            // }

        }
        Start.prototype.sing = function () {
            console.log('我在唱歌')
        }

        var ldh = new Start('刘德华', 20);
        var zxy = new Start('张学友', 21);

        console.log(ldh.sing() === zxy.sing());//true

        console.log(ldh); //对象身上系统自己添加了一个_proto_指向我们的构造函数原型对象prototype
        console.log(ldh.__proto__=== Start.prototype);//true

方法的查找规则:首先先看ldh 对象身上是否有sing 方法,如果有就执行这个对象上的sing,如果没有sing 这个方法,因为有_proto_ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法。

4.6 constructor 构造函数

对象原型(proto)和构造函数原型对象(prototype)里面都有一个属性 constructor,constructor 我们称为构造函数,因为它指回构造函数本身。
constructor 主要用于记录该对象引用哪个构造函数,它可以让原型对象重新指向原来的构造函数。

function Start(uname, age) {
            this.uname = uname;
            this.age = age;
            // this.sing = function () {
            //     console.log('我在唱歌');
            // }

        }
        //很多情况下,我们需要手动利用constructor这个属性,指回原来的构造函数
        // Start.prototype.sing = function () {
        //     console.log('我在唱歌')
        // }

        // Start.prototype.movie =function() {
        //     console.log('我在演电影');
        // }

        //当我们将原型对象的方法全部写在一个对象里,就会造成覆盖,这样的话constructor就不在指向原有的构造函数
        //解决办法: 需要在原型对象内部借助constructor 属性指向原来的构造函数
        Start.prototype = {
            constructor: Start,
            sing: function () {
                console.log('我在唱歌')
            },
            movie: function () {
                console.log('我在演电影');
            }
        }
        var ldh = new Start('刘德华', 20);
        var zxy = new Start('张学友', 21);
        console.log(ldh.__proto__);
        console.log(Start.prototype);
        console.log(ldh.__proto__.constructor);
        console.log(Start.prototype.constructor);
4.7 构造函数、实例、原型对象三者之间的关系

在这里插入图片描述

4.8 原型链

在这里插入图片描述

4.9 JavaScript 的成员查找机制(规则)
  1. 当访问一个对象的属性或者方法时,首先先查找这个对象自身有没有这个属性。
  2. 如果没有,就查找她的原型(也就是__proto__指向的prototype原型对象)
  3. 如果还没有,就查找原型对象的原型(Object的原型对象)
  4. 以此类推,一直找到Object为止(null)
  5. __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条线路
4.10 原型对象中this指向问题

构造函数和原型对象内部的this 指向的是实例对象。

4.11 扩展内置对象
 // 原型对象的应用  扩展内置对象方法
        console.log(Array.prototype);

        Array.prototype.sum = function () {
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
                sum += this[i];
            }
            return sum;
        }

        // Array.prototype = {
        //     sun: function () {
        //         var sum = 0;
        //         for (var i = 0; i < this.length; i++) {
        //             sum += this[i];
        //         }
        //         return sum;
        //     }
        // }

        var arr = [1, 2, 3];
        console.log(arr.sum());

        var arr1 = new Array(11, 22, 33);
        console.log(arr1.sum());

注意: 数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {}, 只能是 Array.prototype.sum = function() {} 的方式

五、继承

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

5.1 call()

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

fn.call(thisArg, arg1, arg2)
  • thisArg 当前调用函数的this的指向对象
  • arg1, arg2 传递的其他参数
function fn(x, y) {
            console.log(this);
            console.log(x + y);
        };

        var obj = {
            name: 'andy',
        }

        fn(); // window
        fn.call(obj); // {name: 'andy'} 改变了this指向
        fn.call(obj, 1, 2);
5.2 借用构造函数继承父类型属性

核心原理: 通过call() 把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性

function Father(uname, age) {
            // this 指向父构造函数的实例对象
            this.uname = uname;
            this.age = age;
        }

        function Son(uname, age, score) {
            // this 指向子构造函数的实例对象
            Father.call(this, uname, age);
            this.score = score;

        }

        var son = new Son('刘德华', 20, 100);
        console.log(son);
5.3 借用原型对象继承方法
//1.借用构造函数继承父类型属性
        // 父构造函数
        function Father(uname, age) {
            // this 指向父构造函数的实例对象
            this.uname = uname;
            this.age = age;
        }
        Father.prototype.money = function () {
            console.log('父亲挣钱了');
        }

        // 子构造函数
        function Son(uname, age, score) {
            // this 指向子构造函数的实例对象
            Father.call(this, uname, age);
            this.score = score;

        }
        //2.借用原型对象继承方法
        // 这样直接赋值会有问题,如果直接继承,如果修改了子原型对象,父原型对象也随之改变
        // 是因为将子原型对象指向了父原型对象的地址,因此父原型对象会随子原型对象变化
        Son.prototype = Father.prototype;

        Son.prototype = new Father();
        //如果利用对象的形式修改了原型对象, 别忘了利用constructor 指向原来的构造函数
        Son.prototype.construstor = Son;
        Son.prototype.exam = function () {
            console.log('子要考试');
        }

        var son = new Son('刘德华', 20, 100);
        console.log(son);
        son.money();
        console.log(Father.prototype);
        console.log(Son.prototype);

5.4 类的本质
  1. class的本质还是function
  2. 类的所有方法都定义到类的prototype属性上
  3. 类创建的实例里面也有 proto 属性,并且这个属性指向类的prototype原型对象
  4. 所以ES6的类它的大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象的语法而已
  5. 所以ES6的类就是语法糖
  6. 语法糖: 就是一种便捷写法,简单理解,有两种方法可以实现相同的功能,但是一种写法更加清晰、方便 ,那么这个方法就是语法糖。
六、ES5 中的新增方法
6.1 数组方法
迭代(遍历)方法: forEach()
  • value :每个数组元素
  • index: 每个数组元素的索引
  • arry: 数组本身
var arr = [1, 2, 3];
        var sum = 0;
        arr.forEach(function (value, index, arry) {
            console.log('每个数组元素' + value);
            console.log('每个数组元素的索引' + index);
            console.log('数组本身' + arry);
            sum += value;
        })
        console.log(sum);
迭代(遍历)方法: filter()
  • filter() 方法,筛选满足条件的值,返回一个新的数组。
  • value :每个数组元素
  • index: 每个数组元素的索引
  • arry: 数组本身
var arr = [12, 34, 56, 89];
        var newArr = arr.filter(function (value, index) {
            // return value >= 20;
            return value % 2 === 0;
        })
        console.log(newArr);some
迭代(遍历)方法: some()
  • some()方法 用于查找数组中是否有满足条件的元素
  • 返回值为布尔值 ,如果存在返回true ,否则返回false
  • 检测到第一个满足条件的元素就会停止循环,不再查找
  • value :每个数组元素
  • index: 每个数组元素的索引
  • arry: 数组本身
		var arr = [34, 45, 89];
        var flag = arr.some(function (value, index) {
            return value > 80;
        })
        console.log(flag);
迭代(遍历)方法: map()
  • map() 方法会将数组中元素依次传入方法中,并将方法的返回结果组成新数组返回。
  • 传入的function可以有自己的三个形参,currentValue, index,arr分别代表当前元素,元素索引,元素所属数组对象;其中currentValue是必须的。
var numbers = [4, 9, 16, 25];
  
 function myFunction() {
      console.log(numbers.map(Math.sqrt));
 }

输出结果为:

2,3,4,5

注意: map不会改变原数组,map不会检查空数组

迭代(遍历)方法: every()
  • every:用于检测数组所有元素是否都符合指定条件(通过函数提供)
  • every和some正好相反:如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测, 如果所有元素都满足条件,则返回 true。
  • function形参同上
var ages = [32, 33, 16, 40];
2 
3 function checkAdult(age) {
4     return age >= 18;
5 }
6 
7 function myFunction() {
8     console.log(ages.every(checkAdult));
9 }

输出结果:false

6.2 字符串方法
trim()方法
  • trim() 方法会从一个字符串的两端删除空白字符。
  • trim() 方法并不影响字符串本身,它返回的是一个新的字符串
var str = '   hello   ';
        console.log(str);
        var str1 = str.trim();
        console.log(str1);
6.3 对象方法
Object.defineProperty() 方法
  • Object.defineProperty() 方法 定义新属性或修改原有的属性。
Object.defineProperty(obj, prop, dexcriptor)
  • obj :要修改的目标对象
  • prop:修改的属性名
  • 第三个参数 descriptor 说明: 以对象形式{} 书写
  • -value :设置属性的值,默认为undefined
  • -writable:值是否可以重写。true | false 默认是 false, 不可重写
  • -enumerable:目标属性是否可以被枚举。 true | false , 默认false
  • -configurable:目标属性是否可以被删除或者是可以再次修改特性 。 true | false ,默认false
var obj = {
            id: 1,
            pname: '小米',
            price: 1999,
        };

        //1. 以前传统方式修改对象属性
        // obj.pname = "大华";
        // obj.price = 99;
        // console.log(obj);

        //2. 利用Object.defineProperty()修改对象的属性
        Object.defineProperty(obj, 'price', {
            value: '999',
        })
        console.log(obj);

        //writable :false 不允许修改
        Object.defineProperty(obj, 'id', {
            writable: false,
        });
        obj.id = 2;
        console.log(obj);

        //enumerable :false 属性不能被枚举
        Object.defineProperty(obj, 'address', {
            value: '河南省',
            writable: false,
            enumerable: false,
            //configurable: false 属性不能被删除 也不能被再次修改
            configurable: false,

        });
        delete obj.address;
        console.log(obj);
        console.log(Object.keys(obj));
七、函数进阶
7.1 函数的定义和调用
7.1.1 函数的定义
  1. 自定义函数(命名函数)
  2. 函数表达式(匿名函数)
  3. 利用 new Function(‘参数1’, ‘参数2’, ‘函数体’)
    - Function 里面参数都必须是字符串格式
    - 第三种方式执行效率低,也不方便书写,因此较少使用
    - 所有函数都是Function的实例对象
    - 函数也属于对象
// 1. 自定义函数(命名函数)
        function fn() { };
        //  2. 函数表达式(匿名函数)
        var f1 = function () { };
        //  3. 利用 new Function('参数1', '参数2', '函数体')
        var f2 = new Function('a', 'b', 'console.log(a + b)');
        f2(1, 2);
7.1.1 函数的调用方式
//1. 普通函数
        function fn() {};
        fn();
        //2. 对象中的方法
        var obj = {
            sayHi: function(){
                console.log('hello');
            }
        }
        obj.sayHi();
        //3.构造函数
        function Star() {};
        new Star();
        //4.绑定事件调用 
        btn.onclick = function(){}; //点击按钮调用
        //5.定时器函数
        setInterval(function(){}, 1000); //一秒之后调用
        //6.立即执行函数
        (function(){
            console.log(123);
        })(); // 立即执行函数是自动调用
7.2 this
7.2.1 函数内 this 指向

在这里插入图片描述

7.2.2 改变函数内 this 指向

1. call()方法及其应用

fun. call(thisArg, arg1, arg2,...)

//call 可以调用函数,也可以改变函数内部this指向
        var o = {
            name: 'andy',
        }
        function fn(x, y) {
            console.log(this);
            console.log(x + y);
        }
        fn.call(o, 2, 3);

        //call 的主要作用可以实现继承
        function Father(uname, age) {
            this.uname = uname;
            this.age = age;

        }

        function Son(uname, age) {
            Father.call(this, uname,age);
        }
        var son = new Son('刘德华', 23);
        console.log(son);

2. apply()方法及其应用

apply0方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的this指向。

fun. apply (thisArg, [argsArray])

  • thisArg :在fun函数运行时指定的this值
  • argsArray :传递的值,必须包含在数组里面
  • 返回值就是函数的返回值,因为它就是调用函数
var o = {
            name: 'andy',
        };
        function fn(arr) {
            console.log(this);
            console.log(arr);// 'pink'
        }

        fn.apply(o);
        fn.apply(o, ['pink']);
        //apply 调用函数,也可以改变函数内部 this 指向
        //但是参数必须是数组(伪数组)
        // apply 的主要应用 比如说我们可以利用 apply 借助于数学内置对象求最大值
        // Math.max()
        var arr = [23, 45, 12, 34];
        var max = Math.max.apply(Math, arr);
        var min = Math.min.apply(Math, arr);
        console.log(max);
        console.log(min);

3. bind()方法及其应用

bind0.方法不会调用函数。但是能改变函数内部this指向

fun. bind (thisArg, arg1, arg2,...)
  • thisArg :在fun函数运行时指定的this值
  • arg1 , arg2 :传递的其他参数
  • 返回由指定的this值和初始化参数改造的原函数拷贝
<!-- <button>点击</button> -->
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <script>
        var o = {
            name: 'andy'
        }
        function fn(x, y) {
            console.log(this);
            console.log(x + y);
        }
        var f = fn.bind(o, 1, 2)
        f();
        //1. 不会调用函数  只会改变原来函数内部this指向
        //2. 返回的是原函数改变this指向之后的新函数
        //3. 应用场景: 如果有的函数我们不需要立即调用,但是又想改变这个函数内部this指向
        //4. 示例: 有一个按钮,点击之后禁用,3秒后重新开启
        // var btn = document.querySelector('button');
        // btn.onclick = function () {
        //     this.disabled = true; //这个this 指向 btn
        //     // var that = this;
        //     setTimeout(function () {
        //         //定时器里面this 指向 window
        //         // btn.disabled = false;
        //         // that.disabled = false;
        //         this.disabled = false;

        //     }.bind(this), 3000)
        // }

        //5.
        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), 2000);
            }
        }


    </script>
7.2.3 call 、apply 、bind 总结

相同点:

都可以改变函数内部的this指向.

区别点:

  1. call和apply会调用函数并且改变函数内部this指向.

  2. call 和apply传递的参数不一样, call传递参数aru1, aru…形式apply必须数组形式[arg]3. bind 不会调用函数可以改变函数内部this指向.

主要应用场景:

  1. call经常做继承

  2. apply 经常跟数组有关系比如借助于数学对象实现数组最大值最小值

  3. bind 不调用函数但是还想改变this指向.比如改变定时器内部的this指向.

7.3 严格模式

除了正常模式,还有严格模式。
1、设立严格模式的目的:

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
  • 消除代码运行的一些不安全之处,保证代码运行的安全;
  • 提高编译器效率,增加运行速度;
  • 为未来新版本的Javascript做好铺垫
"use strict";

2、严格模式如何调用:
2.1 针对整个脚本文件
将"use strict"放在脚本文件的第一行,则整个脚本都将以"严格模式"运行。如果这行语句不在第一行,则无效,整个脚本以"正常模式"运行。

<script>
    "use strict";
    console.log("这是严格模式。");
  </script>

  <script>
    console.log("这是正常模式。");
  </script>

2.2 针对单个函数

将"use strict"放在函数体的第一行,则整个函数以"严格模式"运行。

function strict(){
    "use strict";
    return "这是严格模式。";
  }

  function notStrict() {
    return "这是正常模式。";
  }

2.3 脚本文件的变通写法

因为第一种调用方法不利于文件合并,所以更好的做法是,借用第二种方法,将整个脚本文件放在一个立即执行的匿名函数之中。

(function (){

    "use strict";

    // some code here

   })();

3. 严格模式中的变化
严格模式对JavaScript 的语法和行为,都做了一些改变。

3.1 变量规定

  1. 正常模式: 如果一个变量没有声明就赋值,默认是全局变量。严格模式:变量必须先声明在使用。
  2. 严谨删除已声明的变量。例如 delete x;语法是错误的。
<script>
        'use strict'
        //  1. 正常模式: 如果一个变量没有声明就赋值,默认是全局变量。严格模式:变量必须先声明在使用。
        // num = 10;
        // console.log(num);
        //  2. 严谨删除已声明的变量。例如 delete x;语法是错误的。
        var x = 1;
        delete x;
    </script>

3.2 严格模式下this指向问题

  1. 以前全局作用域函数中的 this 指向 window;严格模式下全局作用域函数中的 this 是 undefined。
  2. 正常模式下,构造函数不加new也可以调用,当普通函数,this 指向 window; 严格模式下,构造函数不加new调用,会报错。
  3. new 实例化的构造函数指向创建的对象实例。
  4. 定时器this 指向还是 window。
  5. 事件、对象还是指向调用者。
'use strict';
 //  1. 以前全局作用域函数中的 this 指向 window;严格模式下全局作用域函数中的 this 是 undefined。
        function fn() {
            console.log(this);
        }
        fn();
        //  2. 正常模式下,构造函数不加new也可以调用,当普通函数,this 指向 window; 严格模式下,构造函数不加new调用,会报错。
        function Star() {
            this.sex = 'nan';
        }
        Star();
        console.log(window.sex);

        //  3. new 实例化的构造函数指向创建的对象实例。
        var ldh = new Star();
        console.log(ldh.sex);

        //  4. 定时器this 指向还是 window。
        setTimeout(function () {
            console.log(this);
        }, 2000)
        //  5. 事件、对象还是指向调用者。

3.3 函数变化

  1. 函数不能有重名的参数
  2. 函数必须申明在顶层,不允许在非函数的代码块内声明函数。(例如: if里,for里是不允许的,但是在函数里声明函数是可以的)
'use strct';
//  1. 函数不能有重名的参数
        function f(a, a,) {
            console.log(a + a);
        }
        f(1, 2);
        //  2. 函数必须申明在顶层,不允许在非函数的代码块内声明函数。
        if (true) {
            function f1() {
                console.log(111);
            }
        }
八、高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。

<div></div>
    <script>
        // 高阶函数 -- 接收函数作为参数
        function fn(x, y, callback) {
            console.log(x + y);
            callback && callback();
        }

        fn(1, 2, function () {
            console.log("我是最后");
        })
        //
        $('div').animate({
            left: 500,
        }, function () {
            $('div').css('backgroundColor', 'puper')
        })
    </script>
九、闭包
9.1 什么是闭包

闭包 ,一个作用域可以访问另一个函数的局部变量

// 闭包(closure)指有权访问另一个函数作用域的变量
        // 此时fn 就是闭包
        function fn() {
            var num = 10;
            function fun() {
                console.log(num);
            }
            fun();
        }
        fn();
9.2 闭包的作用

延伸了变量的作用范围。

// 闭包(closure)指有权访问另一个函数作用域的变量
        // 此时fn 就是闭包
        // 闭包的作用:延伸了变量的作用范围
        // 在函数fn 外面的作用域可以访问 fn 内部的作用域

        function fn() {
            var num = 10;
            // function fun() {
            //     console.log(num);
            // }
            // fun();
            // return fun;
            return function fun() {
                console.log(num);
            }
        }
        var f = fn();
        // 相当于 f = function fun() {
        //         console.log(num);
        //     }

        f();
9.3 闭包的几个小案例

1. 点击li输出索引号

<body>
    <ul class='nav'>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</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 (i) {
                lis[i].onclick = function () {
                    console.log(i);
                }
            })(i)
        }
    </script>
</body>

2. 定时器中的闭包 — 3秒之后将所有小li 打印出来

<body>
    <ul class='nav'>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
    </ul>
    <script>
        // 闭包应用---点击li输出当前li的索引号
        //1.我们可以利用动态添加属性的方式

        var lis = document.querySelector('.nav').querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            (function (i) {
                setTimeout(function () {
                    console.log(lis[i].innerHTML);
                }, 3000)
            })(i)
        }
    </script>
</body>

2. 计算打车价格

<script>
        //  打车起步价13(3公里内), 之后每多一公里增加 5块钱,用户输入公里数就可以计算打车价格,如果有拥堵情况,总价格多收取10块钱拥堵费
        var car = (function () {
            var start = 13; //起步价
            var total = 0; //总价
            return {
                //正常总价
                price: function (n) {
                    if (n <= 3) {
                        total = 13;
                    } else {
                        total = start + (n - 3) * 5;
                    }
                    return total;
                },
                // 拥堵总价
                yd: function (flag) {
                    total = flag ? total + 10 : total;
                    return total;
                },
            }
        })()
        console.log(car.price(5)); //23
        console.log(car.yd(true)); //33

        console.log(car.price(1)); //13
        console.log(car.yd(false));//13
    </script>
十、递归
10.1 什么是递归

函数内部自己调用自己,那么这个函数就是递归函数。
与循环相似,防止栈溢出错误,因此递归必须加退出条件。

var num = 1;
        function fn() {
            console.log('我要输出6次');
            if (num === 6) return;
            num++;
            fn();
        }
        fn();
10.2 利用递归求阶乘

求 123…*n

var count = 1;
        var total = 1;
        function fn(n) {
            if (n === 1) return 1;
            return n * (n - 1);
        }

        console.log(fn(3));
10.3 利用递归求斐波拉契

F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
兔子序列1、1、2、3、5、8、13、21

function fn(n) {
            if (n === 1 || n === 2) return 1;
            return fn(n - 1) + fn(n - 2)
        }
        console.log(fn(3));
10.4 利用递归遍历数据

<script>
        var data = [{
            id: 1,
            name: '家电',
            goods: [{
                id: 11,
                gname: '冰箱',
                goods: [{
                    id: 111,
                    gname: '海尔冰箱',
                }, {
                    id: 112,
                    gname: '美的冰箱',
                }]
            }, {
                id: 12,
                gname: '洗衣机',
            }]
        }, {
            id: 2,
            name: '服饰',
        }];
        //我们想要做输入id号,就可以返回的数据对象
        // 利用forEach 去遍历里面的每一个对象
        function getID(json, id) {
            var o = {};
            json.forEach(item => {
                if (item.id == id)
                    // console.log(item);
                    o = item;
                else if (item.goods && item.goods.length > 0) {
                    o = getID(item.goods, id);
                }
            });
            return o;
        }

        console.log(getID(data, 1));
        console.log(getID(data, 2));
        console.log(getID(data, 11));
        console.log(getID(data, 12));
        console.log(getID(data, 111));
        console.log(getID(data, 112));

    </script>
十一、浅拷贝和深拷贝
  1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝。
  3. 利用 Object.assign(target, …source) 可以实现浅拷贝,是es6新增方法。

浅拷贝代码:

//  1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用。
        //  2. 深拷贝拷贝多层,每一级别的数据都会拷贝。
        //  3. 利用 Object.assign(target,  ...source) 可以实现浅拷贝,是es6新增方法。

        var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 20,
            }
        }
        var o = {};
        // for (var k in obj) {
        //     // k 属性名    obj[k] 属性值
        //     o[k] = obj[k];
        // }
        // console.log(o);
        // o.msg.age = 200;

        Object.assign(o, obj);
        console.log(o);
        console.log(obj);

深拷贝代码:

var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 20,
            },
            color: ['pink', 'purple'],

        }
        var o = {};

        function deepCopy(newObj, oldObj) {
            for (var k in oldObj) {
                //判断属性值的类型 oldObj[k]
                var item = oldObj[k];
                //1.属于数组
                if (item instanceof Array) {
                    newObj[k] = [];
                    deepCopy(newObj[k], item);
                } else if (item instanceof Object) {
                    // 2.属于对象
                    newObj[k] = {};
                    deepCopy(newObj[k], item);
                } else {
                    // 3.属于简单数据类型
                    newObj[k] = item;
                }
            }
        }
        deepCopy(o, obj);
        console.log(o);
        o.msg.age = 200;
        console.log(obj);
十二、正则表达式
12.1 什么是正则表达式

正则表达式适用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
作用:

  • 匹配。验证表单
  • 替换。替换敏感词
  • 提取。提取特定部分
12.2 正则表达式的特点
  • 灵活性、逻辑性和功能性非常的强。
  • 可以迅速地用及简单的方式达到字符串的复杂控制。
  • 对于刚接触的人来说,比较晦涩难懂
12.3 正则表达式在js中的使用

1. 创建正则表达式

  • 利用 RegExp 对象来创建 正则表达式
  • 利用字面量创建 正则表达式
// - 利用 RegExp 对象来创建 正则表达式
        var regexp = new RegExp(/123/);
        console.log(regexp);
        //  - 利用字面量创建 正则表达式
        var rg = /123/;
        console.log(rg);

2. 正则表达式的检测

  • test 方法用来检测字符串是否符合正则表达式要求的规范,符合返回true,不符合返回false.
// - 利用 RegExp 对象来创建 正则表达式
        var regexp = new RegExp(/123/);
        console.log(regexp);
        //  - 利用字面量创建 正则表达式
        var rg = /123/;
        console.log(rg);

        // test 方法用来检测字符串是否符合正则表达式要求的规范
        console.log(rg.test(123));
        console.log(rg.test('abc'));
12.4 正则表达式中的特殊字符

1. 边界符
边界符(位置符)用来提示字符所处的位置,主要有两个字符。

在这里插入图片描述
如果 ^ 和 $ 在一起,表示必须是精确匹配。

var reg = /abc/; //正则表达式里面不需要加引号  不管是数字型还是字符串型
        //1. /abc/ 只要包含有abc这个字符串返回的都是true
        console.log(reg.test('abc')); //true
        console.log(reg.test('abcd')); //true
        console.log(reg.test('aabc')); //true
        console.log('-----------------------');
        var rg = /^abc/;
        console.log(rg.test('abc')); // true
        console.log(rg.test('abcd')); //true
        console.log(rg.test('aabc')); //false
        console.log('-----------------------');
        var rg1 = /^abc$/; // 精确匹配,必须是abc字符串才符合规范
        console.log(rg1.test('abc')); // true
        console.log(rg1.test('abcd')); //false
        console.log(rg1.test('aabc')); //false
        console.log(rg1.test('abcabc')); //false

2. 字符类
字符类: [ ] 表示有一系列字符可供选择,只要匹配其中一个就可以了。[a-z],表示从a到z的范围。

var rg = /[abc]/; //只要包含有a 或者有b, 或者有c 就返回true
        console.log(rg.test('andy')); //true
        console.log(rg.test('baby')); //true
        console.log(rg.test('color')); //true
        console.log(rg.test('red')); //false
        console.log('--------------------------');
        var rg1 = /^[abc]$/; //三选一  只能是a 或者是b  或者是c  这三个字母才返回true
        console.log(rg1.test('aa')); //false
        console.log(rg1.test('a'));  //true
        console.log(rg1.test('b'));  //true
        console.log(rg1.test('c'));  //true
        console.log(rg1.test('abc'));  //false
        console.log('--------------------------');
        var rg2 = /^[a-z]$/; //26个英文字母任何一个字母返回true
        console.log(rg2.test('a')); //true
        console.log(rg2.test('z')); //true
        console.log(rg2.test('11')); //false
        console.log(rg2.test('A')); //false
//字母组合
        var rg3 = /^[a-zA-Z0-9-_]$/; //26个英文字母(包括大小写字母)还有任何一个字母还有数字返回true
        console.log(rg3.test('a')); //true
        console.log(rg3.test('B')); //true
        console.log(rg3.test(9)); //true
        console.log(rg3.test('-')); //true
        console.log(rg3.test('_')); //true
        console.log(rg3.test('!')); //false
        //如果中括号 [ ]里面有^ 表示取反的意思 ,千万别和便捷付^ 混淆
        var rg3 = /^[^a-zA-Z0-9-_]$/;
        console.log(rg3.test('a')); //false
        console.log(rg3.test('B')); //false
        console.log(rg3.test(9)); //false
        console.log(rg3.test('-')); //false
        console.log(rg3.test('_')); //false
        console.log(rg3.test('!')); //true

2. 量词符
量词符用来设定某个模式出现次数。
在这里插入图片描述

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


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

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

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

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

        // {3,6} 大于等于3 小于等于6  中间不能有空格
        var rg = /^a{3,}$/;
        console.log(rg.test('')); //false
        console.log(rg.test('a')); //false
        console.log(rg.test('aaa')); //true
        console.log(rg.test('aaaa'));//true
        console.log(rg.test('aaaaaaaa'));//false

4. 预定义类
预定义类: 某些常见模式的简写方式。
在这里插入图片描述

12.5 正则替换

replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。

stringObject.replace(regexp/substr, replacement)

1.第一个参数: 被替换的字符串 或者 正则表达式
2.第二个参数: 替换为的字符串
3.返回值是一个替换完毕的新字符串

正则表达式参数: /表达式/[switch]
switch有三种值。g: 全局匹配;i: 忽略大小写;gi: 全局匹配和忽略大小写。

案例: 替换敏感字:

<body>
    <textarea name="" id="text" cols="30" rows="10"></textarea><button>提交</button>
    <div></div>
    <script>
        // var str = 'andy和merry';
        // var newStr = str.replace('andy', 'shily');
        // console.log(newStr);

        var text = document.querySelector('#text');
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        // var rg = /^激情|gay&/g;
        btn.onclick = function () {
            div.innerHTML = text.value.replace(/激情|gay/g, '**');
        }
    </script>
</body>
十三、关键字let 、const、var
13.1 let:

用于声明变量的关键字。
使用let关键字声明的变量具有块级作用域
在一个大括内,使用let关键字声明的变量才具有块级作用域, var 关键字不具备这个特点
放置循环变量变成全局变量
使用let关键字声明的变量没有变量提升
使用let关键字声明的变量具有暂时性死区

13.2 cosnt:

用于声明常量;
使用const关键字声明的常量具有块级作用域;
声明常量时必须赋初值;
常量赋值后,值不能修改,分为两种情况:(1)基本数据类型,值就是不可更改(2)复杂数据类型,数据结构内部的值可以更改,但是数据值本身不可更改;

(1)基本数据类型,值就是不可更改
	const PI = 3.14;
	PI = 100;  //有误
(2)复杂数据类型,数据结构内部的值可以更改,但是数据值本身不可更改;
	cosnt arr = [100, 200];
	arr[0] = 123; //可行
	arr = [1, 2]; //不可行
13.3 总结:

在这里插入图片描述

十四、解构赋值

ES6中允许从数组中提取值,按照对应位置,对应量赋值,对象也可以实现解构。

14.1 数组解构:
let arr = [1, 2, 3];
let [a, b, c, d, e] = [1, 2, 3];
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
consoel.log(d); // undefined
consoel.log(e); // undefined
14.2 对象解构:
//对象解构允许我们使用变量的名字匹配对象的属性 匹配成功将对象属性的值赋值给变量

let person = {name: 'zhangsan', age: 12, sex: 'man' };
// 1.
let {name, age, sex} = person;
console.log(name); // zhansan
console.log(age); // 12
console.log(sex); // man


// 2.
let {name: myname} = person;
console.log(myname); // zhangsan
十五、箭头函数

箭头函数是用来简化函数定义语法的;
在箭头函数中,如果函数体中只有一句代码,并且代码的执行结果就是函数的返回值,函数体大括号可以省略;
在箭头函数中,如果形参只有一个,形参外侧的小括号也是可以省略;
箭头函数是没有this指向的,箭头函数里的this指向箭头函数定义位置的上下文;

let obj = {
	name: 'zhangsan',
	age: 12,
	say: () => {
		console.log(this.age);
	}
};

obj.say(); // undefined 因为this指向window

十六、剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

const sum = (...args) => {
	let total = 0;
	args.forEach(item => total += item;)
	return total;
};

sum(19, 20);
sum(10, 20, 30); 

剩余参数和解构配合使用:

let arr = ['张三', '李四', '王宇'];
let [s1, ...s2] = arr;
console.log(s1); // 张三
console.log(s2); // ['李四', '王宇']
十七、Array的扩展方法
17.1 扩展运算符(展开语法):

扩展运算符可以将数组或者对象转为用逗号分隔的参数序列。

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

扩展运算符可以应用于合并数组

//方法一
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr3 = [...arr1, ...arr2];
console.log(arr3); // [1,2,3,4,5,6]

//方法二
arr1.push(...arr2);

扩展运算符可以将伪数组转换为真正的数组:

<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>


//js部分
let divs = document.getElementByTagName('div')
cosnole.log(divs); // HtmlCollections[1,2,3,4,5]
let arr = [...divs];
cosnoel.log(arr); // array[1,2,3,4,5]
17.2 构造函数方法: Array.from()

将类数组或可遍历对象转换为真正的数组。

var arrayLike = {
	'0': '张三',
	'1': '李四',
	'2': '王五',
	'length': 3,
}
var ary = Array.from(arrayLike);
cosnoel.log(ary); // ['张三', '李四', '王五']

Array.from() 方法还可以接收第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

var arrayLike = {
	'0': 1,
	'1': 2,
}
var ary = Array.from(arrayLike, item => return item * 2);
cosnoel.log(ary); // [2, 4];
17.3 实例方法 :find()

用于找出第一个符合条件的数组成员,如果没有找到返回undefined

let arr =[
	 {id:1, name: 'zhangsan'},
	 {id: 2, name:'lisi'}
];
let target = arr.find((item) => {
	//return 的是满足条件
	return item.id == 2
});
console.log(target); // {id: 2, name:'lisi'}

17.4 实例方法: findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回 -1

let arr = [1,2,3,10];
let index = arr.findIndex((value, index) => value > 9);
console.log(index); // 3
17.5 实例方法:includes()

表示某个数组是否包含给定的值,返回布尔值。

[1,2,3].includes(1); // true
[1,2,3].includes(4); // false
十八、模板字符串:

模板字符串可以解析变量;

let name = '张三';
let say = 'hello, ${name}';
console.log(say); // hello,张三

模板字符串中可以换行;

let result = {
	name: '张三',
	age: 20,
}
let html = `<div>
	<span>${result.name}</span>
	<span>${result.age}</span>
</div>`

console.log(html);

模板字符串中可以调用函数;

function fn = () => {
	return '我是fn函数';
}
let html = `我是模板字符串  ${fn()}`;

console.log(html); // 我是模板字符串 我是fn函数
十九、String的扩展方法
19.1 实例对象: startsWith()和 endsWith()
  • startsWith(): 表示参数字符串是否在原字符串的头部,返回布尔值
  • endsWith(): 表示参数字符串是否在原字符串的尾部,返回布尔值
let str = 'hell0 2020';
let r1 = str.startsWith('hello');
let r2 = str.endsWith('2020');
cnosole.log(r1); // true
console.log(r2); // true
19.2 实例方法:repeat()

repeat方法表示将原字符串重复n次,返回一个新字符串

'x'.repeat(3); // 'xxx'
'hello'.repeat(2); // 'hellohello'
二十、Set数组结构

ES6提供了新的数据结构Set。 它类似于数组,但是成员的值都是唯一的,没有重复的值。

// Set本身是一个构造函数,用来生成Set数据结构
const s = new Set();
console.log(s.size); // 0

//Set函数可以接受一个数组作为参数,用来初始化
cosnt set = new Set(['a', 'b', 'c']);
console.log(set.size); // 3

//利用Set可以做数组去重
cosnt s1 = new Set(['a', 'a', 'b', 'b']);
console.log(s1.size); // 2
const ary = [...s1];
console.log(ary); // ['a', 'b']

Set的实例方法:

const s = new Set();
//1. 向set结构中添加值 使用add方法
s.add('a').add('b');
console.log(s.size); // 2

//2. 从set结构中删除值 用到的方法delete  返回值是布尔值,成功true,失败false
const r1 = s.delete('b');
const r2 = s.delete('c');
console.log(r1); // true
console.log(r2); // false

//3.判断某个值是否是set结构中的成员 使用has  返回值是布尔值,是true,否false
const r3 = s.has('a'); //true
const r4 = s.has('d'); // false

//4. 清空set数据结构中的值 使用clear方法
s.clear();
console.log(s.size); // 0

//5.遍历 forEach方法
const s2 = new Set([1,2,3]);
s2.forEach((value) => {
	console.log(value);
})




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值