JavaScript的原型和继承
原型
先看一段代码
function User(name) {
this.name = name
}
let no= new User('不忘')
console.log(no);//User {name: "不忘"}
console.dir(User);//ƒ User(name)
console.dir(Function);//ƒ Function() //function的构造函数
console.dir(Object);//ƒ Object() //object的构造函数
复制代码
当你打开上面的输出时,你能看到一个prototype,或者__proto__属性,这就是他们的原型,打开原型你们会看到一个constructor属性,这个是该对象的构造函数,父级的属性靠这个传给子级,让子级有父级的属性,父级和子集的关系
子级._prop_===父级.prototype
子级._prop_.constructor===父级
由于父级也可能是别人的子级,所以
孙级._prop_._proto_===爷级.prototype
爷爷自然也可以有曾爷爷,原型链就是靠__prop__一直延续到最后
按照上述的条件,我把上面函数的四个输出画了一份关系图
没错JavaScript的家族关系就是如此错乱,要是有兴趣自己研究一下,
比如:
Function自己生了自己,所以Function._proto_===Function.prototypt
no的父级是user,user的父级是Function,但no的的爷爷是Object
Function的爷爷是Object,但Object的爸爸是Function等等
说实话知道他们家族的关系感觉没什么太大作用,关键要知道该对象和他的原型之间的关系,还有子级能靠这样可以直接调用父级的属性,JavaScript的继承就是利用了这个方法,让子级继承父级的属性。
function User(name) {
this.name = name
this.getName=function(){
console.log(this.name);
}
}
let no= new User('不忘')
no.getName()//不忘
//在父级User的prototype中添加的方法,子级no也可以调用
复制代码
有关原型属性的方法
create设置_proto_
let no={name:'不忘'}
let yes=Object.create(no,{
name:{
value:'回忆'
}
})
console.log(yes);//{name: "回忆"}
//此时yes的__proto__为no,此方法不可查询
let ok={name:'好的'}
ok.__proto__ = no
console.log(ok);//{name: "好的"}
//此时yes的__proto__为no,可查询
复制代码
setPrototypeOf设置_proto_
let no={name:'不忘'}
let yes={name:'回忆'}
Object.setPrototypeOf(yes,no)
console.log(yes);//{name: "回忆"}
//此时yes的__proto__为no,官方升级版
//__proto__是一个访问器,只能改成对象
复制代码
isPrototype判断是否在对方的__proto__上
let no={name:'不忘'}
let yes={name:'回忆'}
Object.setPrototypeOf(yes,no)
console.log(no.isPrototypeOf(yes));//true
//no在yes的__proto__上,返回true
复制代码
instanceof构造函数的prototype是否是该原型链上
function User(name) {
this.name = name
}
let no= new User('不忘')
console.log(no instanceof User);//true
//当no的原型链上有User的prototype返回true
复制代码
hasOwnPrototype不会判断原型链上面的值是否存在
let no={Nname:'不忘'}
let yes={name:'回忆'}
Object.setPrototypeOf(yes,no)
console.log('Nname' in no);//true
console.log(yes.hasOwnProperty('Nname'));//false
//hasOwerProperty不会查询到原型链上
复制代码
可以用call和apply调用别的原型上的方法
function User(name) {
this.name = name
}
User.prototype.getName=function(){
console.log(this.name);
}
let no= new User('不忘')
let yes={name:'回忆'}
no.getName.call(yes)//回忆
复制代码
继承
我之前提到原型链是用来实现继承的,继承的重点在子级.prototype,给子级的prototype的值
基础
function User(name) {
this.name = name
}
function Name() {}
Name.prototype.getName = function() {//给Name的prototype增加方法
console.log(this.name);
}
User.prototype.__proto__=Name.prototype;//User继承Name的prototype
// User.prototype=Object.create(Name.prototype)//第二种写法,要写在在new之前,不然没有效果
let no=new User('不忘');
no.getName()//不忘 //由于继承了Name的prototype,所以可以调用getName方法
复制代码
加上construct
function User(name) {
this.name = name
}
function Name() {}
Name.prototype.getName = function() {
console.log(this.name);
}
User.prototype=Object.create(Name.prototype)
User.prototype.constructor = Name;//将构造函数指过去
Object.defineProperty(User.prototype,'constructor',{
value:Name,
enumerable:false
})//静止遍历
let no=new User('不忘');
no.getName()//不忘
//这样就将Name变成了User的父级
复制代码
子级的方法和用父级的方法互相调用
function User(name) {
this.name = name
}
function Name() {}
Name.prototype.getName = function() {
return this.name
}
User.prototype=Object.create(Name.prototype)
User.prototype.show =function () {//这次添加要写在create后面
console.log(this.getName()+'女朋友是冰冰');
}
User.prototype.constructor = Name;//将构造函数指过去
Object.defineProperty(User.prototype,'constructor',{
value:Name,
enumerable:false
})//静止遍历
let no=new User('不忘');
no.show()//不忘女朋友是冰冰
复制代码
父级初始属性
function User(name,age) {
this.name = name
this.age=age
}
Girl.prototype=Object.create(User.prototype)
function Girl(name,age) {
User.call(this,name,age)//这里用call调用一下
}
User.prototype.show=function(){
console.log(this.name+"今年"+this.age+'岁了');
}
let yes=new Girl('冰冰','18')
yes.show()//冰冰今年18岁了
复制代码
继承方法封装
function extend(son,father) {//son为子级,father为父级
son.prototype=Object.create(father.prototype);//设置prototy
Object.defineProperty(son.prototype, 'constructor',{//设置constructor
value: father,
enumerable:false,
})
}
function User(name,age) {
this.name = name
this.age=age
}
extend(Girl,User)
function Girl(name,age) {
User.call(this,name,age)
}
User.prototype.show=function(){
console.log(this.name+"今年"+this.age+'岁了');
}
let yes=new Girl('冰冰','18')
yes.show()//冰冰今年18岁了
复制代码
对象工厂继承
function User(name,age) {
this.name = name
this.age=age
}
function girl(name,age) {
const gproto=Object.create(User.prototype)//创建一个对象继承prototype
User.call(gproto,name,age)
return gproto
}
User.prototype.show=function(){
console.log(this.name+"今年"+this.age+'岁了');
}
let yes=girl('冰冰','18')
yes.show()
复制代码
多继承
function extend(son,father) {
son.prototype=Object.create(father.prototype);
Object.defineProperty(son.prototype, 'constructor',{
value: father,
enumerable:false,
})
}
function Boy(name,age) {
this.name = name;
this.age = age;
}
function Sound() {}
Sound.prototype.a=function(){
console.log(this.name+'会伪音');
}
function Mackeup() {}
Mackeup.prototype.b=function(){
console.log(this.name+'会化妆');
}
function Wear() {}
Wear.prototype.c=function(){
console.log(this.name+'会女装');
}
extend(Mackeup,Wear)
extend(Sound,Mackeup)
extend(Boy,Sound)
let yes=new Boy('冰冰','18')
yes.a()//冰冰会伪音
yes.b()//冰冰会化妆
yes.c()//冰冰会女装
//当需要继承不同的父级时,由于只能有一个爸爸,那就变成爷爷,曾爷爷,这样一直链接下去会导致链接太长
复制代码
mixin升级版
function Boy(name,age) {
this.name = name;
this.age = age;
}
Sound={
a:function(){
console.log(this.name+'会伪音');
}
}
Mackeup={
b:function(){
console.log(this.name+'会化妆');
}
}
Wear={
c:function(){
console.log(this.name+'会女装');
}
}
Boy.prototype=Object.assign(Boy.prototype,Sound,Mackeup,Wear);
let yes=new Boy('冰冰','18')
yes.a()//冰冰会伪音
yes.b()//冰冰会化妆
yes.c()//冰冰会女装
//这样就不需要一直延长原型链了
复制代码
mixin可以内部继承和super关键字
function Boy(name,age) {
this.name = name;
this.age = age;
}
let Run={
go:function(){
return '跑的很快'
}
}
let Mackeup={
__proto__:Run,
make(){//这里方法要这么写才可以用super
// console.log(this.__proto__.go()+'去化妆');
console.log(super.go()+'去化妆');
}
}
Boy.prototype=Object.assign(Boy.prototype,Mackeup,Run);
let yes=new Boy('冰冰','18')
yes.make()
复制代码
class
继承的语法糖
class User {
constructor(name){
this.name = name;
}
girlfriend(){
console.log(this.name+'有很多女朋友');
}
}
let no=new User('不忘');
no.girlfriend()//不忘有很多女朋友
//class的继承用法,创建对象在严格模式下进行,不可遍历
复制代码
静态属性
class User {
static from='中国'
where(){
console.log(User.from);
}
}
let no=new User();
let yes=new User();
no.where()//中国
yes.where()//中国
//公用的数据就是静态属性
复制代码
静态方法
class User {
static show(){
console.log('直接调用的方法');
}
}
User.show()//直接调用的方法
//不用实例化对象,直接调用
复制代码
class的访问器
class User {
constructor(name,age){
this.name = name;
this.age = age;
}
set age(nub){
if(nub<=18){
throw new Error("未成年不能进入")
}
}
get age(){
return this.age
}
}
let no=new User('不忘',18)//Uncaught Error: 未成年不能进入 //和对象一样
复制代码
class继承
class Change {
constructor(age){
this.age=age;
}
code(){
console.log(this.name+'代码越敲越强');
}
see(){
console.log(this.age);
}
}
class User extends Change{
constructor(name,age){
super(age)
this.name = name;
}
}
let no=new User('不忘',18)
no.code()//不忘代码越敲越强
no.see()//18
复制代码
私有属性
class Change {
#characteristics="屁股上有胎记"
constructor(age){
this.age=age;
}
#code(){
console.log(this.name+'代码越敲越强');
}
see(){
console.log(this.age);
}
}
class User extends Change{
constructor(name,age){
super(age)
this.name = name;
}
}
let no=new User('不忘',18)
// no.#code()//Uncaught SyntaxError: Private field '#code' must be declared in an enclosing class
console.log(no.#characteristics);//Uncaught SyntaxError: Private field '#characteristics' must be declared in an enclosing class
//外面无法访问该属性,和该方法,方法也不能继承
复制代码
多继承
class Boy{
constructor(name,age){
this.name = name;
this.age = age;
}
}
Sound={
a:function(){
console.log(this.name+'会伪音');
}
}
Mackeup={
b:function(){
console.log(this.name+'会化妆');
}
}
Wear={
c:function(){
console.log(this.name+'会女装');
}
}
Boy.prototype=Object.assign(Boy.prototype,Sound,Mackeup,Wear);
let yes=new Boy('冰冰','18')
yes.a()//冰冰会伪音
yes.b()//冰冰会化妆
yes.c()//冰冰会女装
//和构造函数一样
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END