zl程序教程

您现在的位置是:首页 >  后端

当前栏目

21前端学习之js高级:构造函数和原型、继承、ES5中新增

JS学习前端继承 高级 21 新增 原型
2023-09-27 14:29:29 时间

一、构造函数和原型

1. 概念:

在典型的OOP语言中, 都存在类的概念, 类就是对象的模板, 对象就是类的实例. 在ES6之前, JS中并没有类的概念;
ES6, 全称ECMAScript 6.0, 2015.06发版. 但是目前浏览器的JavaScript事ES5, 大多数高版本的浏览器也支持ES6, 只不过实现了ES6的部分特性和功能;
在ES6之前, 对象不是基于类创建的, 而是用一种称为构造函数的特殊函数来定义对象和他们的特征;
创建对象可用通过以下三种方式:

  • 对象字面量; var obj1 = new Object();
  • new Object(); var obj2 = {}
  • 自定义构造函数;
    function Star(uname, age) {
    	this.uname = uname;
    	this.age = age;
    	this.sing = function() {
    		console.log(this.uname + ' 唱歌...')
    	}
    }
    
    var ldh = new Star('刘德华', 20);
    ldh.sing();	// 刘德华 唱歌...
    

2. 构造函数:

构造函数是一种特殊的函数, 主要用来初始化对象, 即为对象成员变量付初始值. 它总与new一起使用. 可以把对象中一些公共的属性和方法抽取出来, 然后封装到这个函数里面;
在JS中, 使用构造函数时要注意一下两点:

  • 构造函数用于创建某一对象, 其首字母要大写;
  • 构造函数要和new一起使用才有意义;

new在执行时会做四件事情:

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

3. 静态成员与实例成员:

JavaScript的构造函数中可以添加一些成员, 可以在构造函数本身上添加, 也可以在构造函数内部的this上添加. 通过这两种方式添加的成员, 就分别称为静态成员和实例成员

  • 静态成员: 在构造函数本身上添加的成员称为静态成员. 只能由构造函数本身访问;
  • 实例成员: 在构造函数内部创建的对象成员称为实例成员. 只能由示例化的对象来访问,通过this来添加的 ;
function Star(uname, age) {
   this.uanem = uanme;
    thia.age = age;
}
console.log(Star);
/*
ƒ Star(uname, age) {
            this.uanem = uanme;
            thia.age = age;
        }
*/
Star.sex = "男";
console.log(Star.sex);	// 男

4. 原型:

4.1 构造函数的问题:

  • 存在浪费内存的问题:
    在这里插入图片描述

4.2 原型prototype:

构造函数通过原型分配的函数是所有对象所共享的;
JavaScript规定, 每一个构造函数都有一个prototype属性, 指向另一个对象. 注意这个prototype就是一个对象, 这个对象的所有属性和方法, 都会被构造函数所拥有;
可以把那些不变的方法, 直接定义的prototype对象上, 这样所有的对象的实例就可以共享这些方法了;
一般情况下, 公共属性订到构造函数中; 公共方法放到原型对象上;

function Star(uname, age) {
    this.uanem = uname;
    this.age = age;
}
console.dir(Star);

在这里插入图片描述

function Star(uname, age) {
     this.uanem = uname;
     this.age = age;
 }
 Star.prototype.sing = function() {
     console.log("我会唱歌...");
 }
 // Star.sing(); // 错误
 var ldh = new Star("刘德华", 20);
 ldh.sing();

4.3 对象原型__proto__:

**对象都会有一个__proto__**指向构造函数的prototype原型对象, 对象之所以可以使用prototype原型对象的属性和方法, 就是因为对象有__proto__原型存在;

  • __proto__对象原型和prototype原型对象是等价的;
  • __proto__对象原型的意义就在于对象的查找机制提供一个方向, 或者说一条路线, 但是它是一个非标准熟悉感, 因此,实际开发中, 不可以使用这个属性, 它只是内部指向原型对象prototype;
console.log(ldh);

在这里插入图片描述

方法的查找规则:

  • 先看对象身上是否有方法, 如果有就执行这个对象上的方法;
  • 如果没有这个方法, 因为有__proto__的存在, 就去构造函数原型对象prototype身上找这个方法;

4.4 构造函数 constructor:

对象原型(proto)和原型对象(prototype)里面都有一个constructor属性, 被称为构造函数, 因为它指回构造函数本身;

 function Star(uname, age) {
     this.uanem = uname;
     this.age = age;
 }
 var ldh = new Star("刘德华", 20);
 console.log(ldh.__proto__);
 console.log(Star.prototype);
 console.log(ldh.__proto__.constructor);
 console.log(Star.prototype.constructor);
 // prototype使用对象赋值, 需要手动利用constructor指回原来的构造函数
 Star.prototype = {
     // 手动添加构造函数
     constructor: Star,
     sing: function() {
         console.log("我会唱歌...");
     },
     movie: function() {
         console.log("我会演电影...");
     }
 }
 console.log(ldh.__proto__);
 console.log(Star.prototype);
 console.log(ldh.__proto__.constructor);
 console.log(Star.prototype.constructor);

在这里插入图片描述

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

在这里插入图片描述

4.6 原型链:

在这里插入图片描述
JavaScript的成员查找机制:

  • 当访问一个对象的属性(或方法)时, 首先查找这个对象自身有没有这个属性(或方法);
  • 如果没有就查找它的原型(也就是__proto__指向的prototype对象);
  • 如果还没有, 就查找原型对象的原型(Object的原型对象);
  • 依次类推一直到Object为止(null);

4.7 原型对象this指向:

  • 构造函数中, 里面的this指向的是对象实例;
  • 原型对象函数里面的this指向的是实例对象;

4.8 扩展内置对象:

可以通过原型对象, 对原来的内置对象进行扩展自定义的方法. 比如: 给数组增加自定义求偶数和的功能;

console.log(Array.prototype);
// 增加自定义
Array.prototype.sum = function(){
    let sum = 0;
    for(let i = 0; i < this.length; i++){
        sum += this[i]
    }
    return sum
}
var arr = [1,2,3,5]
console.log(arr.sum());	 // 11

二、 继承:

ES6之前并没有提供extends继承. 可以通过构造函数+原型模拟实现继承, 被称为组合继承

1 call():

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

func.call(thisArg, arg1, arg2)
  • thisArg: 当前调用函数this的指向函数;
  • arg1, arg2, …: 传递的其他参数

2 借用构造函数继承父类属性:

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

// 1. 父构造函数
function Father(uname, age) {
     this.uname = uname;
     this.age = age;
 }

 // 2. 子构造函数
 function Son(uname, age) {
     Father.call(this, uname, age)
 }
 var son = new Son('张三', 10);
 console.log(son.uname);	// 张三

3. 借用构造函数继承父类方法:

// 1. 父构造函数
function Father(uname, age) {
    this.uname = uname;
    this.age = age;
}
// 如果不写在了原型对象中, 则不能被call继承
Father.prototype.money = function() {
    console.log("赚钱...");
}

// 2. 子构造函数
function Son(uname, age) {
    Father.call(this, uname, age)
}
// 如果利用对象赋值的方式修改了原型, constructor指回原来的构造函数
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.exam = function() {
    console.log("孩子要考试...");
}
var son = new Son('张三', 10);
console.log(son);
console.dir(Father);

在这里插入图片描述

三、ES5中的新增方法:

1. 数组方法:

  • 迭代方法: forEach()、map()、filter()、some()、every()

1.1 forEach:

array.forEach(function(currentValue, index, arr))
  • currentValue: 数组当前项的值;
  • index: 数组当前项的索引;
  • arr: 数组本身;

var arr = [1,2,3,4,5,6,7,8,9,0]
arr.forEach((currentValue, index, arr) => {
    console.log(currentValue);
    console.log(index);
    console.log(arr);
});

1.2 filter:

filter()创建一个新的数组, 新数组中的元素是通过检测指定数组中符合条件的所有元素,主要用于筛选数组

array.filter(function(currentValue, index, arr))
  • currentValue: 数组当前项的值;
  • index: 数组当前项的索引;
  • arr: 数组本身;
var f_arr = arr.filter((currentValue, index, arr) => {
   return currentValue % 2 == 0;
})
console.log(f_arr);	// [2, 4, 6, 8, 0]

1.3 some:

some()用于检测数组中的元素是否满足指定条件, 通俗点查找数组中是否有满足条件的值;
注意: 它返回值是布尔值, 如果查找到这个元素, 就返回true; 如果查找不到就返回false;
如果找到第一个满足条件的元素, 就终止循环,返回true; 不存在则返回false;

array.some(function(currentValue, index, arr))
  • currentValue: 数组当前项的值;
  • index: 数组当前项的索引;
  • arr: 数组本身;
var res = arr.some((currentValue) => {
   return currentValue % 2 == 0;
})
console.log(res);	// true

2. 字符串方法:

trim()会从一个字符串的两端去除空格;
trim()并不影响原字符串本身, 而是返回一个新的字符串;

str.trim()

3. 对象方法:

3.1 Object.keys():

Object.keys()用于获取对象自身的所有属性:

Object.keys()

返回一个有属性名组成的数组;

3.2 Object.defineProperty():

Object.defineProperty()定义对象中新属性或修改原有属性

Object.defineProperty(obj, prop, descripttor)
  • obj: (必须)目标对象;
  • prop: (必须) 新定义或修改的属性的名字;
  • descriptor: (必须) 以对象的形式, 目标属性所拥有的特性;
    descriptor 参数说明:
属性说明
value设置属性值, 默认为undefined
writable值是否可重写(可被遍历). 默认: false, enumerable:false
enumerable目标属性是否可以被枚举(被遍历);默认: false
configurable目标属性是否可以被删除 或 是否可以被再次修改特性; 默认: false
var obj = {
  id: 1,
    pname: "小米",
    price: 1999
}
// 添加 or 修改
Object.defineProperty(obj, 'num', {
    value: 1000
})
// 限定属性
 Object.defineProperty(obj, 'id', {
     writable: false // 不可修改, 默认enumerable: false 不可被遍历
 })
 obj.id = 3; // 不生效, 不报错
 console.log(obj);

 // enumerable
 Object.defineProperty(obj, 'address', {
     value: '北京市昌平区',
     enumerable: false   // 不可被遍历
 })
console.log(Object.keys(obj));

 // 不允许删除 或 修改特性
 Object.defineProperty(obj, 'pname', {
     configurable: false
 })
 delete obj.pname
 console.log(obj);