原型相关概念
一、实例和构造函数的关系
实例是构造函数的具象化,由new关键字执行类之后得到的对象
类是实例的抽象化 将多个具有共同特点的对象的特点抽象成规范,这个规范叫类(构造函数)
实例 被 类 创造
二、实例对象
实例对象和普通对象
内部自带一个属性:`__proto__,这个属性是个对象类型,用来指向创建自身的类身上的`prototype`属性
三、 可被构造的函数(类)
- 每个可被new执行的函数(类),身上都有一个属性:`prototype`,这个属性是个对象类型,用来**被**将来new执行时创建出来的实例身上的隐式原型`__proto__`指向
- `prototype`也被称为显示原型
- 在`prototype`这个属性内,又有一个属性:`constructor`,用来指向当前`prototype`所属的函数
四. 对象的属性的读写规则
- 向上查找,就近原则
- 当使用对象某个属性时,会先在对象自身查找,如果有就使用,如果没有,会顺着隐式原型__proto__,继续查找,如果找到,使用,同时停止,如果还没,继续查找...直到顶层原型,还找不到,抛出undefined
- 通过实例,查找某个属性,自身没有,其实就是找自身构造函数的`prototype`,直达到找到最外层的Object,还没有就是undefined
- 如果给构造函数的`prototype`添加属性或方法,意味着,将来的实例,也可以使用
继承方式
注:js继承指的是类与类之间的继承
一、改变this指向实现继承
特点:
**1.只能继承构造函数内部的属性或方法,无法继承原型上的属性或方法
2. 简单方便易操作**
// 一、改变this指向实现继承
// 特点:
// 只能继承构造函数内部的属性或方法,无法继承原型上的属性或方法
// 简单方便易操作
function Speak(){
this.speak = function(){
console.log("这是一个:通话");
}
}
function Phone(){
Speak.call(this)
// 原本是window执行了16行this所在的函数this指向window,通过call改变this指向,22行this所在函数被26行new执行,所以这个
// this指向实例p,既16行执行时改变它this的指向为将来的实例
// 通过改变父类this指向实现继承
}
var p = new Phone();
console.log(p) //Phone {speak: ƒ}
p.speak() //这是一个:通话
注:此方法 只能继承构造函数内部的属性或方法,无法继承原型上的属性或方法**
二、原型身上属性方法的继承
方式一:
Child.prototype = Parent.prototype;
特点
对象,直接复制,浅拷贝,修改新数据,会影响老数据,
可理解为两个不同的变量名(obj1,obj2)存在栈中一样,在堆中他两对应的是一个对象a,给任意一个变量(如obj1)重新赋值(c)时obj2的值也变成了c**
// 二、实现原型身上属性方法的继承
function Parent(){}
Parent.prototype.init = function(){
console.log("hahaha ")
}
function Child(){}
var p = new Parent();
console.log(p);
console.log(p.init)
p.init()
var c = new Child();
console.log(c);
// console.log(c.init);
c.init();
// 方式一:
// 对象,直接复制,浅拷贝,修改新数据,会影响老数据,
// 可理解为两个不同的变量名(obj1,obj2)存在栈中一样,在堆中他两对应的是一个对象a,给任意一个变量(如obj1)重新赋值(c)时
// obj2的值也变成了c
Child.prototype = Parent.prototype;
// Child.prototype.init = function(){console.log("啊啊啊 ")}
方式二:
通过for-in切换成深拷贝
for(var i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i];
};
特点:
比较消耗性能,没有充分利用原型的特点,仅仅是对象的拷贝
// 方式二:还行,比较消耗性能,没有充分利用原型的特点,仅仅是对象的拷贝
function Parent(){}
Parent.prototype.init = function(){
console.log("hahaha ")
}
function Child(){}
//通过for-in切换成深拷贝
for(var i in Parent.prototype){
Child.prototype[i] = Parent.prototype[i];
};
var p = new Parent();
console.log(p);
console.log(p.init)
p.init()
var c = new Child();
console.log(c);
// console.log(c.init);
c.init();
方式三:
Child.prototype = new Parent("zhangsan")
将子类的原型设置成父类的实例,通过实例的__prototype__找到父类的prototype,更像是原型链
**特点:
隐患:new Parent()立即执行如果不传参报错,相当于多执行一次Parent的实例的创建 **
// 方式三:
// 将子类的原型设置成父类的实例,通过实例的__prototype__找到父类的prototype,更像是原型链
// 有隐患,某些参数的处理上如
function Parent(name){
this.name = name;
this.init();
}
Parent.prototype.init = function(){
var res = this.name.slice(0,3);
console.log(res);
}
function Child(n){
this.name = n;
this.init();
}
Child.prototype = new Parent("zhangsan");//隐患所在
var p = new Parent("admin");
console.log(p);
var c = new Child("root");
console.log(c);
// 隐患:
// 当parent内有属性需要参数接受时,87行 new Parent()立即执行如果不传参报错,相当于多执行一次Parent的实例的创建
// 解决思路:
// 得到一个parent的实例
// parent的实例的隐式原型 指向 Parent构造函数的显示原型
// 既能创建一个:隐式原型指向Parent构造函数的显示原型的对象
// 又不执行当前Parent函数,那就可以了
方式四:
Child.prototype = Object.create( Parent.prototype );
Object.create功能:创建一个新对象,并将这个新对象的隐式原型(proto),指向参数1
特点:趋向于完善,ES6提供的Object.create方法 更像是 原型 链
function Parent(name){
this.name = name;
this.init();
}
Parent.prototype.init = function(){
var res = this.name.slice(0,3);
console.log(res);
}
function Child(n){
this.name = n;
this.init();
}
// 方式四:趋向于完善,ES6提供的Object.create方法 更像是 原型 链
Child.prototype = Object.create( Parent.prototype );
// Object.create功能:创建一个新对象,并将这个新对象的隐式原型(__proto__),指向参数1
// var obj = Object.create( Parent.prototype );
var p = new Parent("admin");
console.log(p);
var c = new Child("root");
console.log(c);
注意:原型继承 只能继承原型身上的方法或属性,不能继承构造函数中的方法或属性
三、组合继承
特点:既能继承构造函数中的方法或属性,又能继承原型身上的方法或属性
继承构造函数中的方法或属性通过改变this指向;继承原型身上的方法或属性通过object.create
function Parent(n){
this.name = n;
}
Parent.prototype.show = function(){
console.log(this.name);
}
function Child(n){
Parent.call(this, n);
}
Child.prototype = Object.create( Parent.prototype );
var p = new Parent("admin");
p.show();
var c = new Child("root");
c.show();
四、 最常用的是ES6提供的类的继承
父类:
class Person{
// 相当于构造函数里的属性
constructor(name,age){
//属性
this.name = name;
this.age = age;
}
// 相当于父类的prototype父类的方法
sayHello(){
console.log(this.name)
}
static foo(){
console.lg()
}
// static 定义静态方法(类方法),通过类直接访问,
// 而不是通过类将来创建的实例访问
}
// Person.foo();
子类
// 固定语法Male类继承Person类的属性,原型
class Male extends Person{
constructor(name,age){
super(name,age);
//super作用:
// 1.创建this对象
// 2.指向父类的构造函数
// 当给子类添加自己的属性时一定要加上super
this.sexy = "male";
}
// 给子类添加自己的类方法
sayHi(){
// console.log("hi"+this.name);子类的原型方法
// 需求:在子类的原型方法内部使用父类的方法
super.sayHello();
// 这里super的作用:
// 指向父类的原型对象
this.sayHello();
// this指向实例
// sayHi是子类Male的原型方法,子类Male继承了
// 父类的sayHello原型方法,this指向将来的实例,
// 也能访问到父类sayHello因为创造实例的类
// 已经继承父类sayHello
}
// 子类的类方法(静态方法)
static bar(){
// 需求在子类的类(静态)方法中访问父类的类(静态)方法
super.foo()//super指向父类,相当于Person.foo();
this.foo()
// cosole.log(this)
这里的this指向的是整个子类Male,
子类继承了父类,子类本身(不是子类创造的实例)
也能访问到父类的类方法
父类中原型对象的方法,子类的实例也能访问到
}
}
// 原型方法实例访问,类方法类访问
// 通过类访问类方法
// Male.bar()
let male = new Male("zhangsan",20);
male.sayHello();
console.log(male.sexy);
male.sayHi()
Male.bar()