构造函数
-
定义:通过new函数名,来实例化对象的函数叫做构造函数
-
功能:初始化对象,特点是和new一起使用。new就是在创建对象,从无到有,构造函数就是为初始化的对象添加属性和方法
-
new的理解:new申请内存,创建对象,当调用new时,后台会隐式执行new Object()创建对象。所以通过new创建的字符串、数字是引用类型。
-
常用的构造函数
1. var arr = [] 为 var arr = new Array()的语法糖
2. var obj = {} 为 var obj = new Object()的语法糖
3. var date = new Date()
实例
var person = new Object()
JS是基于原型的面向对象语言,所有数据都可以当作对象处理,所以person是对象,
可以把它当作Object的实例。
- 实例都是对象,但对象不一定是实例
- 实例是类的具象化产品
- 简单理解
动物 -- 对象
一只狗 -- 实例
prototype原型(构造函数访问原型)
原型就是一个对象,实例会“继承”这个对象的属性。在原型上定义的属性,通过“继承”,实例也拥有了这个属性。“继承”这一行为是在new操作符内部实现的
- 构造函数内部都有一个名为prototype的属性,通过这个属性就能访问到原型 Person就是构造函数,Person.prototype就是原型
function Person(){}构造函数
- 有个构造函数,我们可以创建实例,并在构造函数的原型上创建可以继承的属性
function Person (){}
var person = new Person()
console.log(person instanceof Person)
// person是Person的实例
constructor属性(原型访问构造函数)
从上面所述,构造函数可以通过prototype访问到原型,那么原型也应该能访问到构造函数,这就是constructor属性.
Person.prototype.constrauctor = Person
proto隐式原型(实例访问原型)
实例是通过_proto_访问到原型,所以实例可以直接通过_proto_访问原型,所以
person._proto_ === Person.prototype
实例访问构造函数
person._proto_.constructor = Person
读取实例的属性
但我们要读取一个实例的属性时,如果属性在该实例中没有找到,就会循着_proto_指定的原型上去找,如果还找不到就尝试寻找原型的原型
举例
function Person(){}
Person.prototype.type = '123'
person = new Person()
var res = Relect.ownKeys(person) // 先寻找自有属性
res = person.type
console.log(res) // 打印的是原型上的属性
function Person(){}
Person.prototype.type = '123'
person = new Person()
person.type = '123'
var res = Reflect.ownKeys(person)
res = person.type
console.log(res) // 打印的是实例上的属性
原型链
当我们需要访问实例的一个非自有属性时,就会通过_proto_连接起来的一系列原型、原型的原型、直到构造函数搜索查找属性。这个搜索过程形成的链状关系就是原型链
function Parent(){}
Parent.prototype.type="123"
function Child(){}
Child.prototype = new Parent()
var p = new Child()
console.log(p.type)
JavaScript实现继承的几种方式
-
思路
- 原型链:父类的实例当作子类的原型。如此子类的原型包含父类实例定义的属性
- 构造函数:子类直接使用父类构造函数。如此子类的实例包含父类实例定义的属性
- 原型式:复制父类原型属性给子类原型。如此子类的实例包含父类实例定义的属性
- 寄生式:思路与3一样,只是利用工厂模式对复制父类原型对象进行增强
-
方式
- 原型链继承
- 构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
原型链继承
// 父类
function Parent(){
this.name = 'baba';
}
Parent.prototype.getName = function(){
return this.name
}
// 子类
function Child(){
this.age = 10;
}
Child.prototype = new Parent()
Child.prototype.getAge = function(){
return this.age
}
var instance = new Child()
console.log(instance)
缺点:
1. 来自原型对象的引用的属性是所有实例共享的
2. 创建子类实例时,无法向父类构造函数穿餐
构造函数继承
function Parent(name){
this.name = name;
this.colors = ['red', 'green'];
this.getName = function(){
return this.name
}
}
function Child(name){
Parent.call(this,name);
this.age = 20;
}
var instance = new Child('tom')
instance.colors.push('blue')
cosnole.log(instance.colors)
借用构造函数继承解决了原型链两个问题,既可以传参,也不会造成子类实例共享父类引用属性。
缺点:
1. 但这里是使用call来实现继承的,并没有通过new生成一个父类实例。无法形成同步更新。
2. 无法复用函数
3. 每次构建实例都会在实例中保留方法函数,造成内存浪费
组合继承
将原型链继承和构造函数组合到一块
function Parent(name){
this.name = name;
this.colors = ['red', ;green];
}
Parent.prototype.getName = function(){
console.log(this.name);
}
function Child(name,age){
// 继承父类实例属性
Parent.call(this.name);
// 子类实例属性
this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = child
child.prototype.getAge = function (){
console.log(this.age)
}
var instance = new Child('tom', 20)
instance.colors.push('black')
console,log(instance.colors)
缺点:调用了两次父类构造函数,一次通过call 一次通过new Parent()
原型式继承
function object(a){
function F()
F.prototype = a;
return new F()
}
var person = {
name: 'tom',
colors: ['red', 'blue']
};
var person1 = object(person);
person1.name = 'anna';
person1.colors.push('green');
cosnole.log(person1.colors);
缺点:和原型链继承一样,所有子类实例共享父类引用类型
寄生式继承
function object(a){
function F(){}
F.prototype = a;
return new F()
}
function createPerson (a){
var clone = object(a);
clone.getName = function(){}
return clone
}
var person = {
name: 'tom',
colors: ['red','blue']
},
var person1 = createPerson(person);
person1.getName();
person1.colors.push('black');
console.log(person1.colors)
缺点:
1. 和原型链式继承一样,所有子类实例共享父类引用类型
2. 和借用构造函数一样,每次创建对象都会创建一次方法
寄生组合式继承
function object(a){
function F(){}
F.prototype = a;
return new F()
}
function inheritP(Child, Parent){
var prototype = object(Parent, Child);
prototype.constructor = Child;
child.prototype = prototype
}
function Parent(name){
this.name = name;
this.colors = ['red','black'];
}
Parent.prototype.getName = function(){}
function Child(name,age){
Parent.call(this,name)
this.age = age
}
// 继承父类方法
inheritP(Child, Parent);
Child.prototype.getAge = function(){}
var instance = new Child('tom', 20);
instance.colors.push('green');
instance.getNmae();
instance.getAge()
ES6实现继承
class Parent{
constructor(name){
this.name = name;
this.colors = ['red','blue']
}
getName(){
console.log(this.name)
}
}
class Child extends Parent {
constructor(name,age){
super(name);
this.age = age
}
getAge(){
console.log(this.age)
}
}
var instance = new Child('tom', 20);
instance.getAge();
instance.getName();
instance.colors.push('black')\
本质也采用的是寄生组合式继承