原型链继承
/*1.原型链继承*/
function parent(){
this.name = 'parent'
}
parent.prototype.getname = function(){
console.log(this.name)
}
function child(){
parent.call(this)
}
child.prototype = new parent()
var child2 = new child()
child2.getname() //parent
/*
由于new出来的是一个对象,所以必须要用对象名.key的方式访问
*/
/*
1.引用类型的属性被所有实例共享,举个例子:
*/
var child3 = new child()
child3.getname() //parent
child3.name = '3'
child2.getname() //3
child3.getname() //3
/*
在创建 Child 的实例时,不能向Parent传参
*/
function parent2(){
this.name = 'parent2'
}
function child4(){
parent2.call(this)
}
借用构造函数继承
function parent(){
this.name = ['parent','1']
}
function child(){
parent.call(this)
}
var p1 = new child()
var p2 = new child()
p1.name.push('p1')
p2.name.push('p2')
console.log(p1.name)
console.log(p2.name)
// ["parent", "1", "p1"]
// ["parent", "1", "p2"]
/*
优点:
1.避免了引用类型的属性被所有实例共享
2.可以在child中向parent传参
*/
/*
缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法。
*/
原型式继承
/*
es5 Object.create模拟实现
*/
function objectcreate(object){
function f(){}
f.prototype = object
return new f()
}
var obj = {
name : '123',
age : [17,18]
}
var p1 = objectcreate(obj)
var p2 = objectcreate(obj)
p1.name = '456'
console.log(p2.name) // 123
p1.age.push('p1')
console.log(p2.age) // [17, 18, "p1"]
//缺点:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
寄生式继承
//寄生式继承
function createobj(o){
var clone = Object.create(o)
clone.sayName = function(){
console.log('hi')
}
return clone
}
//创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。
// 缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。
组合继承
/*
原型链继承和经典继承双剑合璧。
*/
var parent = function(){
this.name = parent
this.color = ['red','blue']
}
parent.prototype.getname = function(){
console.log(this.name)
}
var child = function(name,age){
parent.call(this,name)
this.age = age
}
child.prototype = new parent()
var p1 = new child()
var p2 = new child()
console.log(p1)
console.log(p2)
//child {color: Array(2), age: undefined, name: ƒ}
//child {color: Array(2), age: undefined, name: ƒ}
//优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式
// 组合继承最大的缺点是会调用两次父构造函数。
寄生组合式继承
/*组合继承
function parent(name,color){
this.name = name
this.color = ['red','blue']
}
parent.prototype.getname = function(){
console.log(this.name)
}
function child(name,age){
parent.call(this,name)
this.age = age
}
child.prototype = new parent()
var child1 = new child('kevin', '18');
console.log(child1)
*/
/*
组合继承最大的缺点是会调用两次父构造函数。
一次是设置子类型实例的原型的时候:
child.prototype = new Parent();
一次在创建子类型实例的时候:
var child1 = new child('kevin', '18');
回想下 new 的模拟实现,其实在这句中,我们会执行:
parent.call(this, name);
在这里,我们又会调用了一次 Parent 构造函数
所以,在这个例子中,如果我们打印 child1 对象,我们会发现 child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue', 'green']。
console.log(child.prototype)
console.log(child1)
*/
/*
寄生组合式继承
*/
function parent(name,color){
this.name = 'parent'
this.color = ['red','blue']
}
function child(name, age) {
parent.call(this, name);
this.age = age;
}
function createobj(o){
function f(){}
f.prototype = o
/*
{constructor: ƒ}
constructor: ƒ parent(name,color)
__proto__: Object
*/
return new f()
}
function prototypeobj(child,parent){
var prototype = createobj(parent.prototype)
prototype.constructor = child
child.prototype = prototype
}
prototypeobj(child,parent)
var p1 = new child()
var p2 = new child()
p1.name = 'p1'
p2.name = 'p2'
console.log(p1)
console.log(p2)
//child {name: "p1", color: Array(2), age: undefined}
//child {name: "p2", color: Array(2), age: undefined}
/*
寄生组合原理:
p1的constructor为child,__proto__中的constructor为parent
createobj(parent.prototype) 这一步的功能是创建一个对象,这个对象是parent的实例,具有parent的constructor方法
prototype.constructor = child 这一步将prototype的constructor设为child函数
child.prototype = prototype 设置child的原型为prototype 这样child就继承了parent的constructor
通过prototypeobj方法,child的prototype有了两个constructor,一份是原本自己的,另一份通过__proto__ 寻找到父级的constructor
*/
/*
三步骤
首先先创建个变量
var prototype = Object.create(父类的prototype)
prototype.constructor = child(子类的constructor)
child.prototype(子类的prototype) = prototype
*/
es6 extend
class Point{
constructor(x){
this.x = x;
this.p = 2;
this.j = 3;
}
print(){
return this.x
}
m(){
return this.p
}
static b(){
console.log(this.c())
}
static c(){
return 'world'
}
}
Point.prototype.z = 2
//调用
var p1 = new Point('p1') //更es5一样,调用Point构造函数new一个实例对象
console.log(p1)
//Point {x: "p1", p: 2}
/*
constructor是该类的构造函数,即使你没有创建,系统也会自动生成一个。一般 constructor 方法返回实例对象 this ,但是也可以指定 constructor
方法返回一个全新的对象,让返回的实例对象不是该类的实例。
*/
/*
上面的代码用es5可以这样表达
function Point(x){
this.x = x;
this.p = 2;
}
Point.prototype.print = function(){
return this.x
}
*/
/*
super
*/
/*
函数时使用
super只能在子类的constructor使用,不能在别的方法调用
*/
class colorPoint extends Point{
constructor(x){
super(x) // 调用父类的constructor(x)
this.y = 2
super.y = 3
}
b(){
console.log(super.print()) //调用父类的print() this指向子类colorPoint的实例
console.log(super.m()) //调用父类的m() 结果是2
console.log(super.z) //获取父类的prototype.z属性
}
}
let p2 = new colorPoint('p2')
//colorPoint {x: "p2", p: 2}
/*
对象时使用
super.print(),将super当作一个对象使用,super指向Point.prototype,此时print就是Point.prototype.print方法
super.z 由于super指向Point.prototype,此时z就是Point.prototype.z
super.y赋值为3,这时等同于对this.y赋值为3。而当读取super.y的时候,读的是A.prototype.y 显示为undefined
*/
/*
Object.getPrototypeOf方法可以用来从子类上获取父类
*/
Object.getPrototypeOf(colorPoint) === Point //true
/*
static 静态方法
static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
1.可以被子类继承
2.只能通过类调用
3.this指本类,不指实例
4.通过this调用只能调用本类定义的static方法
*/
Point.b()
// world
colorPoint.b()
// world
/*
new.target
new.target属性允许你检测函数或构造方法是否通过是通过new运算符被调用的。
*/