当前位置: 首页 > news >正文

js构造函数和原型对象,ES6中的class,四种继承方式

一、构造函数

1.构造函数是一种特殊的函数,主要用来初始化对象
2.使用场景

常见的{...}语法允许创建一个对象。可以通过构造函数来快速创建多个类似的对象

 const Peppa = {name: '佩奇',age: 6,sex: '女'}const George = {name: '乔治',age: 3,sex: '男'}const Mum = {name: '佩奇妈妈',age: 30,sex: '女'}const Dad = {name: '佩奇爸爸',age: 32,sex: '男'}function Pig(name, age, sex) {this,name = name;this.age = age;this.sex = sex;}const Peppa1 = new Pig('佩奇', 6, '女')const George1 = new Pig('乔治', 3, '男')const Mum1 = new Pig('佩奇妈妈', 30, '女')const Dad1 = new Pig('佩奇爸爸', 32, '男')

构造函数在技术上是常规函数

命名以大写字母开头。

通过new关键字来执行。

3.创建构造

使用new关键字调用函数的行为被称为实例化

实例化构造函数时没有参数可以省略()

构造函数内部无需写return,返回值即为新创建的对象

构造函数内部的return返回的值无效,所以不需要写return

new Object()和new Date()也是实例化的构造函数

4.实例化执行过程!

a.创建新空对象,

b.构造函数this指向新对象,

c.执行构造函数代码,修改this,添加新的属性,

d.返回新对象

5.实例成员和静态成员

实例成员:通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员(实例属性和实例方法)

为构造函数传入参数,创建结构相同但值不同的对象

构造函数创建的实例对象彼此独立互不影响

    //实例对象上的属性和方法属于实例成员function Pig(name) {this.name = name;}const peiqi = new Pig('佩奇')const qiaozhi = new Pig('乔治')peiqi.name = '小猪佩奇' //实例属性peiqi.sayHi = () => { //实例方法console.log('hi~');}console.log('peiqi', peiqi);console.log('qiaozhi', qiaozhi);

构造函数的属性和方法称为静态成员(静态属性和静态成员)

静态成员只能通过构造函数来访问

静态方法中的this指向构造函数

    //构造函数上的属性和方法称为静态成员function Pig(name) {this.name = name;}Pig.eyes = 2 //静态属性console.log('Pig.eyes', Pig.eyes);Pig.sayHi = function() { //静态方法console.log(this);}console.log('Pig.sayHi', Pig.sayHi);

6.内置构造函数

引用类型Object,Array,RegExp,Date等

包装类型String,Number,Boolean等

Object的静态方法Object.keys(),Object.values(),Object.assign()

    const o = {name: '佩奇',age: 6}//获取所有属性名,返回数组console.log(Object.keys(o));//获取所有属性值,返回数组console.log(Object.values(o));//拷贝对象const oo = {}Object.assign(oo, o)//给对象添加属性console.log(oo);Object.assign(o, {gender: '女'})console.log(o);

二、编程思想

1.面向过程

分析解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用。按照分析好了的步骤,按照步骤解决问题。

优点:性能比面向对象高,适合和硬件联系很紧密的东西

缺点:没有面向对象易维护、易复用、易扩展

2.面向对象

把食物分解成一个一个对象,然后由对象之间分工与工作。

以功能来划分问题,而不是步骤。

面向对象编程(oop):在面向对象程序开发中,每一个对象都是功能中心,具有明确分工。面向对象编程具有灵活,代码可复用,容易维护和开发的优点,更适合多人合作的大型软件项目。

优点:封装性,继承性,多态性。可以使系统更加灵活,更加易于维护

缺点:性能比面向过程低

js实现面向对象需要借助构造函数来实现

构造函数实例创建的对象彼此独立,互不影响。

构造函数存在浪费性能的问题
    function Star(name, age, sex) {this.name = name;this.age = age;this.sing = function() {console.log('sing');};}const ldh = new Star('ldh', 55)const zxy = new Star('zxy', 58)console.log(ldh.sing === zxy.sing); //false

三、原型

原型可以解决构造函数里浪费内存的问题。

1.目的:能够利用原型对象实现方法共享

构造函数通过原型分配的函数是所有对象所共享的。

js规定:每一个构造函数都有一个prototype属性,指向另一个对象,我们也称为原型对象。

这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节约内存。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。

    function Star(name, age, sex) {//公共属性写到构造函数里this.name = name;this.age = age;}const ldh = new Star('ldh', 55)const zxy = new Star('zxy', 58)console.log('Star.prototype', Star.prototype)//构造函数有一个prototype属性,公共方法写到原型对象身上Star.prototype.sing = function() {console.log('唱歌');};ldh.sing()zxy.sing()console.log(ldh.sing === zxy.sing); //true

构造函数和原型对象中的this都指向实例化对象。

    let a;let b;function Star(name, age, sex) {a = this;this.name = name;this.age = age;}const ldh = new Star('ldh', 55)console.log(a === ldh);//true//构造函数里面的 this就是实例对象Star.prototype.sing = function() {b = this;console.log('唱歌');};ldh.sing()console.log(b === ldh);//true
    //自己定义数组扩展方法max,min,sumArray.prototype.max = function() {//原型函数里面的this指向实例化对象arrreturn Math.max(...this)}Array.prototype.min = function() {//原型函数里面的this指向实例化对象arrreturn Math.min(...this)}Array.prototype.sum = function() {//原型函数里面的this指向实例化对象arrreturn this.reduce((sum, item) => sum + item, 0)}const arr = [1, 2, 3]console.log(arr.max());console.log(arr.min());console.log(arr.sum());
2.constructor属性

每个原型对象里面都有一个constructor属性,该属性指向该原型对象的构造函数.

    function Star() {}const ldh = new Star()console.log(Star.prototype.constructor === Star);//true

 如果有多个对象的方法,我们可以给原型对象采取对象形式赋值。

但是这样就会覆盖构造函数原型对象原来的内容,修改后原型对象constructor就不再指向当前构造函数了,我们需要在修改后的原型对象中,添加一个constructor指向原来的构造函数。

    function Star() {}// Star.prototype.sing = function() {//     console.log('唱歌');// }// Star.prototype.dance = function() {//         console.log('跳');//     }Star.prototype = {//重新指回创造这个原型对象的构造函数constructor: Star,sing: function() {console.log('唱歌');},dance: function() {console.log('跳');}}console.log(Star.prototype);

 

3.原型

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

__protp_是js非标准属性,[prototype]和__protp_意义相同

用来表明当前实例对象指向哪个原型对象prototype

__protp_对象原型里面也有一个constructor属性,指向创建该实例对象的构造函数。

    function Star() {}const ldh = new Star()//每一个构造函数都有一个prototype属性,指向另一个对象,我们也称为原型对象。console.log('Star.prototype', Star.prototype);//每个原型对象里面都有一个constructor属性,该属性指向该原型对象的构造函数.console.log(Star.prototype.constructor === Star);//true//ldh.__proto__ 指向该构造函数的原型对象console.log(ldh.__proto__ === Star.prototype); //true//对象原型里面也有constructor属性,指向创建该实例对象的构造函数。console.log(ldh.__proto__.constructor === Star); //true

总结

prototype是原型对象,构造函数都自动有原型对象

construvtor属性在原型对象(prototype)和对象原型(__proto__)上都有 ,指向创建实例对象/原型的构造函数。

__proto__属性在实例对象里面,指向原型对象。

4.原型继承

继承是面向对象编程的另一个特征,通过继承进一步提升代码封装的程度,js中大多是借助原型对象实现继承的特性。

    const Person = {head: 1,eyes: 2}function Woman() {}//Woman通过原型来继承PersonWoman.prototype = Person//指回原来的构造函数Woman.prototype.constructor = Womanconst red = new Woman()console.log('red', red);

问题:同时使用了同一个对象,根据引用类型的特点,他们指向同一个对象,修改一个都会改变。

const Person = {head: 1,eyes: 2}function Woman() {}Woman.prototype = PersonWoman.prototype.constructor = WomanWoman.prototype.sing = function() {console.log('唱歌');}const red = new Woman()console.log('red', red);function Man() {}Man.prototype = PersonMan.prototype.constructor = Manconst bob = new Man()console.log('bob', bob);

    function Person() {this.head = 1this.eyes = 2}function Woman() {}//父构造函数(父类)子构造函数(子类) 子类的原型=new父类Woman.prototype = new Person()Woman.prototype.constructor = WomanWoman.prototype.sing = function() {console.log('唱歌');}const red = new Woman()console.log('red', red);function Man() {}Man.prototype = new Person()Man.prototype.constructor = Manconst bob = new Man()console.log('bob', bob);

5.原型链

基于原型对象的继承使得不同的构造函数的原型对象关联在一起,并且这种关联的关系是一种链状结构,我们将原型对象的链状结构关系称为原型链。

    function Person() {}const ldh = new Person()console.log(ldh.__proto__);console.log(Person.prototype);console.log(ldh.__proto__ === Person.prototype); //trueconsole.log(Person.prototype.__proto__ === Object.prototype); //trueconsole.log(Object.prototype.__proto__); //nullconsole.log(ldh instanceof Person); //trueconsole.log(ldh instanceof Object); //true

原型链查找规则

a.当访问一个对象的属性时,首先查找这个对象自身有没有该属性。

b.如果没有就查找它的原型,也就是__proto__指向的prototype原型对象。

c.如果没有找到就查找原型对象的原型。

d.依次类推一直找到Object为之。

e.__proto__对象原型的意义就在于为对象成员查找机制提供一个方向。

f.可以使用instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上

四、示例

1.封装模态框 

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><button id="delete">删除</button><button id="login">登录</button>
</body>
</html><script>//构造函数封装模态框function Modal(title = '', message = '') {// 1.创建div标签// 2.给div标签添加类名modal// 3.modal盒子内部填充2个div并且修改文字内容this.modalBox = document.createElement('div')this.modalBox.className = "modal"this.modalBox.innerHTML = `<div class="header">${title}<i>x</i></div><div class="main">${message}</div>`}//挂载open方法Modal.prototype.open = function() {//准备open显示时先判断 页面中有没有modal,有就移除,没有就添加const box = document.querySelector('.modal')box && box.remove()document.body.append(this.modalBox)//等盒子显示出来,就可以绑定关闭事件this.modalBox.querySelector('i').addEventListener('click', () => {//需要用箭头函数,这个this指向实例对象this.close()})}Modal.prototype.close = function() {this.modalBox.remove()}document.querySelector('#delete').addEventListener('click', () => {const del = new Modal('温馨提示', '你没有权限删除')del.open()})document.querySelector('#login').addEventListener('click', () => {const login = new Modal('友情提示', '你没有注册')login.open()})
</script>
 2.选项卡(构造函数写法)

 

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#box div,#box1 div {width: 200px;height: 50px;background-color: palegreen;display: none;}.active {background-color: palevioletred;}#box .current,#box1 .current {display: block;background-color: palegreen;}</style>
</head><body><div id="box"><section><button class="active">娱乐</button><button>体育</button><button>教育</button></section><section><div class="current">娱乐</div><div>体育</div><div>教育</div></section></div><hr><div id="box1"><section><button class="active">娱乐</button><button>体育</button><button>教育</button></section><section><div class="current">娱乐</div><div>体育</div><div>教育</div></section></div>
</body>
<script>//构造函数function Tab(ele, type) {this.ele = document.querySelector(ele); // {ele:"元素"}this.btns = this.ele.children[0].childrenthis.divs = this.ele.children[1].childrenthis.changeColor(type); // 函数的调用  "click"}Tab.prototype.changeColor = function(type) {// this: 指向的是实例化对象的thisfor (let i = 0; i < this.btns.length; i++) {this.btns[i].addEventListener(type, () => {console.log(2);// this.btns[i]:i就可以用了,this.btns[i]代表具体点击的元素for (var j = 0; j < this.btns.length; j++) {this.btns[j].classList.remove("active")this.divs[j].classList.remove("current")}this.btns[i].classList.add("active")this.divs[i].classList.add("current")})}}// 创建对象let tab1 = new Tab("#box", "click")let tab2 = new Tab("#box1", "mouseover")</script></html>

 

五、class类

从ES6开始,JavaScript引入了类(class)的语法,可以用来创建构造函数。这种语法更加简洁和清晰。

    //写法一: 构造函数function Person(name, age) {this.name = name;   this.age = age;   }Person.prototype.say = function () {console.log("呵呵");}let p1 = new Person("zs", 10)let p2 = new Person("zs", 10)console.log(p1);console.log(p1 == p2); // false//写法二: class类的写法class Person {constructor(name, age) {this.name = name;this.age = age;}//相当于挂载在了构造函数的原型对象上, Person.prototype.say = function(){}say() {  console.log("呵呵");}}let p1 = new Person("zs", 10)let p2 = new Person("zs", 10)console.log(p1);console.log(p1 == p2); // false
选项卡(class类写法) 

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#box div,#box1 div {width: 200px;height: 50px;background-color: palegreen;display: none;}.active {background-color: palevioletred;}#box .current,#box1 .current {display: block;background-color: palegreen;}</style>
</head><body><div id="box"><section><button class="active">娱乐</button><button>体育</button><button>教育</button></section><section><div class="current">娱乐</div><div>体育</div><div>教育</div></section></div><hr><div id="box1"><section><button class="active">娱乐</button><button>体育</button><button>教育</button></section><section><div class="current">娱乐</div><div>体育</div><div>教育</div></section></div>
</body>
<script>// class类class Tab {constructor(ele, type) {this.ele = document.querySelector(ele); // {ele:"元素"}this.btns = this.ele.children[0].childrenthis.divs = this.ele.children[1].childrenthis.changeColor(type); // 函数的调用  "click"}changeColor(type) {for (let i = 0; i < this.btns.length; i++) {this.btns[i].addEventListener(type, () => {console.log(2);// this.btns[i]:i就可以用了,this.btns[i]代表具体点击的元素for (var j = 0; j < this.btns.length; j++) {this.btns[j].classList.remove("active")this.divs[j].classList.remove("current")}this.btns[i].classList.add("active")this.divs[i].classList.add("current")})}}}let tab1 = new Tab("#box", "click")let tab2 = new Tab("#box1", "mouseover")
</script></html>

 六、继承

      发生在两个构造函数之间,如果说A构造函数使用了B构造函数中的属性或者方法,A继承自B,B就属于父类,A属于子类

      可以把构造函数当成一个类

1.借用父类构造函数继承

优点:可以继承父类构造函数内的属性和方法 ​ 缺点:不可以继承父类原型对象上的属性和方法

    // 定义父类构造函数function Father(name, age) {this.name = name;this.age = age;}// 父类原型方法Father.prototype.sayHello = function() {console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);};// 定义子类构造函数function Child(name, age, grade) {// 借用父类构造函数Father.call(this, name, age); // 通过call()借用父类构造函数this.grade = grade; // 子类特有的属性}// 使用示例const child = new Child('Alice', 10, 5); //{name: 'Alice', age: 10, grade: 5}child.sayHello(); //  child.sayHello is not a function
2.原型链继承

优点:可以继承父类原型对象上的属性和方法 ​ 缺点:不可以继承父类构造函数内的属性和方法

 

    // Father类function Father(money) {this.money = money}Father.prototype.smoke = function() {console.log("吸烟");}// Son类function Son(name, money) {this.name = name;}Son.prototype = new Father(); // 会覆盖掉Son构造函数原型对象的constructor属性Son.prototype.constructor = Son; // 手动加上Son.prototype.study = function() {console.log("study");}var f = new Father(10000)var s = new Son("小张")console.log(s);s.smoke();
 3.组合继承

优点:实现函数复用,保证每个实例之间的属性不会相互影响。 ​

缺点:使用了两次继承,代码书写繁琐,还出现了constructor的覆盖

    // Father类function Father(money) {this.money = money}Father.prototype.smoke = function() {console.log("吸烟");}// Son类function Son(name, money) {this.name = name;Father.call(this, money); // 继承父类属性的关键}Son.prototype = new Father(); // 继承父类原型对象方法的关键Son.prototype.study = function() {console.log("study");}var s = new Son("小张", 1000); // 即继承了属性console.log(s);s.smoke(); // 又继承了父类原型对象上的方法
4.es6继承:extends super

优点:1.既可以继承父类的属性,也可以继承父类原型对象上的方法 ​ 2.写法简单

 

    class Father {constructor(money) {this.money = money;}smoke() {console.log('抽大烟');}}class Son extends Father {constructor(name, money) {super(money);this.name = name;}}// extends来继承父类的属性和方法,必须还需要使用 super()访问父类的构造器(构造器的代码走一遍)var son = new Son("小明", 10000)console.log(son);son.smoke()// 既可以继承父类的属性,也可以继承父类原型对象上的方法


http://www.mrgr.cn/news/55726.html

相关文章:

  • AI大模型是否有助于攻克重大疾病?
  • idea 2023 创建 springboot 项目 LTS
  • vue项目引入高德地图
  • 计算PSNR, SSIM, VAMF工具
  • Java抽象类
  • ParallelsDesktop20最新版本虚拟机 一键切换系统 游戏娱乐两不误
  • Python Flask 数据库开发
  • 提示词高级阶段学习day3.1
  • 目前最新 Reflector V11.1.0.2067版本 .NET 反编译软件
  • 【C++】拆分详解 - stack和queue
  • 03_深入理解Linux:系统组成、内核版本及文件系统详解
  • 【MySQL】索引和事务
  • JAVA继承
  • 时间数据可视化基础实验——Python实现
  • 【付费】Ambari集成Dolphin实战-002-bigtop下编译dolphin——下
  • 简述 C# 二维数据集合 List 的创建、遍历、修改、输出
  • 3. IoC 与DI
  • 数据流风格
  • 改变函数调用上下文:apply与call方法详解及实例
  • Windows 11开发:全面指南
  • Spring Boot 3 + Vue3 + Element-Plus 后台基础管理系统.zip
  • Lua中的break语句
  • 02,talend
  • C++详细笔记(四)
  • c++算法第4天
  • django5入门【03】新建一个hello界面