Javascript 构造函数和类
1.构造函数
- 含义:所谓”构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构
- 写法:构造函数的名称一般都是首字母大写,用来表明这是一个构造函数,其内部通过this给实例挂载属性和值,通过关键字new调用该方法,来生成一个对应的对象
<script>
// 创建构造函数
function Person(name,age){
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
// 创建实例
var p1 = new Person("张三",20)
var p2 = new Person("李四",18)
</script>
2.关键字new
使用new命令时,它后面的函数依次执行下面的步骤:
- 创建一个空对象,作为将要返回的对象实例
- 将这个空对象的原型,指向构造函数的prototype属性
- 将这个空对象赋值给函数内部的this关键字
- 开始执行构造函数内部的代码
<script>
// 创建构造函数
function Person(name,age){
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
// 模拟new关键字(传入构造函数和其参数)
function _new(){
//所有参数
var args = Array.from(arguments)
//目标构造函数(第一个参数),剩下的args就是参数
var constructor = args.shift()
// 创建一个要返回对象,并继承构造函数的 prototype 属性
var context = Object.create(constructor.prototype)
//执行构造函数,并绑定this,传入参数
var result = constructor.apply(context,args)
//判断构造函数是否有返回值,且是对象结构,如果是,则返回构造函数原来的返回值,否则返回当前创建的对象context
if(typeof result === 'object' && result != null){
return result
}else{
return context
}
}
//创建实例
var p1 = _new (Person,"张三",20)
var p2 = _new (Person,"李四",18)
console.log(p1) //Person {name: '张三', age: 20}
console.log(p2) //Person {name: '李四', age: 18}
</script>
- 如果没有使用new进行调用,构造函数就变成了普通函数,并不会生成实例对象,且此时的this指向全局,对this的属性赋值会污染全局变量
<script>
//全局变量
var name = "张三"
var age = 20
// 创建构造函数
function Person(name,age){
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
//不使用new直接调用构造函数
Person("李四",18)
//全局变量被污染
console.log(name,age) //李四 18
</script>
- 为了防止这种情形,可以在构造函数第一行加上"use strict",这样的话,一旦忘了使用new命令,直接调用构造函数就会报错
<script>
// 创建构造函数
function Person(name,age){
"use strict"
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
</script>
- 判断构造函数是否由new调用:在函数内部,通过new.target的值来判断,如果直接调用,则返回undefined,否则返回构造函数本身
<script>
// 创建构造函数
function Person(name,age){
//输出new.target
console.log(new.target)
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
//不使用new直接调用构造函数
Person("李四",18) //undefined
//使用new直接调用构造函数
new Person("李四",18) //=>Person
</script>
3.原型链
- 在JavaScript中,所有数据(对象)都是居于构造函数构建的,这些构建的实例之间的数据都是彼此独立的,JavaScript需要一个机制来让这些实例共享一些数据和方法,以此节省资源和性能,这个机制就继承
- JavaScript 规定,所有对象都有自己的原型对象(prototype,只有函数才可以直接访问并修改这个属性,普通对象不能直接访问),指向一个对象,凡事通过它创建的实例就可以以继承的形式访问这个属性,以此达到各个实例共享的效果
- 所有对象都自己的原型对象(prototype),而且任何一个对象,都可以充当其他对象的原型,彼此引用嵌套,因此,就会形成一个“原型链”(prototype chain),通俗的说,就是构造函数可以指定其他对象为自己的prototype,以此来访问这个对象乃至这个对象上一级原型的属性和方法
- 构造函数的prototype默认有一个属性constructor指向其自身,且可以对prototype进行操作,以此扩展子类的功能,所以通过他创建的实例都可以访问这个prototype上面的属性和方法
<script>
// 创建构造函数
function Person(name,age){
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
// 创建实例
var p1 = new Person("张三",20)
var p2 = new Person("李四",18)
// 访问实例属性
console.log(p1.name,p1.age) //张三 20
console.log(p2.name,p2.age) //李四 18
// 添加原型链属性/方法(所有实例共享)
Person.prototype.sayHello = function(){
console.log(`我的名字是${this.name},今年${this.age}岁`)
}
// 访问原型链方法
p1.sayHello() //我的名字是张三,今年25岁
p2.sayHello() //我的名字是李四,今年23岁
// 类方法/静态属性(只能由构造函数本身访问)
Person.title = "这是构造函数的标题"
console.log(Person.title) //"这是构造函数的标题"
console.log(p1.title) //undefined
console.log(p2.title) //undefined
// 动态创建实例方法(与原型链方法重名)
p1.sayHello = function(){
console.log("我是p1的sayHello()")
}
// 优先从实例属性中读取
p1.sayHello() //我是p1的sayHello()
// p2没有相关实例属性,所以访问原型链方法
p2.sayHello() //我的名字是李四,今年23岁
</script>
- prototype和constructor属性的关系:构造函数自带prototype,而prototype默认的constructor属性执行构造函数本身
<script>
// 创建构造函数
function Person(name,age){
// 挂载在this上面的都是实例属性
this.name = name
this.age = age
}
//构造函数的prototype默认带constructor属性,执行其自身
console.log(Person.prototype.constructor) //==>Person
//添加原型链方法
Person.prototype.sayHello = function(){
console.log(`我的名字是${this.name},今年${this.age}岁`)
}
//创建实例
var p1 = new Person("张三",20)
//输出其构造函数
console.log(p1.constructor) //==>Person
//p1本身没有constructor这个属性
console.log(p1.hasOwnProperty("constructor")) //false
//指向同一个地址
console.log(Person.prototype.constructor === p1.constructor) //true
//结论:p1本身没有constructor这个属性,接着遍历原型链,在其构造函数的原型中找到了这个constructor进行访问
//强制修改构造函数原型链
Person.prototype.constructor = "new - constructor"
//跟着发生改变
console.log(p1.constructor) //new - constructor
</script>
4.构造函数的继承
- 继承实例属性
- 继承原型链
<script>
// 创建父类构造函数
function Person(name = "",age = 0){
//保存参数
this.name = name
this.age = age
}
//原型方法
Person.prototype.sayHello = function(){
console.log(`我的名字是${this.name},今年${this.age}岁`)
}
// 创建继承Person的构造函数
function Student(name,age,school){
// 挂载实例属性
this.school = school
// 调用Person()并强制绑定this,为实例挂载属性
Person.call(this,name,age)
}
// 绑定原型链为Person的匿名实例
//Student.prototype = new Person()
//或者用下面这两句
//Object.create() 方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
// 为Student添加新的原型方法
Student.prototype.motto = function(){
console.log("好好学习,天天向上")
}
// 创建实例
var s1 = new Student("李四",25,"蓝翔")
// 访问实例属性
console.log(s1.name,s1.age,s1.school) //李四 25 蓝翔
// 访问Person原型上的方法
s1.sayHello() //我的名字是李四,今年25岁
// 访问Student原型上的方法
s1.motto() //好好学习,天天向上
</script>
5.class类
- 由来:在ES5的构造函数中,原型链属性和方法,类方法需要写在构造函数外部,这样的写法不够清晰明了,也不便于管理维护,所以在ES6中诞生了class类的概念
- 含义:class类只是一个语法糖,它的绝大部分功能,ES5 都可以做到,但是他的写法更加清晰、更像面向对象编程,一个类(构造函数)的所有代码都放入同一段代码块中,更加容易管理维护,他的特点如下:
(1)通过class进行声明,在类内部默认的constructor()方法中设定实例属性
(2)类里面的其他方法和属性一律默认挂载到原型链中,供所有实例访问,如果是静态属性,则使用static关键字进行声明
(3)类的数据类型就是函数,类本身就指向构造函数
<script>
// 创建一个类
class Person{
// constructor内部的数据是每个实例有独有一份
constructor(name,age){
this.name = name
this.age = age
}
// 原型链方法
sayHello(){
console.log(`我的名字是${this.name},今年${this.age}岁`)
}
// 静态方法(只能由类本身访问)
static foo(){
console.log("我是类的静态方法")
}
// 静态属性(只能由类本身访问)
static title = "这是类的标题"
}
// 创建实例
var p1 = new Person("张三",20)
var p2 = new Person("李四",18)
// 访问实例属性
console.log(p1.name,p1.age) //张三 20
console.log(p2.name,p2.age) //李四 18
// 修改实例属性
p1.age = 25
p2.age = 23
console.log(p1.name,p1.age) //张三 25
console.log(p2.name,p2.age) //李四 23
// 访问原型链方法
p1.sayHello() //我的名字是张三,今年25岁
p2.sayHello() //我的名字是李四,今年23岁
// 类方法/静态属性(只能由类本身访问)
Person.foo() //"我是类的静态方法"
console.log(Person.title) //"这是类的标题"
console.log(p1.title) //undefined
console.log(p2.title) //undefined
// 类的本质
console.log(typeof Person) //function
console.log(p1.sayHello === Person.prototype.sayHello) //true
</script>
6.extends 继承
- 含义:在创建类的时候,可以继承其他其他类的属性和方法,它有以下几个特点:
(1)通过关键字 extends 进行继承,且在constructor()中必须调用super()方法
(2)子类可以设定可以的原型链方法,还可以访问父类的原型链方法
<script>
// 创建一个类
class Person{
// constructor内部的数据是每个实例有独有一份
constructor(name,age){
this.name = name
this.age = age
}
// 原型链方法
sayHello(){
console.log(`我的名字是${this.name},今年${this.age}岁`)
}
// 静态方法(只能由类本身访问)
static foo(){
console.log("我是类的静态方法")
}
// 静态属性(只能由类本身访问)
static title = "这是类的标题"
}
// Student类继承Person
class Student extends Person {
// name,age,school就是创建时传入的参数
constructor(name,age,school){
// super()是父类的构造器,将name和age传递给他
super(name,age)
// 添加新的实例属性,接收school参数(学生这个类有自己的实例属性)
this.school = school
}
// 添加原型链方法
motto(){
console.log("好好学习,天天向上")
}
// 添加类属性
static title = "我是Studnet类的标题"
}
// 创建实例
var stu1 = new Student("小明",16,"蓝翔")
var stu2 = new Student("小强",17,"新东方")
// 访问实例属性
console.log(stu1.name,stu1.age,stu1.school) //小明 16 蓝翔
console.log(stu2.name,stu2.age,stu2.school) //小强 17 新东方
// 修改实例属性
stu1.age = 20
stu2.age = 21
console.log(stu1.name,stu1.age,stu1.school) //小明 20 蓝翔
console.log(stu2.name,stu2.age,stu2.school) //小强 21 新东方
// 访问原型链方法
stu1.sayHello() //我的名字是小明,今年20岁
stu2.sayHello() //我的名字是小强,今年21岁
stu1.motto() //好好学习,天天向上
stu2.motto() //好好学习,天天向上
// 类方法/静态属性(只能由类本身访问)
console.log(Student.title) //"我是Studnet类的标题"
console.log(stu1.title) //undefined
console.log(stu2.title) //undefined
// 类的本质
console.log(typeof Student) //function
console.log(stu1.motto === Student.prototype.motto) //true
// 子类的原型链指向了父类
console.log(stu1)
// Student {name: "小明", age: 20, school: "蓝翔"}
// age: 20
// name: "小明"
// school: "蓝翔"
// __proto__: Person
</script>
Javascript 构造函数和类的更多相关文章
- JavaScript面向对象编程(二)构造函数和类
new关键字和构造函数 在文章JavaScript面向对象编程(一)原型与继承中讨论啦JavaScript中原型的概念,并且提到了new关键字和构造函数.利用new关键字构造对象的实例代码如下: // ...
- 一、javascript中的类
1.找出对象的构造器----constructor/instanceof constructor是用模版实例化对象的时候附带的一个额外属性,这个属性指向创建该对象时所使用的javascript构造函数 ...
- 玩转JavaScript OOP[2]——类的实现
概述 当我们在谈论面向对象编程时,我们在谈论什么?我们首先谈论的是一些概念:对象.类.封装.继承.多态.对象和类是面向对象的基础,封装.继承和多态是面向对象编程的三大特性. JavaScript提供了 ...
- javascript基础知识-类和模块
在JavaScript中可以定义对象的类,让每个对象都共享这些属性. 在JavaScript中,类的实现是基于其原型继承机制的.如果两个实例都从同一个原型对象上继承了属性,我们就说它们是同一个类的实例 ...
- javascript 构造函数方式定义对象
javascript是动态语言,可以在运行时给对象添加属性,也可以给对象删除(delete)属性 <html> <head> <script type="tex ...
- javascript继承(一)—类的属性研究
本篇文章主要针对javascript的属性进行分析,由于javascript是一种基于对象的语言,本身没有类的概念,所以对于javascript的类的定义有很多名字,例于原型对象,构造函数等,它们都是 ...
- JavaScript 构造函数
关于JavaScript构造函数,如今出现了很多JavaScript的框架,例如jQuery.Ext等等这些,这些将JavaScript作为一种面向对象的语言进行编程,那么JavaScript到底是怎 ...
- Javascript学习6 - 类、对象、继承
原文:Javascript学习6 - 类.对象.继承 Javasciprt并不像C++一样支持真正的类,也不是用class关键字来定义类.Javascript定义类也是使用function关键字来完成 ...
- JavaScript中的类
JavaScript类的相关知识 1.例子 /* 例1 */// 定义一个构造函数function Range(from, to){ this.from = from; this.to = ...
- javascript构造函数及原型对象
/** @ javascript中没有类的概念,所以基在对象创建方面与面向对象语言有所不同* @ 对象创建的常用方法及各自的局限性* @ 使用Object或对象字面量创建对象* @ 工厂模式创建对象* ...
随机推荐
- 数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪
数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪 随着全球对可持续发展和产品透明度的关注日益增加,企业需要一种可靠的方法来跟踪和管理产品生命周期中的关键数据.我们 ...
- flink 大批量任务提交 yarn 失败问题
问题现象 用户迁移到新集群后,反馈他们开发平台大量 flink 任务提交失败了,当时集群的 yarn 资源是足够的 排查过程 用户是在他们的开发平台上提交的,查看他们失败的任务,发现是他们提交端主动 ...
- Java日期时间API系列34-----Jdk8中java.time包中的新的日期时间API类应用,使用Period一行代码计算生日。
通过Java日期时间API系列9-----Jdk8中java.time包中的新的日期时间API类的Period和Duration的区别中得知,Period可以比较2个日期相差的年月日.年龄计算是2个日 ...
- Pytorch 基于加权平滑过渡的无缝拼接
基于加权平滑过渡的无缝拼接 背景 在做照片数字人视频生成的时候,为了达到快速响应实时播放的需求,即视频的生成速度 必须小于 音频的播放速度. 因此,我们截取了一部分较小的可动区域进行推理生成,然后把生 ...
- dotnet 委托delegate的使用 定义和使用
void Main() { // 委托 - 初级和高级的分水岭 // 1. 委托的初体验 // 委托是一个引用类型,其实是一个类型,保存方法的指针(地址) (变量名字都是地址 都是指针) // 是一个 ...
- 64.element表单校验注意点
<!-- 表单验证三要素: --> <!-- ① el-form需要有 model属性[表单数据对象].rules属性[验证规则对象].ref属性[引用字符串] --> < ...
- npm install报错 SyntaxError: Unexpected end of JSON input while parsing near '...=GmVg\r\n-----END PGP'
解决方法: npm cache clean --force 然后重新执行:npm install即可
- 基于surging的木舟平台如何上传模块热部署
一.概述 通过3个月的赶工,基本上快完成1.0版本的研发,将在下个月发布社区1.0版本. 木舟 (Kayak) 是什么? 木舟(Kayak)是基于.NET6.0软件环境下的surging微服务引擎进行 ...
- pandas的一些基本操作
Pandas 是一个开源的数据分析和操作库,它是 Python 编程语言的一个扩展.Pandas 提供了快速.灵活和表达能力强的数据结构,旨在使数据清洗和分析工作变得更加简单易行. 1.为什么要学习p ...
- 《使用Gin框架构建分布式应用》阅读笔记:p251-p271
<用Gin框架构建分布式应用>学习第14天,p251-p271总结,总21页. 一.技术总结 1.Docker & Docker Compose version: "3. ...