JavaScript高级(二)构造函数和原型、继承、ES5新增数组方法

JavaScript高级(二)

一、构造函数和原型

1、对象的三种创建方式

// 使用构造函数创建对象
        function People(name, age, sex) {
     
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.say = function() {
     
                console.log('hello');
            }
        }
        var a = new People('小明', 13, '男');
        console.log(a);
        // 字面量
        var obj = {
     
            name: '小敏',
            age: 12
        };
        // new Object()
        var obj2 = new Object();
        obj2.name = '小红';
        obj2['age'] = 12;
        console.log(obj2);

2、静态成员和实例成员

  • 静态成员:给构造函数添加的属性或方法, 只能通过构造函数本身才能访问的属性或方法
  • 实例成员:只能通过实例对象才能访问的属性或方法
  function People(uname, age, sex) {
     
            this.uname = uname;
            this.age = age;
            this.sex = sex;
            this.say = function() {
     
                console.log('hello');
            }
        }
        var a = new People('小明', 13, '男');
        // 1. 实例成员: 只能通过实例对象才能访问的属性或方法
        console.log(a.uname);
        console.log(People.uname); //undefined ,原因是uname不在People内
        // 2. 静态成员:给构造函数添加的属性或方法, 只能通过构造函数本身才能访问的属性或方法
        People.height = 'height';
        console.log(a.height); //undefined ,原因是height不在a内
        console.log(People.height);

3、构造函数模型

  • 构造函数中直接添加方法的缺点

    每次创建对象,要重复开辟内存空间,浪费资源

  • 构造函数的原型 prototype

    每个函数默认都有一个 propotype 属性, 它的值默认是一个对象

    在 prototype 对象上的方法和属性, 会被 new 构造函数() 创建出来的实例对象所继承

  • 注意:

    ​ (1) 只要是函数就默认有 prototype 属性, 但非函数的对象是不具有的

    ​ (2) 定义构造函数时, 公共的方法定义在原型对象上, 这样可以被所有创建出来的实例直接继承

 function Student(uname, age) {
     
            this.uname = name;
            this.age = age;
        }
        // 每个函数默认都有一个 propotype 属性, 它的值默认是一个对象
        Student.prototype.sing = function() {
     
            console.log('哈哈哈哈');
        }
        Student.prototype.study = '学习JS';
        var xm = new Student('小明', 15);
        // 在prototype上添加的方法或属性会被所有的实例对象继承
        console.log(xm.study);
        xm.sing();
        var obj = {
     
            a: 1
        };
        // console.log(obj.prototype); //非函数没有此属性

4、对象模型

对象的原型
    每个对象都默认有一个 __proto__的属性, 它的值是一个对象, 默认指向创建这个对象的构造函数的原型

对象原型的访问特点
     每个对象访问__proto__下的所有属性和方法,可以省略__proto__

对象上读取属性的顺序
      先从自身的属性上进行查找,如果没有再去__proto__属性指向的对象上去查找

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O6w9IGIo-1614155392507)(E:\黑马培训\前端基础\案例练习\就业班\笔记\JS高级\JS高级第二天\images\img4.png)]

5、constructor构造函数

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

6、原型链

每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4q8gDGJ-1614155392508)(E:\黑马培训\前端基础\案例练习\就业班\笔记\JS高级\JS高级第二天\images\img5.png)]

原型链理解(面试常问)

每个实例对象( object )都有一个私有属性(称之为 proto )指向创建它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象( proto ) ,层层向上直到一个对象的原型对象为 null。

7、原型链和成员的查找机制

对象访问属性时遵从原型链的顺序

​ (1) 当访问一个对象的属性时,首先从这个对象自身进行查找

​ (2) 如果没有找到就从它的原型对象(proto)中查找

​ (3) 如果还没有找到,继续沿着对象的原型链层层向上查找,直到最末尾的 null

8、原型对象中this指向

构造函数中的this和原型对象的this,都指向我们new出来的实例对象

// 非函数中this指向window
        // 函数中this指向函数调用者
        var that;
        function Student(uname, age) {
     
            // 函数中的 this 默认指向函数的调用者
            this.uname = name;
            this.age = age;
        }
        // 每个函数默认都有一个 propotype 属性, 它的值默认是一个对象
        Student.prototype.sing = function() {
     
            that = this;
            console.log('哈哈哈哈');
        }
        Student.prototype.sleep = function() {
     
            console.log(this === Student.prototype);
            console.log('去睡觉');
        }
        var xm = new Student('小明', 15);
        xm.sing();
        console.log(that === xm);

        // sing 中this指向调用者Student.prototype
        Student.prototype.sleep();

9、原型链的应用:拓展内置对象上自定义方法

var arr = [1, 2, 3]
        console.log(arr.__proto__ === Array.prototype);
        arr.push(4);

        // 1. 需求:修改数组 Array 上的原型对象,添加自定义求和的方法
        Array.prototype.getSum = function() {
     
            console.log(this); //指向arr=[1,2,3,4]
            var sum = 0;
            for (var i = 0; i < this.length; i++) {
     
                sum += this[i];
            }
            return sum;
        }
        console.log(arr.getSum());

拓展原型对象上的方法时,不能直接覆盖原来的原型对象,只能添加或和修改方法 注意:内置对象(如:Object, Array, Function) 上的原型对象不允许进行覆盖,只允许修改

二、继承

1、call()

  • 使用:fn.call([thisArg, arg1, arg2…])
  • 作用:调用一个函数,并指定调用时 this 的值
  • 参数: thisArg 函数中 this 的指定值; arg1, arg2…可选的参数列表
  • 返回值:函数调用的结果
  • 注意:第一个参数 thisArg 如果不传或是 null、undefined,默认函数内 this 指向 window

2、子构造函数继承父构造函数中的属性

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
// 子类继承父类属性的核心: 使用 call() 把父类的实例对象 this 手动修改为子类的实例对象 this
        function People(name, age) {
     
            console.log(this); //this指向父类创建的实例对象 
            this.name = name;
            this.age = age;
        }

        function Student(name, age) {
     
            // this指向子类创建的实例对象
            People.call(this, name, age);
        }
        var xm = new Student('小明', 15);
        console.log(xm);

3、借用原型对象继承方法

  1. 先定义一个父构造函数
  2. 再定义一个子构造函数
  3. 子构造函数继承父构造函数的属性(使用call方法)
  //  子类继承父类上方法的核心: 新建一个对象作为子类的原型对象, 并把这个对象的原型指向父类的 prototype
        function People(name, age) {
     
            console.log(this); //this指向父类创建的实例对象 
            this.name = name;
            this.age = age;
        };
        People.prototype.say = function() {
     
            console.log('hello');
        };

        function Student(name, age, subject) {
     
            // this指向子类创建的实例对象
            People.call(this, name, age);
            this.subject = subject;
        };
        // 三种方法可以调用父类方法:
        // (1)直接把父构造函数的原型拿过来用,但是会导致子类添加的方法,父类也会有
        Student.prototype = People.prototype;

        // 2) 不能把两个原型放在一个空间, 需要创建一个新的内存空间
        var obj = {
     
            constructor: Student, //把constructor指向子构造函数
            __proto__: People.prototype, //通过原型链,把父构造函数的原型直接拿过来;
        }
        Student.prototype = obj;

        // (3)实例化父级的构造函数
        Student.prototype = new People();
        Student.prototype.constructor = Student;

        // 子类构造函数原型上添加新的方法
        Student.prototype.sleep = function() {
     
            console.log('去睡觉啊');
        }
        var xm = new Student('小明', 15, '语文');
        xm.say(); //xm直接访问父构造函数原型上的方法;
        console.log(xm);
        console.log(People.prototype); //检测父构造函数上的原型是否被子构造函数修改
        console.log(xm.constructor); //检测xm是谁创建出来的

4、类的本质

  1. 构造函数默认有一个 prototype 属性, 它的值是一个对象
  2. 构造函数 prototype 属性下有 constructor 和 proto 两个默认的属性
  3. 构造函数可以通过在 prototype 上添加方法, 去实现所有实例继承
  4. 构造函数创建的实例上 __proto__指向创建它构造函数的 prototype
  5. class 类的本质其实就是一个改造版的构造函数

三、ES5新增数组方法

1、数组方法forEach遍历数组

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:无
  • 例子: 使用 forEach 对数组求和
 var arr = [10, 12, 55, 13]
arr.forEach(function(item, index, arr) {
     
            console.log('索引号:' + index + '===>' + '数组元素:' + item);
        })

2、filter() 方法从数组筛选出符合条件的所有元素

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:新数组
  • 例子: 使用 filter 筛选数组中所有偶数
  var arr2 = [1, 2, 3, 4, 5, 6];
 var newArr = arr2.filter(function(item,index) {
     
       // 如果return后面的结果为真,就返回到新数组中
	return item % 2 == 0;
        })
        console.log(newArr);

3、some() 方法用于查找数组中是否有符合条件的某一个元素

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:true(找到) || false(找不到)
 var arr = ['hello', '12', ' ', 'true', '']
       var a = arr.some(function(item, index){
     
            return item == '';
        })
        console.log(a);

4、 every() 方法用于查找数组中是否所有的元素都符合条件

  • 参数:callback(元素, 索引, 数组本身)
  • 返回值:true || false (只要有一个不满足就返回 false)
var arr3 = [30, 22, 45, 16, 20];
        var b = arr3.every(function(item, index) {
     
            return item > 20;
        })
        console.log(b);
        // 这些方法本质上都是存在原型上 Array.prototype;
        console.log(arr3.__proto__ === Array.prototype); //true

5、trim方法去除字符串两端的空格

var str = '   hello   '
console.log(str.trim()//hello 去除两端空格
var str1 = '   he l l o   '
console.log(str.trim()//he l l o  去除两端空格

6、获取对象的属性名

Object.keys(对象) 获取到当前对象中的属性名

  • 返回值是所有属性名组成的数组
  var obj = {
     
            a: 1,
            b: 2,
            c: 3
        };
        for (var key in obj) {
     
            console.log(obj[key]);
        }
        // 1. 使用 Object.keys() 方法获取对象上所有的属性名
        // 返回值:所有属性名组成的数组
        var res = Object.keys(obj);
        console.log(res);

        // 2. 新遍历对象的方法
        res.forEach(function(item) {
     
            console.log(item); //获取的是属性名
            console.log(obj[item]); //获取属姓名对应的值,注意都是变量,要用[]
        })

7、Object.defineProperty() 方法

  • 作用:在对象上定义一个新属性,或者修改原属性,并返回对象

  • 使用方法:

    Object.defineProperty(obj, prop, descriptor)

    ​ (1) obj: 要定义属性的对象

    ​ (2) prop: 要定义或修改的属性的名称, 字符串 ‘name’

    ​ (3) descriptor: 要定义或修改的属性描述符, 数据格式是对象 {}

  • 属性的描述符:

    (1) value: 属性值

    (2) writable: 是否可以修改

    (3) enumerable: 是否可以枚举(遍历到)

    (4) configurable: 是否可以删除或再次修改特性

    ​ 三个属性描述符默认都是 false

var obj = {
     
            name: '张三',
            age: '12',
            gender: 'man'
        }
        Object.defineProperty(obj, 'height', {
     
            value: '170',
            writable: true, //属性可以修改
            enumerable: true, //可以遍历到
            configurable: true //可以再次修改
        })
        obj.height = '175';
        console.log(Object.keys(obj));
        // 删除属性  delete;
        delete obj.height;
        console.log(obj.height);

你可能感兴趣的:(JavaScript高级,javascript,html5,html,前端)