类与对象

创建类与对象

  1. 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
  2. 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
  3. constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
<script>
    // 1. 创建类 class  创建一个 明星类
    class Star {
        constructor(uname, age) {
            this.uname = uname;
            this.age = age;
        }
    }

    // 2. 利用类创建对象 new
    var ldh = new Star('刘德华', 18);
    var zxy = new Star('张学友', 20);
    console.log(ldh);
    console.log(zxy);
</script>

在类中添加方法

<script>
    // 1. 创建类 class  创建一个 明星类
    class Star {
        // 类的共有属性放到 constructor 里面
        constructor(uname, age) {
            this.uname = uname;
            this.age = age;
        }
        sing(song) {
            // console.log('我唱歌');
            console.log(this.uname + song);

        }
    }

    // 2. 利用类创建对象 new
    var ldh = new Star('刘德华', 18);
    var zxy = new Star('张学友', 20);
    console.log(ldh);
    console.log(zxy);
    // (1) 我们类里面所有的函数不需要写function 
    //(2) 多个函数方法之间不需要添加逗号分隔
    ldh.sing('冰雨');
    zxy.sing('李香兰');
</script>

类的继承

  • super调用父类的构造函数
<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(x, y); 
        }
    }
    var son = new Son(1, 2);
    var son1 = new Son(11, 22);
    son.sum(); // 3
    son1.sum(); // 33
</script>
  • super 关键字调用父类普通函数

继承中的属性或者方法查找原则: 就近原则

  1. 先去查看子类中是否有方法
  2. 如果子类没有,再去父类中查看是否有方法
<script>
    class Father {
        say() {
            return '我是爸爸';
        }
    }
    class Son extends Father {
        say() {
            // console.log('我是儿子');
            console.log(super.say() + '的儿子');
            // super.say() 就是调用父类中的普通函数 say()
        }
    }
    var son = new Son();
    son.say();
</script>
  • 子类继承父类方法,同时扩展自己的方法
<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(5, 3);
    son.subtract();
    son.sum();
</script>

类里面this的指向问题

  • constructor 里面的this 指向的是 创建的实例对象
  • 类中的方法里面的this指向的 谁调用这个方法,指向谁
<button>点击</button>
<script>
    var that;
    var _that;
    class Star {
        constructor(uname, age) {
            // constructor 里面的this 指向的是 创建的实例对象
            that = this;
            console.log(this);

            this.uname = uname;
            this.age = age;
            // this.sing();
            this.btn = document.querySelector('button');
            this.btn.onclick = this.sing;
        }
        sing() {
            // 这个sing方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
            console.log(this);
            console.log(that.uname); // that里面存储的是constructor里面的this
        }
        dance() {
            // 这个dance里面的this 指向的是实例对象 ldh 因为ldh 调用了这个函数
            _that = this;
            console.log(this);

        }
    }

    var ldh = new Star('刘德华');
    console.log(that === ldh); // true
    ldh.dance();
    console.log(_that === ldh); // true

    // 1. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象

    // 2. 类里面的共有的属性和方法一定要加this使用.
</script>

构造函数和原型

创建对象的三种方式

  • 使用构造函数的注意事项
  1. 构造函数用于创建某个对象, 其首字母要大写
  2. 构造函数要与new用在一起才有意义
  • 使用new创建构造函数所做的事情
  1. 在内存中创建一个新的空对象
  2. 让this指向这个空对象
  3. 执行构造函数的代码,给这个新对象添加属性和方法
  4. 返回这个新对象(所以构造函数里面不需要return)
<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);
    var zxy = new Star('张学友', 19);
    console.log(ldh);
    ldh.sing();
    zxy.sing();
</script>

实例成员和静态成员

<script>
    // 构造函数中的属性和方法我们称为成员, 成员可以添加
    function Star(uname, age) {
        this.uname = uname;
        this.age = age;
        this.sing = function() {
            console.log('我会唱歌');

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

构造函数原型prototype

构造函数存在的一个问题:每次new对象(创建实例)的时候,就会在内存中开辟一片空间,如果需要创建100个实例,那么就会在内存中开辟100个空间,这样做,就会浪费内存空间

为了解决这个问题,引入了原型对象prototype

  • 构造函数通过原型分配的函数是所有对象所共享的
  • js规定,每一个构造方法都有一个prototype属性,指向另外一个对象。
<script>
    // 1. 构造函数的问题. 
    function Star(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    // 通过prototype共享sing方法
    Star.prototype.sing = function() {
        console.log('我会唱歌');
    }
    var ldh = new Star('刘德华', 18);
    var zxy = new Star('张学友', 19);
    console.log(ldh.sing === zxy.sing);	// true
    ldh.sing();
    zxy.sing();
    // 2. 一般情况下,我们的公共属性定义到构造函数里面, 公共的方法我们放到原型对象身上
</script>

对象原型 proto

  • 如下代码,思考为什么可以通过ldh调用sing方法 (ldh.sing())

我们在创建实例的时候,js会自动给实例添加一个 __proto__对象,这个对象指向的是 Star.prototype构造函数的原型,所以 ldh.__proto__ === Star.prototype

<script>
    function Star(uname, age) {
        this.uname = uname;
        this.age = age;
    }
    Star.prototype.sing = function() {
        console.log('我会唱歌');
    }
    var ldh = new Star('刘德华', 18);
    var zxy = new Star('张学友', 19);
    ldh.sing();
    console.log(ldh); // 对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象 prototype
    console.log(ldh.__proto__ === Star.prototype); // true
    // 方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的sing
    // 如果么有sing 这个方法,因为有__proto__ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法
</script>

数组

遍历数组

const arr = []
arr.forEach((item)=>{
   // item表示数组中元素
})