JavaScript——面向对象继承原理及原型原型链详解

要实现继承我们先要理解原型和原型链,那么我们就先来看看他们的概念吧

一.原型

1.普通对象的原型

JavaScript中每个对象中都有一个特有的内置属性[[prototype]],这个特殊的对象可以指向另一个对象。

简单来说就是对象中有个属性指向另一个对象,来看看代码

obj = {name:'sss'}
console.log(obj);
// 获取对象的原型
console.log(obj.__proto__);
console.log(Object.getPrototypeOf(obj)); //用来获取原型的方法
console.log(obj.__proto__ ===Object.getPrototypeOf(obj));

来看看结果

obj中果然有个[[prototype]]属性,那么这个原型又有什么用呢?当我们查找一个属性或者函数时 ,它会优先在自己的对象中查找,如果没找的话就会去它的原型中查找

console.log(obj.name);  //sss
obj.__proto__.message = 'zzz'
console.log(obj.message); //zzz

2.函数的原型

1.所有的函数都有一个特别的prototype属性(显式原型)

function foo(){}
// 当把函数看成一个普通的对象时有个隐式原型(__proto__)
console.log(bar.__proto__);
console.log(foo.__proto__);
// 当把函数看成一个函数时有个显示原型(prototype)
console.log(foo.prototype);
console.log(bar.prototype); // 没有原型

2. 再来回忆下new操作符所做的操作

1.创建一个空对象

2.将这个空对象赋值给this

3.将函数的显示原型作为这个对象隐式原型

4.将这个对象返回

所以我们可以知道foo函数创建的__proto__等同于foo函数的prototype

function foo(name,age){
    this.name = name
    this.age =age
}
f = new foo('ss',18)
console.log(f.__proto ==foo.prototype) //true

每次new一个对象时,会创建出许多属性,但是如果将方法一起创建,那么是不是每创建一个实例,就会将方法一起创建,那么当创建出许多对象时是不是显得很多余,这个时候原型的作用就体现出来了,我们可以将方法放在原型上,就不用多次创建这个方法在对象中了

function foo(){
    name:'ss'
}
foo.prototype.run = function(){
    console.log('我会跑步');
}

let f1 = new foo()
f1.run() // 我会跑步
let f2 =new foo()
console.log(f1.run === f2.run) //true

而且很容易发现f1和f2调用的都是同一个方法,此时根据原型查找关系和this的隐式绑定将方法绑定在调用的对象身上,就可以很容易的使用

3.constructor属性

我们来打印一下foo的原型就可以很容易发现它身上有一个constructor属性

 咦,好像就是foo本身,也就是说foo的prototype指向foo的原型,原型的constructor又指向foo,是个循环引用

function foo(){

}
console.log(foo.prototype.constructor === foo); // true
console.log(foo.prototype.constructor.name); //foo
let f = new foo()
console.log(f.__proto__.constructor === foo); //true

 4.重写对象原型

对象的原型是可以绑定别的对象的

function Foo(){}
Foo.prototype = {
    name:'sss',
    age:18,
    run:function(){
        console.log('跑步');
    }
}
console.log(Foo.prototype); //但是此时我们会发现少了个constructor
Object.defineProperty(Foo.prototype,'constructor',{
    configurable:true,
    writable:true,
    enumerable:false,
    value:Foo
}) //添加constructor并且不可枚举

5.原型链

理解原型后原型链就很简单了,就是我们寻找一个属性时优先在原型上查找,没找到继续接着原型找,而这个链条就是原型链

二.继承的实现

面向对象有三大特性:

1.封装:将属性和方法封装到一个类中

2.继承:为了减少重复的代码,也是多态的前提

3.多态:不同的对象执行时有不同的表现

实现继承的方式

1.寄生式继承方案(最终)

function Person(name,age){
    this.name = name
    this.age = age
}
Person.prototype.run = function(){
    console.log('跑');
}
Person.prototype.sex = function(){
    console.log('涩涩');
}
function Student(name,age,grade){
    //借用构造函数
    Person.call(this,name,age)
    this.grade = grade
}
function createObject(obj){
    function F(){}
    F.prototype = obj.prototype
    return new F()
}
function inherit(Subtype,Supertype){
    Subtype.prototype = createObject(Supertype)
    Object.defineProperty(Subtype.prototype,'constructor',{
        enumerable:false,
        configurable:true,
        writable:true,
        value:Subtype
    })
}
inherit(Student,Person)
let s =new Student('sss',18,100)
s.run() // 跑
s.sex() // 涩涩
console.log(s.name); //sss

2.Object是其他类的父类

3.对象的几个判断方法

hasOwnProperty:对象是否有某一个属于自己的属性

in/for in:判断某个属性思否在对象上或者对象的原型上

instanceof:用于检测构造函数的prototype是否在某个实例对象的原型链上

inPrototypeOf:用于检测某个对象是否在某个实例对象的原型上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值