创建对象的方式:
- 字面量: var a = {};
- new: function F() {}; var a = new F();
- Object方法:var a = Object.create({a: 1}); 意思是a的原型是{a: 1}
对象的属性说明(对象没有prototyte属性):
例子: function F() {}; var a = new F();
- a.constructor = F:对象、构造函数实例,都有constructor 属性,默认指向:它构造函数.prototyte.constrctor ; 构造函数的 prototype 对象 constrctor 属性指向本身;
- a.__proto__ = F.prototyte: 对象有一个隐藏的__proto__属性(原型链),指向它的构造函数的prototype属性
- F.prototyte.constrctor = F; 构造函数的 prototype 的 constrctor 指向本身
函数对象:
- 有原型对象prototype(普通对象没有prototype) 函数实例的__proto__属性指向构造函数的prototype;
- 构造函数的 prototype 属性的 constructor 属性指向本身;
- 有__proto__ 隐藏属性, Array.__proto__ 指向 Function.prototype (Function.prototype 比较特殊,是函数)
new一个函数做了什么:
例子: function F ( name ) { this.name = name ; } new F();
1:创建一个新的空对象a
2:将函数的this指向a对象
3:将a对象的__proto__成员指向了构造函数F的prototype对象
3:运行该F函数
4:如果F函数没有返回值,或者返回的不是对象, 那么就返回a对象
JS继承的方式
1. 原型链继承
原理:
1.子类的原型指向父类的实例;
2.将子类原型上的constructor属性指向本身;
3.错误的做法:Child.prototype = Parent.prototype;这样只能继承Parent.prototype上的属性,Parent方法本身的属性无法继承;
缺点:
1.无法多继承;
2.原型对象所有属性都是共享,不小心修改后会影响所有子类;
3.创建子类,无法向父类构造器传参
父类:
function Parent () {this.name = 'Parent'; this.sex = 'boy';};
Parent.prototype.getName = function () { return this.name; }
子类:
function Child () { this.name = 'child' }
继承:
Child.prototype = new Parent();
Cat.prototype.constructor = Cat;(不加这一行:Cat.prototype.constructor 是 Animal,导致所有子类的constructor都指向Animal)
var c = new Child();
child1.getName() // child
解析:
c1
通过构造函数Child
生成的对象,就有属性name
,并且属性值也是自己的child
Child
的原型被指向了父类构造函数Parent
创建出来实例,就可以使用实例的所有方法以及属性,child1.getName()就生效;
2. 构造函数继承(call或者apply)
原理:
在子类构造函数内部使用
call或apply
来调用父类构造函数;例子:
function Parent (name) { this.name = name }function Child () {this.sex = 'boy'Parent.call(this, ...arguments)}优缺点:
1. 可以解决原型链继承的3个缺点(无法多继承,无法向父类构造函数传参,子类共享原型对象属性)
2. 但是无法继承父类原型上的方法及属性(原型链继承可以)
3.组合继承
概念:组合继承就是将原型链继承与构造函数继承组合在一起,从而发挥两者之长的一种继承模式
实现:
通过
call/apply
在子类构造函数内部调用父类构造函数将子类构造函数的原型对象指向父类构造函数创建的一个匿名实例
修正子类构造函数原型对象的
constructor
属性,将它指向子类构造函数例子:
function Parent (name) {
this.name = name
}
function Child (name, sex) {
this.sex = 'boy'
Parent.apply(this, [name])
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
优点:
可以继承父类实例属性和方法、父类原型属性和方法
解决原型链继承中引用属性的共享的问题(父级的原型链Parent.prototype上的属性还是共享)
可向父类构造器传参
缺点:
- 父类构造函数会被调用两次
- 生成了两个实例,浪费内存
4. 寄生组合继承 - 最屌的继承方式
与组合继承区别就在于:
组合:Child.prototype = new Parent();Child.prototype.constructor = Child
寄生:Child.prototype = Object.create(Parent.prototype)
解释Object.create:
使用了
Object.create(Parent.prototype)
创建了一个空的对象,并且这个对象的__proto__
属性是指向Parent.prototype
Object.create(null)创建的对象,它的
__proto__
属性设置为null,相当于是没有原型链了,连
Object.prototype
上的方法它都不能用了(toString、hasProperty)自己实现Object.create:
function create(proto) {
function F(){}
F.prototype = proto;
F.prototype.constructor = F;
return new F();
}
优点:
只调用了一次父类构造函数
5. es6 class 继承
在class
中继承主要是依靠两个东西:效果和之前我们介绍过的寄生组合继承方式一样(就是那个最屌的继承方式)
extends
super
6. 多继承
原理:
- 使用Object.create继承多个父类原型属性
- 构造函数内部使用apply调用多个父类的实例方法
例子:
function Parent() { }
function OtherParent() { }
function Child(...arg) {
Parent.apply(this, arg)
OtherParent.apply(this, arg)
}
Child.prototype = Object.create(Parent.prototype)
Object.assign(Child.prototype, OtherParent.prototype)
Child.prototype.constructor = Child
参考:https://juejin.im/post/5e75e22951882549027687f9#heading-36