构造函数
构造函数就是一个普通的函数,创建方式和普通函数没有区别,不同的是构造函数习惯上首字母大写。另外就是调用方式的不同,普通函数是直接调用,而构造函数需要使用new关键字来调用。
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.sayName = function () {
alert(this.name);
}
}
var per = new Person("孙悟空", 18, "男");
function Dog(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
var dog = new Dog("旺财", 4, "雄")
console.log(per);//当我们直接在页面中打印一个对象时,事件上是输出的对象的toString()方法的返回值
console.log(dog);
每创建一个Person构造函数,在Person构造函数中,为每一个对象都添加了一个sayName方法,也就是说构造函数每执行一次就会创建一个新的sayName方法。这样就导致了构造函数执行一次就会创建一个新的方法,执行10000次就会创建10000个新的方法,而10000个方法都是一摸一样的,为什么不把这个方法单独放到一个地方,并让所有的实例都可以访问到呢?这就需要原型(prototype)
原型
在JavaScript中,每当定义一个函数数据类型 (普通函数、类)
时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一个对象数据类型的值。
原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。
function Person(name,age){
this.name=name
this.age=age
this.sayName = function () {
alert(this.name);
}
}
改造下
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.year=2021 // 往原型上添加一点东西
Person.prototype.sayName =function(){
alert(this.name);
}
const personA=new Person()
const personB=new Person()
console.log('personA',personA , personB)
console.log(personA.hasOwnProperty('year'), 'year' in personA ) // false true
console.log(personB.hasOwnProperty('year'), 'year' in personB) // false true
// hasOwnProperty只会从实例本身上找属性, in会从实例所属类的原型身上找
console.log('personA.year',personA.year , personB.year ) // 2021 2021
原型的作用:
1:实现对象之间的数据共享。
2.在es6之前,没有class的情况下,模拟面向对象,构造函数中放私有属性,原型上放公有属性,一般放方法。
通过原型添加的方法,可以完美的解决属性与方法共享问题,从而节省了内存空间..
原型链
每一个对象数据类型(普通的对象、实例、prototype......)
也天生自带一个属性__proto__
,这个属性的值是当前实例所属类的原型对象(prototype)。
原型对象中有一个属性constructor, 它指向构造函数。
function Person() {} // 构造函数
var person = new Person() // 实例person
console.log(person.__proto__ === Person.prototype) // true
console.log(Person.prototype.constructor===Person) // true
//顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
js实例对象的原型属性
__proto__
的属性值,指向这个实例对象所属类的原型
例如 new Array 出来的数组,属于Array这个类
const xArray=new Array()
那么
xArray.__proto__ === Array.prototype // true
Array.prototype拥有的方法,array的每一个实例都会有,直接通过xArray.push()这样调用,而不需要xArray._ proto _.push,不需要这样去调用。
Array.prototype.__proto__ === Object.prototype // true
Object.prototype._proto_ // null
所谓原型链,指的就是这一条指针链!
原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。
Object.prototype._proto_ ===null
何为原型链
在JavaScript中万物都是对象,对象和对象之间也有关系,并不是孤立存在的。对象之间的继承关系,在JavaScript中是通过prototype对象指向父类对象,直到指向Object对象为止,这样就形成了一个原型指向的链条,专业术语称之为原型链。
举例说明:person → Person → Object ,普通人继承人类,人类继承对象类
当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用。如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined。
我们可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性;使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
function Person() {}
Person.prototype.a = 123;
Person.prototype.sayHello = function () {
alert("hello");
};
var person = new Person()
console.log(person.a)//123
console.log(person.hasOwnProperty('a'));//false
console.log('a'in person)//true
person实例中没有a这个属性,从 person 对象中找不到 a 属性就会从 person 的原型也就是 person.__proto__
,也就是 Person.prototype中查找,很幸运地得到a的值为123。那假如 person.__proto__
中也没有该属性,又该如何查找?
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层Object为止。Object是JS中所有对象数据类型的基类(最顶层的类),在Object.prototype上没有__proto__
这个属性。
console.log(Object.prototype.__proto__ === null) // true
所谓原型链,指的就是图中的proto这一条指针链!
原型链的顶层就是Object.prototype,而Object的原型对象的是没有原型属性的。
先找自身,找不到就沿着__proto__
一直往上找原型,直到找到最顶层的类Object,Object类没有原型了,如果还找不到就返回undefined
实战:用ES6的class定义一套对象/函数
ES6提供了class,但是这个并不是类,而是 Function 的语法糖。
目的是简化ES5里面,为了实现继承而采用的各种“神操作”。
用class来定义,结构和关系会非常清晰,再也不会看着头疼了,建议新手可以跳过ES5的实现方式,直接用ES6的方式。
我们先定义一个Base,然后定义一个Person继承Base,再定义一个Man继承Person。
也就是说,可以深层继承。
class Base {
constructor (title) {
this.title = '测试一下基类:' + title
}
baseFun1(info) {
console.log('\n这是base的函数一,参数:', info, '\nthis:', this)
}
}
class Person extends Base{
constructor (title, age) {
super(title)
this.title = '人类:' + title
this.age = age
}
personFun1(info) {
console.log('\n这是base的函数一,参数:', info, '\nthis:', this)
}
}
class Man extends Person {
constructor (title, age, date) {
super(title, age)
this.title = '男人:' + title
this.birthday = date
}
manFun3 (msg) {
console.log('jim 的 this ===', this, msg)
}
}
构造函数 constructor
打印结果很清晰的表达了,构造函数就是我们定义的class。属性
属性比较简单,统统都挂在 this 上面,而且是同一个级别。函数
函数就有点复杂了,首先函数是分级别的,挂在每一级的原型上面-
原型链
Man的实例 > Man的原型 > Person的原型 > Base 的原型 > Object 的原型。
通过__proto__
连接了起来。
Man的实例 man1,可以通过这个“链条”,找到 baseFun1,
直接用 man1.baseFun1()
即可(✔),
而不需要使用__proto__
;
man1.__ proto__.__ proto__.__ proto__.baseFun1()
(✘)
man1.__ proto__ === Man.prototype
Man.prototype .__proto__ === Person.prototype
Person.prototype .__proto__ === Base.prototype
Base.prototype .__proto__ === Object.prototype
Object.prototype .__proto__ === null
Object是js顶层类,原型链的顶端也就是Object.prototype ,Object.prototype没有
__proto__
属性